From 2cde8feb9142012bd4722bbdcd2b675c2698f135 Mon Sep 17 00:00:00 2001 From: KyleAtASU Date: Tue, 5 May 2026 16:41:07 -0700 Subject: [PATCH] [feat]: add consumer guide pages --- consumer-guide/.gitignore | 17 + consumer-guide/README.md | 29 + consumer-guide/app/globals.css | 125 + consumer-guide/app/layout.tsx | 45 + consumer-guide/app/page.tsx | 334 ++ consumer-guide/app/tuning/page.tsx | 823 +++++ consumer-guide/components.json | 21 + consumer-guide/components/theme-provider.tsx | 11 + consumer-guide/components/ui/accordion.tsx | 66 + consumer-guide/components/ui/alert-dialog.tsx | 157 + consumer-guide/components/ui/alert.tsx | 66 + consumer-guide/components/ui/aspect-ratio.tsx | 11 + consumer-guide/components/ui/avatar.tsx | 53 + consumer-guide/components/ui/badge.tsx | 46 + consumer-guide/components/ui/breadcrumb.tsx | 109 + consumer-guide/components/ui/button-group.tsx | 83 + consumer-guide/components/ui/button.tsx | 60 + consumer-guide/components/ui/calendar.tsx | 213 ++ consumer-guide/components/ui/card.tsx | 92 + consumer-guide/components/ui/carousel.tsx | 241 ++ consumer-guide/components/ui/chart.tsx | 351 ++ consumer-guide/components/ui/checkbox.tsx | 32 + consumer-guide/components/ui/collapsible.tsx | 33 + consumer-guide/components/ui/command.tsx | 184 + consumer-guide/components/ui/context-menu.tsx | 252 ++ consumer-guide/components/ui/dialog.tsx | 143 + consumer-guide/components/ui/drawer.tsx | 135 + .../components/ui/dropdown-menu.tsx | 257 ++ consumer-guide/components/ui/empty.tsx | 104 + consumer-guide/components/ui/field.tsx | 244 ++ consumer-guide/components/ui/form.tsx | 167 + consumer-guide/components/ui/hover-card.tsx | 44 + consumer-guide/components/ui/input-group.tsx | 169 + consumer-guide/components/ui/input-otp.tsx | 77 + consumer-guide/components/ui/input.tsx | 21 + consumer-guide/components/ui/item.tsx | 193 + consumer-guide/components/ui/kbd.tsx | 28 + consumer-guide/components/ui/label.tsx | 24 + consumer-guide/components/ui/menubar.tsx | 276 ++ .../components/ui/navigation-menu.tsx | 166 + consumer-guide/components/ui/pagination.tsx | 127 + consumer-guide/components/ui/popover.tsx | 48 + consumer-guide/components/ui/progress.tsx | 31 + consumer-guide/components/ui/radio-group.tsx | 45 + consumer-guide/components/ui/resizable.tsx | 56 + consumer-guide/components/ui/scroll-area.tsx | 58 + consumer-guide/components/ui/select.tsx | 185 + consumer-guide/components/ui/separator.tsx | 28 + consumer-guide/components/ui/sheet.tsx | 139 + consumer-guide/components/ui/sidebar.tsx | 726 ++++ consumer-guide/components/ui/skeleton.tsx | 13 + consumer-guide/components/ui/slider.tsx | 59 + consumer-guide/components/ui/sonner.tsx | 25 + consumer-guide/components/ui/spinner.tsx | 16 + consumer-guide/components/ui/switch.tsx | 29 + consumer-guide/components/ui/table.tsx | 116 + consumer-guide/components/ui/tabs.tsx | 66 + consumer-guide/components/ui/textarea.tsx | 18 + consumer-guide/components/ui/toast.tsx | 129 + consumer-guide/components/ui/toaster.tsx | 35 + consumer-guide/components/ui/toggle-group.tsx | 73 + consumer-guide/components/ui/toggle.tsx | 47 + consumer-guide/components/ui/tooltip.tsx | 61 + consumer-guide/components/ui/use-mobile.tsx | 19 + consumer-guide/components/ui/use-toast.ts | 191 + consumer-guide/hooks/use-mobile.ts | 19 + consumer-guide/hooks/use-toast.ts | 191 + consumer-guide/lib/utils.ts | 6 + consumer-guide/next-env.d.ts | 6 + consumer-guide/next.config.mjs | 11 + consumer-guide/package.json | 72 + consumer-guide/pnpm-lock.yaml | 3208 +++++++++++++++++ consumer-guide/postcss.config.mjs | 8 + consumer-guide/public/icon.svg | 26 + consumer-guide/public/placeholder-logo.svg | 1 + consumer-guide/public/placeholder.svg | 1 + consumer-guide/styles/globals.css | 125 + consumer-guide/tsconfig.json | 41 + 78 files changed, 11557 insertions(+) create mode 100644 consumer-guide/.gitignore create mode 100644 consumer-guide/README.md create mode 100644 consumer-guide/app/globals.css create mode 100644 consumer-guide/app/layout.tsx create mode 100644 consumer-guide/app/page.tsx create mode 100644 consumer-guide/app/tuning/page.tsx create mode 100644 consumer-guide/components.json create mode 100644 consumer-guide/components/theme-provider.tsx create mode 100644 consumer-guide/components/ui/accordion.tsx create mode 100644 consumer-guide/components/ui/alert-dialog.tsx create mode 100644 consumer-guide/components/ui/alert.tsx create mode 100644 consumer-guide/components/ui/aspect-ratio.tsx create mode 100644 consumer-guide/components/ui/avatar.tsx create mode 100644 consumer-guide/components/ui/badge.tsx create mode 100644 consumer-guide/components/ui/breadcrumb.tsx create mode 100644 consumer-guide/components/ui/button-group.tsx create mode 100644 consumer-guide/components/ui/button.tsx create mode 100644 consumer-guide/components/ui/calendar.tsx create mode 100644 consumer-guide/components/ui/card.tsx create mode 100644 consumer-guide/components/ui/carousel.tsx create mode 100644 consumer-guide/components/ui/chart.tsx create mode 100644 consumer-guide/components/ui/checkbox.tsx create mode 100644 consumer-guide/components/ui/collapsible.tsx create mode 100644 consumer-guide/components/ui/command.tsx create mode 100644 consumer-guide/components/ui/context-menu.tsx create mode 100644 consumer-guide/components/ui/dialog.tsx create mode 100644 consumer-guide/components/ui/drawer.tsx create mode 100644 consumer-guide/components/ui/dropdown-menu.tsx create mode 100644 consumer-guide/components/ui/empty.tsx create mode 100644 consumer-guide/components/ui/field.tsx create mode 100644 consumer-guide/components/ui/form.tsx create mode 100644 consumer-guide/components/ui/hover-card.tsx create mode 100644 consumer-guide/components/ui/input-group.tsx create mode 100644 consumer-guide/components/ui/input-otp.tsx create mode 100644 consumer-guide/components/ui/input.tsx create mode 100644 consumer-guide/components/ui/item.tsx create mode 100644 consumer-guide/components/ui/kbd.tsx create mode 100644 consumer-guide/components/ui/label.tsx create mode 100644 consumer-guide/components/ui/menubar.tsx create mode 100644 consumer-guide/components/ui/navigation-menu.tsx create mode 100644 consumer-guide/components/ui/pagination.tsx create mode 100644 consumer-guide/components/ui/popover.tsx create mode 100644 consumer-guide/components/ui/progress.tsx create mode 100644 consumer-guide/components/ui/radio-group.tsx create mode 100644 consumer-guide/components/ui/resizable.tsx create mode 100644 consumer-guide/components/ui/scroll-area.tsx create mode 100644 consumer-guide/components/ui/select.tsx create mode 100644 consumer-guide/components/ui/separator.tsx create mode 100644 consumer-guide/components/ui/sheet.tsx create mode 100644 consumer-guide/components/ui/sidebar.tsx create mode 100644 consumer-guide/components/ui/skeleton.tsx create mode 100644 consumer-guide/components/ui/slider.tsx create mode 100644 consumer-guide/components/ui/sonner.tsx create mode 100644 consumer-guide/components/ui/spinner.tsx create mode 100644 consumer-guide/components/ui/switch.tsx create mode 100644 consumer-guide/components/ui/table.tsx create mode 100644 consumer-guide/components/ui/tabs.tsx create mode 100644 consumer-guide/components/ui/textarea.tsx create mode 100644 consumer-guide/components/ui/toast.tsx create mode 100644 consumer-guide/components/ui/toaster.tsx create mode 100644 consumer-guide/components/ui/toggle-group.tsx create mode 100644 consumer-guide/components/ui/toggle.tsx create mode 100644 consumer-guide/components/ui/tooltip.tsx create mode 100644 consumer-guide/components/ui/use-mobile.tsx create mode 100644 consumer-guide/components/ui/use-toast.ts create mode 100644 consumer-guide/hooks/use-mobile.ts create mode 100644 consumer-guide/hooks/use-toast.ts create mode 100644 consumer-guide/lib/utils.ts create mode 100644 consumer-guide/next-env.d.ts create mode 100644 consumer-guide/next.config.mjs create mode 100644 consumer-guide/package.json create mode 100644 consumer-guide/pnpm-lock.yaml create mode 100644 consumer-guide/postcss.config.mjs create mode 100644 consumer-guide/public/icon.svg create mode 100644 consumer-guide/public/placeholder-logo.svg create mode 100644 consumer-guide/public/placeholder.svg create mode 100644 consumer-guide/styles/globals.css create mode 100644 consumer-guide/tsconfig.json diff --git a/consumer-guide/.gitignore b/consumer-guide/.gitignore new file mode 100644 index 000000000..9b04f643a --- /dev/null +++ b/consumer-guide/.gitignore @@ -0,0 +1,17 @@ +# v0 sandbox internal files +__v0_runtime_loader.js +__v0_devtools.tsx +__v0_jsx-dev-runtime.ts +.snowflake/ +.v0-trash/ +.vercel/ +next.user-config.* + +# Environment variables +.env*.local + +# Common ignores +node_modules +.next/ +.DS_Store +.vercel diff --git a/consumer-guide/README.md b/consumer-guide/README.md new file mode 100644 index 000000000..f11272311 --- /dev/null +++ b/consumer-guide/README.md @@ -0,0 +1,29 @@ +# FastVideo Consumer Guide + +A standalone Next.js consumer guide for FastVideo configuration selection and advanced tuning. + +## Requirements + +- Node.js 20 or newer +- pnpm + +## Run Locally + +```bash +pnpm install +pnpm dev +``` + +Then open http://localhost:3000. + +## Build + +```bash +pnpm build +pnpm start +``` + +## Notes + +This app is independent from the main FastVideo Python package. +Run commands from this `consumer-guide/` directory. diff --git a/consumer-guide/app/globals.css b/consumer-guide/app/globals.css new file mode 100644 index 000000000..dc2aea17f --- /dev/null +++ b/consumer-guide/app/globals.css @@ -0,0 +1,125 @@ +@import 'tailwindcss'; +@import 'tw-animate-css'; + +@custom-variant dark (&:is(.dark *)); + +:root { + --background: oklch(1 0 0); + --foreground: oklch(0.145 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.145 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.145 0 0); + --primary: oklch(0.205 0 0); + --primary-foreground: oklch(0.985 0 0); + --secondary: oklch(0.97 0 0); + --secondary-foreground: oklch(0.205 0 0); + --muted: oklch(0.97 0 0); + --muted-foreground: oklch(0.556 0 0); + --accent: oklch(0.97 0 0); + --accent-foreground: oklch(0.205 0 0); + --destructive: oklch(0.577 0.245 27.325); + --destructive-foreground: oklch(0.577 0.245 27.325); + --border: oklch(0.922 0 0); + --input: oklch(0.922 0 0); + --ring: oklch(0.708 0 0); + --chart-1: oklch(0.646 0.222 41.116); + --chart-2: oklch(0.6 0.118 184.704); + --chart-3: oklch(0.398 0.07 227.392); + --chart-4: oklch(0.828 0.189 84.429); + --chart-5: oklch(0.769 0.188 70.08); + --radius: 0.625rem; + --sidebar: oklch(0.985 0 0); + --sidebar-foreground: oklch(0.145 0 0); + --sidebar-primary: oklch(0.205 0 0); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.97 0 0); + --sidebar-accent-foreground: oklch(0.205 0 0); + --sidebar-border: oklch(0.922 0 0); + --sidebar-ring: oklch(0.708 0 0); +} + +.dark { + --background: oklch(0.145 0 0); + --foreground: oklch(0.985 0 0); + --card: oklch(0.145 0 0); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.145 0 0); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.985 0 0); + --primary-foreground: oklch(0.205 0 0); + --secondary: oklch(0.269 0 0); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.269 0 0); + --muted-foreground: oklch(0.708 0 0); + --accent: oklch(0.269 0 0); + --accent-foreground: oklch(0.985 0 0); + --destructive: oklch(0.396 0.141 25.723); + --destructive-foreground: oklch(0.637 0.237 25.331); + --border: oklch(0.269 0 0); + --input: oklch(0.269 0 0); + --ring: oklch(0.439 0 0); + --chart-1: oklch(0.488 0.243 264.376); + --chart-2: oklch(0.696 0.17 162.48); + --chart-3: oklch(0.769 0.188 70.08); + --chart-4: oklch(0.627 0.265 303.9); + --chart-5: oklch(0.645 0.246 16.439); + --sidebar: oklch(0.205 0 0); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.488 0.243 264.376); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.269 0 0); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(0.269 0 0); + --sidebar-ring: oklch(0.439 0 0); +} + +@theme inline { + --font-sans: 'Geist', 'Geist Fallback'; + --font-mono: 'Geist Mono', 'Geist Mono Fallback'; + --color-background: var(--background); + --color-foreground: var(--foreground); + --color-card: var(--card); + --color-card-foreground: var(--card-foreground); + --color-popover: var(--popover); + --color-popover-foreground: var(--popover-foreground); + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-foreground); + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--secondary-foreground); + --color-muted: var(--muted); + --color-muted-foreground: var(--muted-foreground); + --color-accent: var(--accent); + --color-accent-foreground: var(--accent-foreground); + --color-destructive: var(--destructive); + --color-destructive-foreground: var(--destructive-foreground); + --color-border: var(--border); + --color-input: var(--input); + --color-ring: var(--ring); + --color-chart-1: var(--chart-1); + --color-chart-2: var(--chart-2); + --color-chart-3: var(--chart-3); + --color-chart-4: var(--chart-4); + --color-chart-5: var(--chart-5); + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); + --color-sidebar: var(--sidebar); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-ring: var(--sidebar-ring); +} + +@layer base { + * { + @apply border-border outline-ring/50; + } + body { + @apply bg-background text-foreground; + } +} diff --git a/consumer-guide/app/layout.tsx b/consumer-guide/app/layout.tsx new file mode 100644 index 000000000..91eb9484e --- /dev/null +++ b/consumer-guide/app/layout.tsx @@ -0,0 +1,45 @@ +import type { Metadata } from 'next' +import { Geist, Geist_Mono } from 'next/font/google' +import { Analytics } from '@vercel/analytics/next' +import './globals.css' + +const _geist = Geist({ subsets: ["latin"] }); +const _geistMono = Geist_Mono({ subsets: ["latin"] }); + +export const metadata: Metadata = { + title: 'FastVideo Config Selector', + description: 'Choose your hardware and task to get a recommended FastVideo configuration', + generator: 'v0.app', + icons: { + icon: [ + { + url: '/icon-light-32x32.png', + media: '(prefers-color-scheme: light)', + }, + { + url: '/icon-dark-32x32.png', + media: '(prefers-color-scheme: dark)', + }, + { + url: '/icon.svg', + type: 'image/svg+xml', + }, + ], + apple: '/apple-icon.png', + }, +} + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode +}>) { + return ( + + + {children} + {process.env.NODE_ENV === 'production' && } + + + ) +} diff --git a/consumer-guide/app/page.tsx b/consumer-guide/app/page.tsx new file mode 100644 index 000000000..9c147e1ff --- /dev/null +++ b/consumer-guide/app/page.tsx @@ -0,0 +1,334 @@ +"use client" + +import { useState, useMemo } from "react" +import { Monitor, Cpu, HardDrive, Layers, Clock, Copy, Check, Film, Image, Sparkles, Gamepad2, Settings2 } from "lucide-react" +import Link from "next/link" + +type Task = "t2v" | "i2v" | "ti2v" | "action" +type Tier = "quick" | "daily" | "best" + +interface Config { + model: string + modelShort: string + attn: string + h: number + w: number + frames: number + steps: number + cfg: number + vramMin: number + ramMin: number + time: string + vram: string + offload: boolean + extra?: string +} + +const CONFIGS: Record> = { + t2v: { + quick: { model: "FastVideo/FastWan2.1-T2V-1.3B-Diffusers", modelShort: "FastWan2.1-T2V-1.3B", attn: "VIDEO_SPARSE_ATTN", h: 480, w: 832, frames: 49, steps: 20, cfg: 5.0, vramMin: 8, ramMin: 16, time: "~60 s", vram: "~16 GB", offload: false }, + daily: { model: "FastVideo/FastWan2.1-T2V-1.3B-Diffusers", modelShort: "FastWan2.1-T2V-1.3B", attn: "FLASH_ATTN", h: 720, w: 1280, frames: 81, steps: 30, cfg: 5.0, vramMin: 20, ramMin: 32, time: "~3 min", vram: "~22 GB", offload: false }, + best: { model: "Wan-AI/Wan2.1-T2V-14B-Diffusers", modelShort: "Wan2.1-T2V-14B", attn: "FLASH_ATTN", h: 720, w: 1280, frames: 81, steps: 50, cfg: 6.0, vramMin: 16, ramMin: 40, time: "~15 min", vram: "~18 GB + ~40 GB RAM", offload: true } + }, + i2v: { + quick: { model: "Wan-AI/Wan2.1-I2V-14B-480P-Diffusers", modelShort: "Wan2.1-I2V-14B-480P", attn: "FLASH_ATTN", h: 480, w: 832, frames: 49, steps: 20, cfg: 5.0, vramMin: 16, ramMin: 32, time: "~2 min", vram: "~18 GB", offload: true, extra: "--image ./input.png" }, + daily: { model: "Wan-AI/Wan2.1-I2V-14B-480P-Diffusers", modelShort: "Wan2.1-I2V-14B-480P", attn: "FLASH_ATTN", h: 480, w: 832, frames: 81, steps: 30, cfg: 5.0, vramMin: 16, ramMin: 32, time: "~5 min", vram: "~20 GB", offload: true, extra: "--image ./input.png" }, + best: { model: "Wan-AI/Wan2.1-I2V-14B-720P-Diffusers", modelShort: "Wan2.1-I2V-14B-720P", attn: "FLASH_ATTN", h: 720, w: 1280, frames: 81, steps: 50, cfg: 6.0, vramMin: 16, ramMin: 40, time: "~20 min", vram: "~20 GB + ~40 GB RAM", offload: true, extra: "--image ./input.png" } + }, + ti2v: { + quick: { model: "FastVideo/FastWan2.2-TI2V-5B-FullAttn-Diffusers", modelShort: "FastWan2.2-TI2V-5B", attn: "FLASH_ATTN", h: 480, w: 832, frames: 49, steps: 20, cfg: 5.0, vramMin: 16, ramMin: 32, time: "~2 min", vram: "~18 GB", offload: false, extra: "--image ./input.png" }, + daily: { model: "FastVideo/FastWan2.2-TI2V-5B-FullAttn-Diffusers", modelShort: "FastWan2.2-TI2V-5B", attn: "FLASH_ATTN", h: 720, w: 1280, frames: 81, steps: 30, cfg: 5.0, vramMin: 20, ramMin: 32, time: "~5 min", vram: "~22 GB", offload: false, extra: "--image ./input.png" }, + best: { model: "Wan-AI/Wan2.2-TI2V-5B-Diffusers", modelShort: "Wan2.2-TI2V-5B", attn: "SAGE_ATTN", h: 720, w: 1280, frames: 81, steps: 50, cfg: 6.0, vramMin: 20, ramMin: 40, time: "~15 min", vram: "~22 GB + ~40 GB RAM", offload: true, extra: "--image ./input.png" } + }, + action: { + quick: { model: "FastVideo/Matrix-Game-2.0-Base-Diffusers", modelShort: "Matrix-Game-2.0-Base", attn: "FLASH_ATTN", h: 480, w: 832, frames: 49, steps: 20, cfg: 5.0, vramMin: 16, ramMin: 32, time: "~2 min", vram: "~18 GB", offload: false, extra: "--image ./input.png --action forward" }, + daily: { model: "FastVideo/Matrix-Game-2.0-Base-Diffusers", modelShort: "Matrix-Game-2.0-Base", attn: "FLASH_ATTN", h: 720, w: 1280, frames: 81, steps: 30, cfg: 5.0, vramMin: 20, ramMin: 32, time: "~5 min", vram: "~22 GB", offload: false, extra: "--image ./input.png --action forward" }, + best: { model: "FastVideo/Matrix-Game-2.0-GTA-Diffusers", modelShort: "Matrix-Game-2.0-GTA", attn: "FLASH_ATTN", h: 720, w: 1280, frames: 81, steps: 50, cfg: 6.0, vramMin: 20, ramMin: 40, time: "~15 min", vram: "~20 GB + ~40 GB RAM", offload: true, extra: "--image ./input.png --action forward" } + } +} + +const TASKS: { id: Task; label: string; sub: string; icon: typeof Film }[] = [ + { id: "t2v", label: "Text to Video", sub: "T2V", icon: Film }, + { id: "i2v", label: "Image to Video", sub: "I2V", icon: Image }, + { id: "ti2v", label: "Text + Image", sub: "TI2V", icon: Sparkles }, + { id: "action", label: "Action Control", sub: "Matrix Game", icon: Gamepad2 }, +] + +const TIERS: { id: Tier; name: string; desc: string; badge?: string }[] = [ + { id: "quick", name: "Quick Try", desc: "Fast preview. Lower resolution, fewer steps." }, + { id: "daily", name: "Daily Use", desc: "Balanced quality and speed.", badge: "Recommended" }, + { id: "best", name: "Best Quality", desc: "14B model, max fidelity. Requires offloading." }, +] + +export default function FastVideoConfigSelector() { + const [task, setTask] = useState("t2v") + const [tier, setTier] = useState("daily") + const [gpu, setGpu] = useState("4090") + const [vram, setVram] = useState(24) + const [ram, setRam] = useState(64) + const [ngpu, setNgpu] = useState(1) + const [copied, setCopied] = useState(false) + + const config = CONFIGS[task][tier] + + const warning = useMemo(() => { + if (vram < config.vramMin) { + return `This mode requires ${config.vramMin} GB VRAM. Your selection is ${vram} GB — consider switching to Quick Try.` + } + if (ram < config.ramMin) { + return `This mode requires ${config.ramMin} GB RAM. Your selection is ${ram} GB.` + } + return null + }, [vram, ram, config]) + + const command = useMemo(() => { + let cmd = `# 1. install\npip install fastvideo\n\n` + cmd += `# 2. download model\nhuggingface-cli download ${config.model} \\\n --local-dir ./models/${config.modelShort}\n\n` + cmd += `# 3. run\nfastvideo generate \\\n` + cmd += ` --model ./models/${config.modelShort} \\\n` + cmd += ` --attention ${config.attn} \\\n` + cmd += ` --height ${config.h} --width ${config.w} \\\n` + cmd += ` --num_frames ${config.frames} \\\n` + cmd += ` --num_inference_steps ${config.steps} \\\n` + cmd += ` --guidance_scale ${config.cfg.toFixed(1)}` + if (config.offload) { + cmd += ` \\\n --dit_cpu_offload \\\n --vae_cpu_offload \\\n --pin_cpu_memory` + } + if (config.extra) { + cmd += ` \\\n ${config.extra}` + } + cmd += ` \\\n --prompt "your prompt here"` + return cmd + }, [config]) + + const copyCommand = async () => { + await navigator.clipboard.writeText(command) + setCopied(true) + setTimeout(() => setCopied(false), 2000) + } + + return ( +
+ {/* Header */} +
+
+
+
+ FastVideo Logo +
+

Config Selector

+

Choose your hardware and task to get a recommended configuration

+
+
+ + + Advanced Tuning + +
+
+
+ +
+ {/* Step 1: Task Selection */} +
+
+ 1 +

What do you want to make?

+
+
+ {TASKS.map(({ id, label, sub, icon: Icon }) => ( + + ))} +
+
+ + {/* Step 2: Machine Specs */} +
+
+ 2 +

Your machine specs

+
+
+
+
+ + GPU Model +
+ +
+
+
+ + VRAM (GB) +
+ +
+
+
+ + RAM (GB) +
+ +
+
+
+ + Number of GPUs +
+ +
+
+
+ + {/* Step 3: Output Mode */} +
+
+ 3 +

Output mode

+
+
+ {TIERS.map(({ id, name, desc, badge }) => { + const tierConfig = CONFIGS[task][id] + return ( + + ) + })} +
+ {warning && ( +
+ {warning} +
+ )} +
+ + {/* Output Configuration */} +
+
+ ✓ +

Your configuration

+
+
+ {/* Meta Cards */} +
+
+
Model
+
{config.modelShort}
+
+
+
Est. Generation Time
+
{config.time}
+
+
+
Peak VRAM
+
{config.vram}
+
+
+ {/* Command Block */} +
+ +
+                {command}
+              
+
+
+
+
+
+ ) +} diff --git a/consumer-guide/app/tuning/page.tsx b/consumer-guide/app/tuning/page.tsx new file mode 100644 index 000000000..526002d23 --- /dev/null +++ b/consumer-guide/app/tuning/page.tsx @@ -0,0 +1,823 @@ +"use client" + +import { useState, useMemo } from "react" +import { + Monitor, Cpu, Film, Sparkles, Zap, HardDrive, BarChart3, + Copy, Check, Info, ChevronDown, ChevronUp, Settings2, + Layers, Clock, Database, Gauge +} from "lucide-react" +import Link from "next/link" + +// Types +interface Config { + // Category A - Model & Hardware + model_id: string + workload_type: string + gpu: string + num_gpus: number + // Category B - Video Spec + height: number + width: number + num_frames: number + fps: number + // Category C - Quality + num_inference_steps: number + guidance_scale: number + embedded_cfg_scale: number + flow_shift: number | null + seed: number + // Category D - Speed + attention_backend: string + VSA_sparsity: number + dmd_denoising_steps: string + // Category E - Memory + dit_cpu_offload: boolean + dit_layerwise_offload: boolean + vae_cpu_offload: boolean + text_encoder_cpu_offload: boolean + image_encoder_cpu_offload: boolean + pin_cpu_memory: boolean + use_fsdp_inference: boolean +} + +const modelOptions = [ + { id: "FastVideo/FastWan2.1-T2V-1.3B-Diffusers", name: "FastWan 1.3B (T2V)", size: "1.3B" }, + { id: "Wan-AI/Wan2.1-T2V-14B-Diffusers", name: "Wan 14B (T2V)", size: "14B" }, + { id: "Wan-AI/Wan2.1-I2V-14B-480P-Diffusers", name: "Wan 14B I2V 480P", size: "14B" }, + { id: "Wan-AI/Wan2.1-I2V-14B-720P-Diffusers", name: "Wan 14B I2V 720P", size: "14B" }, +] + +const workloadOptions = [ + { id: "t2v", name: "Text to Video", icon: Film }, + { id: "i2v", name: "Image to Video", icon: Sparkles }, + { id: "ti2v", name: "Text+Image to Video", icon: Layers }, + { id: "action", name: "Action", icon: Zap }, +] + +const gpuOptions = [ + { id: "rtx4090", name: "RTX 4090", vram: "24GB" }, + { id: "rtx5090", name: "RTX 5090", vram: "32GB" }, + { id: "a100", name: "A100", vram: "40/80GB" }, + { id: "h100", name: "H100", vram: "80GB" }, +] + +const resolutionPresets = [ + { height: 480, width: 832, label: "480p (832×480)" }, + { height: 720, width: 1280, label: "720p (1280×720)" }, + { height: 1080, width: 1920, label: "1080p (1920×1080)" }, +] + +const framePresets = [ + { frames: 49, label: "49 frames (~2s @24fps)" }, + { frames: 81, label: "81 frames (~3.4s @24fps)" }, + { frames: 121, label: "121 frames (~5s @24fps)" }, +] + +const attentionBackends = [ + { id: "FLASH_ATTN", name: "Flash Attention", desc: "Best quality-speed trade-off", speed: "Baseline" }, + { id: "VIDEO_SPARSE_ATTN", name: "Video Sparse Attention", desc: "~30% faster, minimal quality loss", speed: "Fast" }, + { id: "SAGE_ATTN", name: "Sage Attention", desc: "~40% faster, visible at high motion", speed: "Faster" }, + { id: "SLA_ATTN", name: "Sliding Tile Attention", desc: "Check model compatibility", speed: "Fast" }, +] + +// Tooltip component +function Tooltip({ content }: { content: string }) { + const [show, setShow] = useState(false) + return ( +
+ + {show && ( +
+ {content} +
+
+ )} +
+ ) +} + +// Collapsible section +function Section({ + title, + icon: Icon, + number, + children, + defaultOpen = true, + color = "sky" +}: { + title: string + icon: React.ElementType + number: string + children: React.ReactNode + defaultOpen?: boolean + color?: "sky" | "emerald" | "amber" | "violet" | "rose" | "slate" +}) { + const [open, setOpen] = useState(defaultOpen) + const colorClasses = { + sky: "bg-sky-500/20 text-sky-400", + emerald: "bg-emerald-500/20 text-emerald-400", + amber: "bg-amber-500/20 text-amber-400", + violet: "bg-violet-500/20 text-violet-400", + rose: "bg-rose-500/20 text-rose-400", + slate: "bg-slate-500/20 text-slate-400", + } + const textColors = { + sky: "text-sky-400", + emerald: "text-emerald-400", + amber: "text-amber-400", + violet: "text-violet-400", + rose: "text-rose-400", + slate: "text-slate-400", + } + + return ( +
+ + {open &&
{children}
} +
+ ) +} + +// Input components +function NumberInput({ + label, + value, + onChange, + min, + max, + step = 1, + tooltip, + unit +}: { + label: string + value: number + onChange: (v: number) => void + min?: number + max?: number + step?: number + tooltip?: string + unit?: string +}) { + return ( +
+ +
+ onChange(Number(e.target.value))} + min={min} + max={max} + step={step} + className="w-full rounded-lg border border-white/10 bg-slate-900/50 px-3 py-2.5 text-sm text-white focus:border-sky-500/50 focus:outline-none focus:ring-1 focus:ring-sky-500/50" + /> + {unit && {unit}} +
+
+ ) +} + +function SelectInput({ + label, + value, + onChange, + options, + tooltip +}: { + label: string + value: string + onChange: (v: string) => void + options: { id: string; name: string; desc?: string }[] + tooltip?: string +}) { + return ( +
+ + +
+ ) +} + +function Toggle({ + label, + checked, + onChange, + tooltip, + disabled = false +}: { + label: string + checked: boolean + onChange: (v: boolean) => void + tooltip?: string + disabled?: boolean +}) { + return ( + + ) +} + +export default function AdvancedTuningPage() { + const [copied, setCopied] = useState(false) + const [config, setConfig] = useState({ + // Category A + model_id: "FastVideo/FastWan2.1-T2V-1.3B-Diffusers", + workload_type: "t2v", + gpu: "rtx4090", + num_gpus: 1, + // Category B + height: 480, + width: 832, + num_frames: 49, + fps: 24, + // Category C + num_inference_steps: 30, + guidance_scale: 5.0, + embedded_cfg_scale: 6.0, + flow_shift: null, + seed: -1, + // Category D + attention_backend: "FLASH_ATTN", + VSA_sparsity: 0, + dmd_denoising_steps: "1000,757,522", + // Category E + dit_cpu_offload: false, + dit_layerwise_offload: false, + vae_cpu_offload: false, + text_encoder_cpu_offload: false, + image_encoder_cpu_offload: false, + pin_cpu_memory: false, + use_fsdp_inference: false, + }) + + // Derived values + const videoLength = useMemo(() => (config.num_frames / config.fps).toFixed(1), [config.num_frames, config.fps]) + const totalPixels = useMemo(() => config.height * config.width * config.num_frames, [config.height, config.width, config.num_frames]) + const computeCost = useMemo(() => totalPixels * config.num_inference_steps, [totalPixels, config.num_inference_steps]) + + // Estimate generation time (rough approximation) + const estimatedTime = useMemo(() => { + const baseTime = (totalPixels / 19600000) * 60 * (config.num_inference_steps / 30) + let multiplier = 1 + if (config.dit_cpu_offload) multiplier *= 2.5 + if (config.dit_layerwise_offload) multiplier *= 4 + if (config.attention_backend === "VIDEO_SPARSE_ATTN") multiplier *= 0.7 + if (config.attention_backend === "SAGE_ATTN") multiplier *= 0.6 + return Math.round(baseTime * multiplier) + }, [totalPixels, config.num_inference_steps, config.dit_cpu_offload, config.dit_layerwise_offload, config.attention_backend]) + + // Generate command + const command = useMemo(() => { + const lines = [ + "# 1. Install FastVideo", + "pip install fastvideo", + "", + "# 2. Download model", + `huggingface-cli download ${config.model_id} \\`, + ` --local-dir ./models/${config.model_id.split('/').pop()}`, + "", + "# 3. Run generation", + "fastvideo generate \\", + ` --model ./models/${config.model_id.split('/').pop()} \\`, + ` --attention ${config.attention_backend} \\`, + ` --height ${config.height} --width ${config.width} \\`, + ` --num_frames ${config.num_frames} \\`, + ` --num_inference_steps ${config.num_inference_steps} \\`, + ` --guidance_scale ${config.guidance_scale} \\`, + ] + + if (config.embedded_cfg_scale !== 6.0) { + lines.push(` --embedded_cfg_scale ${config.embedded_cfg_scale} \\`) + } + if (config.flow_shift !== null) { + lines.push(` --flow_shift ${config.flow_shift} \\`) + } + if (config.seed !== -1) { + lines.push(` --seed ${config.seed} \\`) + } + if (config.attention_backend === "VIDEO_SPARSE_ATTN" && config.VSA_sparsity > 0) { + lines.push(` --vsa_sparsity ${config.VSA_sparsity} \\`) + } + if (config.dit_cpu_offload) lines.push(" --dit_cpu_offload \\") + if (config.dit_layerwise_offload) lines.push(" --dit_layerwise_offload \\") + if (config.vae_cpu_offload) lines.push(" --vae_cpu_offload \\") + if (config.text_encoder_cpu_offload) lines.push(" --text_encoder_cpu_offload \\") + if (config.image_encoder_cpu_offload && (config.workload_type === "i2v" || config.workload_type === "ti2v")) { + lines.push(" --image_encoder_cpu_offload \\") + } + if (config.pin_cpu_memory) lines.push(" --pin_cpu_memory \\") + if (config.use_fsdp_inference && config.num_gpus > 1) lines.push(" --use_fsdp_inference \\") + + if (config.workload_type === "i2v" || config.workload_type === "ti2v") { + lines.push(" --image ./input.png \\") + } + lines.push(' --prompt "your prompt here"') + + return lines.join("\n") + }, [config]) + + const copyCommand = () => { + navigator.clipboard.writeText(command) + setCopied(true) + setTimeout(() => setCopied(false), 2000) + } + + const updateConfig = (key: K, value: Config[K]) => { + setConfig((prev) => ({ ...prev, [key]: value })) + } + + const isFastWanDMD = config.model_id.includes("FastWan") + const isI2V = config.workload_type === "i2v" || config.workload_type === "ti2v" + + return ( +
+ {/* Header */} +
+
+
+
+ + FastVideo Logo + +
+

Advanced Tuning Guide

+

RTX 4090 / RTX 5090 - All 27 Parameters

+
+
+ + Quick Start + +
+
+
+ +
+ {/* Stats Bar */} +
+
+
+
+ + Video Length +
+
{videoLength}s
+
+
+
+ + Est. Time +
+
~{estimatedTime}s
+
+
+
+ + Total Pixels +
+
{(totalPixels / 1000000).toFixed(1)}M
+
+
+
+ + Compute Cost +
+
{(computeCost / 1000000000).toFixed(2)}G
+
+
+
+ +
+ {/* Left Column - Parameters */} +
+ {/* Category A - Model & Hardware */} +
+
+
+ updateConfig("model_id", v)} + options={modelOptions.map(m => ({ id: m.id, name: `${m.name} (${m.size})` }))} + tooltip="HuggingFace model path - uniquely identifies the model weights and architecture." + /> +
+ updateConfig("workload_type", v)} + options={workloadOptions.map(w => ({ id: w.id, name: w.name }))} + tooltip="Task category. Determines which pipeline stages run." + /> + updateConfig("gpu", v)} + options={gpuOptions.map(g => ({ id: g.id, name: `${g.name} (${g.vram})` }))} + tooltip="GPU model. Used for benchmark tracking and hardware-aware configuration." + /> + updateConfig("num_gpus", v)} + min={1} + max={8} + tooltip="Values > 1 require use_fsdp_inference = true." + /> +
+
+ + {/* Category B - Video Spec */} +
+
+
+ +
+ {resolutionPresets.map((preset) => ( + + ))} +
+
+
+ updateConfig("height", v)} + min={256} + max={2160} + step={8} + unit="px" + tooltip="Frame height in pixels. Must be divisible by 8 (VAE requirement)." + /> + updateConfig("width", v)} + min={256} + max={3840} + step={8} + unit="px" + tooltip="Frame width in pixels. Must be divisible by 8." + /> +
+
+ +
+ {framePresets.map((preset) => ( + + ))} +
+
+
+ updateConfig("num_frames", v)} + min={1} + max={240} + tooltip="Total number of frames to generate. Directly sets video duration at a given fps." + /> + updateConfig("fps", v)} + min={1} + max={60} + tooltip="Frames per second for the output video. Does NOT affect generation time." + /> +
+
+
+ + {/* Category C - Quality */} +
+
+
+ +
+ updateConfig("num_inference_steps", Number(e.target.value))} + className="flex-1 h-2 rounded-full bg-slate-700 appearance-none cursor-pointer [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:h-4 [&::-webkit-slider-thumb]:w-4 [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:bg-amber-400" + /> + {config.num_inference_steps} +
+
+ Draft (10) + Balanced (30) + High (80) +
+
+
+ +
+ updateConfig("guidance_scale", Number(e.target.value))} + className="flex-1 h-2 rounded-full bg-slate-700 appearance-none cursor-pointer [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:h-4 [&::-webkit-slider-thumb]:w-4 [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:bg-amber-400" + /> + {config.guidance_scale} +
+
+ Loose (3) + Balanced (5) + Strict (8+) +
+
+
+ updateConfig("embedded_cfg_scale", v)} + min={1} + max={10} + step={0.5} + tooltip="Model-level CFG signal passed into the DiT. Default: 6.0. Rarely change." + /> + updateConfig("seed", v)} + min={-1} + tooltip="Random seed. Set to -1 for random. Fix a seed for reproducibility." + /> +
+
+
+ + {/* Category D - Speed */} +
+
+
+ +
+ {attentionBackends.map((backend) => ( + + ))} +
+
+ {config.attention_backend === "VIDEO_SPARSE_ATTN" && ( +
+ +
+ updateConfig("VSA_sparsity", Number(e.target.value))} + className="flex-1 h-2 rounded-full bg-slate-700 appearance-none cursor-pointer [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:h-4 [&::-webkit-slider-thumb]:w-4 [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:bg-violet-400" + /> + {config.VSA_sparsity} +
+
+ )} + {isFastWanDMD && config.gpu === "rtx4090" && ( +
+
+ + DMD Mode Available +
+
+ FastWan DMD uses 3-step distillation for ~10x faster generation. +
+
+ dmd_steps: {config.dmd_denoising_steps} +
+
+ )} +
+
+ + {/* Category E - Memory */} +
+
+
+
+ Tip: For 24GB VRAM with 14B models, enable dit_cpu_offload + vae_cpu_offload + pin_cpu_memory. +
+
+ updateConfig("dit_cpu_offload", v)} + tooltip="Offload entire DiT to RAM. Saves ~4GB, ~2-3x slower." + /> + updateConfig("dit_layerwise_offload", v)} + tooltip="Layer-by-layer offload. Maximum VRAM saving (~8GB), ~4x slower." + /> + updateConfig("vae_cpu_offload", v)} + tooltip="Offload VAE decoder. Saves ~2GB. Minimal time penalty." + /> + updateConfig("text_encoder_cpu_offload", v)} + tooltip="Offload text encoder. Saves ~1-2GB. Runs once at start." + /> + updateConfig("image_encoder_cpu_offload", v)} + tooltip="I2V/TI2V only. Saves ~1-2GB." + disabled={!isI2V} + /> + updateConfig("pin_cpu_memory", v)} + tooltip="Pins RAM pages for faster PCIe DMA transfers. Always enable with any offload flag." + /> + updateConfig("use_fsdp_inference", v)} + tooltip="Shards model weights across multiple GPUs via FSDP." + disabled={config.num_gpus <= 1} + /> +
+
+
+ + {/* Right Column - Output */} +
+
+ {/* Generated Command */} +
+
+
+ + + +

Generated Command

+
+ +
+
+
+                    {command}
+                  
+
+
+ + {/* Quick Reference */} +
+

Quick Reference

+
+
+ Resolution + {config.width}x{config.height} +
+
+ Frames + {config.num_frames} +
+
+ Steps + {config.num_inference_steps} +
+
+ Backend + {config.attention_backend} +
+
+ Offloading + + {[config.dit_cpu_offload && "DiT", config.vae_cpu_offload && "VAE"].filter(Boolean).join("+") || "None"} + +
+
+
+
+
+
+
+
+ ) +} diff --git a/consumer-guide/components.json b/consumer-guide/components.json new file mode 100644 index 000000000..4ee62ee10 --- /dev/null +++ b/consumer-guide/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "", + "css": "app/globals.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} diff --git a/consumer-guide/components/theme-provider.tsx b/consumer-guide/components/theme-provider.tsx new file mode 100644 index 000000000..55c2f6eb6 --- /dev/null +++ b/consumer-guide/components/theme-provider.tsx @@ -0,0 +1,11 @@ +'use client' + +import * as React from 'react' +import { + ThemeProvider as NextThemesProvider, + type ThemeProviderProps, +} from 'next-themes' + +export function ThemeProvider({ children, ...props }: ThemeProviderProps) { + return {children} +} diff --git a/consumer-guide/components/ui/accordion.tsx b/consumer-guide/components/ui/accordion.tsx new file mode 100644 index 000000000..e538a33b9 --- /dev/null +++ b/consumer-guide/components/ui/accordion.tsx @@ -0,0 +1,66 @@ +'use client' + +import * as React from 'react' +import * as AccordionPrimitive from '@radix-ui/react-accordion' +import { ChevronDownIcon } from 'lucide-react' + +import { cn } from '@/lib/utils' + +function Accordion({ + ...props +}: React.ComponentProps) { + return +} + +function AccordionItem({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AccordionTrigger({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + + svg]:rotate-180', + className, + )} + {...props} + > + {children} + + + + ) +} + +function AccordionContent({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + +
{children}
+
+ ) +} + +export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } diff --git a/consumer-guide/components/ui/alert-dialog.tsx b/consumer-guide/components/ui/alert-dialog.tsx new file mode 100644 index 000000000..970445266 --- /dev/null +++ b/consumer-guide/components/ui/alert-dialog.tsx @@ -0,0 +1,157 @@ +'use client' + +import * as React from 'react' +import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog' + +import { cn } from '@/lib/utils' +import { buttonVariants } from '@/components/ui/button' + +function AlertDialog({ + ...props +}: React.ComponentProps) { + return +} + +function AlertDialogTrigger({ + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogPortal({ + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogOverlay({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogContent({ + className, + ...props +}: React.ComponentProps) { + return ( + + + + + ) +} + +function AlertDialogHeader({ + className, + ...props +}: React.ComponentProps<'div'>) { + return ( +
+ ) +} + +function AlertDialogFooter({ + className, + ...props +}: React.ComponentProps<'div'>) { + return ( +
+ ) +} + +function AlertDialogTitle({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogDescription({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogAction({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AlertDialogCancel({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { + AlertDialog, + AlertDialogPortal, + AlertDialogOverlay, + AlertDialogTrigger, + AlertDialogContent, + AlertDialogHeader, + AlertDialogFooter, + AlertDialogTitle, + AlertDialogDescription, + AlertDialogAction, + AlertDialogCancel, +} diff --git a/consumer-guide/components/ui/alert.tsx b/consumer-guide/components/ui/alert.tsx new file mode 100644 index 000000000..e6751abe6 --- /dev/null +++ b/consumer-guide/components/ui/alert.tsx @@ -0,0 +1,66 @@ +import * as React from 'react' +import { cva, type VariantProps } from 'class-variance-authority' + +import { cn } from '@/lib/utils' + +const alertVariants = cva( + 'relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current', + { + variants: { + variant: { + default: 'bg-card text-card-foreground', + destructive: + 'text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90', + }, + }, + defaultVariants: { + variant: 'default', + }, + }, +) + +function Alert({ + className, + variant, + ...props +}: React.ComponentProps<'div'> & VariantProps) { + return ( +
+ ) +} + +function AlertTitle({ className, ...props }: React.ComponentProps<'div'>) { + return ( +
+ ) +} + +function AlertDescription({ + className, + ...props +}: React.ComponentProps<'div'>) { + return ( +
+ ) +} + +export { Alert, AlertTitle, AlertDescription } diff --git a/consumer-guide/components/ui/aspect-ratio.tsx b/consumer-guide/components/ui/aspect-ratio.tsx new file mode 100644 index 000000000..40bb1208d --- /dev/null +++ b/consumer-guide/components/ui/aspect-ratio.tsx @@ -0,0 +1,11 @@ +'use client' + +import * as AspectRatioPrimitive from '@radix-ui/react-aspect-ratio' + +function AspectRatio({ + ...props +}: React.ComponentProps) { + return +} + +export { AspectRatio } diff --git a/consumer-guide/components/ui/avatar.tsx b/consumer-guide/components/ui/avatar.tsx new file mode 100644 index 000000000..aa98465a3 --- /dev/null +++ b/consumer-guide/components/ui/avatar.tsx @@ -0,0 +1,53 @@ +'use client' + +import * as React from 'react' +import * as AvatarPrimitive from '@radix-ui/react-avatar' + +import { cn } from '@/lib/utils' + +function Avatar({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AvatarImage({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AvatarFallback({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { Avatar, AvatarImage, AvatarFallback } diff --git a/consumer-guide/components/ui/badge.tsx b/consumer-guide/components/ui/badge.tsx new file mode 100644 index 000000000..fc4126b7a --- /dev/null +++ b/consumer-guide/components/ui/badge.tsx @@ -0,0 +1,46 @@ +import * as React from 'react' +import { Slot } from '@radix-ui/react-slot' +import { cva, type VariantProps } from 'class-variance-authority' + +import { cn } from '@/lib/utils' + +const badgeVariants = cva( + 'inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden', + { + variants: { + variant: { + default: + 'border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90', + secondary: + 'border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90', + destructive: + 'border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60', + outline: + 'text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground', + }, + }, + defaultVariants: { + variant: 'default', + }, + }, +) + +function Badge({ + className, + variant, + asChild = false, + ...props +}: React.ComponentProps<'span'> & + VariantProps & { asChild?: boolean }) { + const Comp = asChild ? Slot : 'span' + + return ( + + ) +} + +export { Badge, badgeVariants } diff --git a/consumer-guide/components/ui/breadcrumb.tsx b/consumer-guide/components/ui/breadcrumb.tsx new file mode 100644 index 000000000..1750ff26a --- /dev/null +++ b/consumer-guide/components/ui/breadcrumb.tsx @@ -0,0 +1,109 @@ +import * as React from 'react' +import { Slot } from '@radix-ui/react-slot' +import { ChevronRight, MoreHorizontal } from 'lucide-react' + +import { cn } from '@/lib/utils' + +function Breadcrumb({ ...props }: React.ComponentProps<'nav'>) { + return