├── .gitignore
├── LICENSE
├── README.md
├── app
├── ai-chat
│ ├── loading.tsx
│ └── page.tsx
├── api
│ ├── chat
│ │ └── route.ts
│ ├── fetch-repo-data
│ │ └── route.ts
│ ├── generate-readme
│ │ └── route.ts
│ ├── git-moji
│ │ └── route.ts
│ └── gitmojis
│ │ └── route.ts
├── generate-readme
│ └── page.tsx
├── git-mojis
│ ├── loading.tsx
│ └── page.tsx
├── globals.css
├── layout.tsx
└── page.tsx
├── components.json
├── components
├── auth
│ ├── login-modal.tsx
│ ├── protected-route.tsx
│ └── user-auth-button.tsx
├── core
│ ├── accordion.tsx
│ └── text-shimmer-wave.tsx
├── readme
│ └── generation-status.tsx
├── theme-provider.tsx
└── ui
│ ├── accordion.tsx
│ ├── alert-dialog.tsx
│ ├── alert.tsx
│ ├── animated-beams.tsx
│ ├── aspect-ratio.tsx
│ ├── avatar.tsx
│ ├── badge-shine.tsx
│ ├── badge.tsx
│ ├── breadcrumb.tsx
│ ├── button.tsx
│ ├── calendar.tsx
│ ├── card.tsx
│ ├── carousel.tsx
│ ├── chart.tsx
│ ├── checkbox.tsx
│ ├── code-block.tsx
│ ├── code-snippet.tsx
│ ├── collapsible.tsx
│ ├── command.tsx
│ ├── context-menu.tsx
│ ├── dialog.tsx
│ ├── drawer.tsx
│ ├── dropdown-menu.tsx
│ ├── form.tsx
│ ├── hover-card.tsx
│ ├── input-otp.tsx
│ ├── input-pulse-border.tsx
│ ├── input.tsx
│ ├── label.tsx
│ ├── markdown.tsx
│ ├── menubar.tsx
│ ├── message.tsx
│ ├── mobile-menu.tsx
│ ├── navbar.tsx
│ ├── navigation-menu.tsx
│ ├── pagination.tsx
│ ├── popover.tsx
│ ├── progress.tsx
│ ├── radio-group.tsx
│ ├── resizable.tsx
│ ├── response-stream.tsx
│ ├── scroll-area.tsx
│ ├── select.tsx
│ ├── separator.tsx
│ ├── sheet.tsx
│ ├── sidebar.tsx
│ ├── skeleton.tsx
│ ├── slider.tsx
│ ├── sonner.tsx
│ ├── suggestion-card.tsx
│ ├── switch.tsx
│ ├── table.tsx
│ ├── tabs.tsx
│ ├── text-animated-gradient.tsx
│ ├── textarea.tsx
│ ├── theme-toggle.tsx
│ ├── toast.tsx
│ ├── toaster.tsx
│ ├── toggle-group.tsx
│ ├── toggle.tsx
│ ├── tooltip.tsx
│ ├── use-mobile.tsx
│ ├── use-toast.ts
│ ├── user-auth-button.tsx
│ ├── user-menu.tsx
│ └── user-profile-avatar.tsx
├── context
└── auth-context.tsx
├── hooks
├── use-mobile.tsx
└── use-toast.ts
├── lib
├── firebase.ts
├── redis.ts
├── timeout-utils.ts
└── utils.ts
├── next.config.mjs
├── package-lock.json
├── package.json
├── pnpm-lock.yaml
├── postcss.config.mjs
├── public
├── gitfriend-icon.png
├── image.jpg
├── placeholder-logo.png
├── placeholder-logo.svg
├── placeholder-user.jpg
├── placeholder.jpg
└── placeholder.svg
├── styles
└── globals.css
├── tailwind.config.ts
└── tsconfig.json
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 |
6 | # next.js
7 | /.next/
8 | /out/
9 |
10 | # production
11 | /build
12 |
13 | # debug
14 | npm-debug.log*
15 | yarn-debug.log*
16 | yarn-error.log*
17 | .pnpm-debug.log*
18 |
19 | # env files
20 | .env*
21 |
22 | # vercel
23 | .vercel
24 |
25 | # typescript
26 | *.tsbuildinfo
27 | next-env.d.ts
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 krishn404
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the “Software”), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/app/ai-chat/loading.tsx:
--------------------------------------------------------------------------------
1 | export default function Loading() {
2 | return null
3 | }
4 |
--------------------------------------------------------------------------------
/app/api/chat/route.ts:
--------------------------------------------------------------------------------
1 | import { NextResponse } from "next/server"
2 | import { Groq } from 'groq-sdk'
3 |
4 | // Initialize Groq
5 | const groq = new Groq({
6 | apiKey: process.env.GROQ_API_KEY!,
7 | })
8 |
9 | export async function POST(req: Request) {
10 | try {
11 | const { messages } = await req.json()
12 |
13 | // Enhanced system message with greeting instructions
14 | const systemMessage = {
15 | role: "system",
16 | content: `You are GitFriend, an AI assistant specializing in Git and GitHub. Your responses should be detailed, accurate, and tailored to helping developers solve Git and GitHub-related problems.
17 |
18 | GREETING BEHAVIOR:
19 | - When a user greets you, respond with a VARIED, brief greeting that sounds natural
20 | - NEVER use the same greeting formula twice in a conversation
21 | - Randomly select different topics you can help with for each greeting (from a wide range of Git/GitHub topics)
22 | - Vary your sentence structure and word choice in greetings
23 | - DO NOT introduce yourself the same way each time
24 | - Keep greeting responses concise and friendly
25 | - Add slight personality variations in your tone
26 |
27 | RESPONSE FORMAT:
28 | - ALWAYS format responses using proper markdown syntax
29 | - Use code blocks with appropriate language specifiers (e.g., \`\`\`bash, \`\`\`git)
30 | - Utilize bold, italic, lists, and tables where appropriate
31 | - For commands, show both the command and expected output where helpful
32 | - Use emoji strategically: ✅ for best practices, ⚠️ for warnings, 🔍 for tips
33 |
34 | EXPERTISE AREAS:
35 | - Git basics (init, clone, add, commit, push, pull)
36 | - Branching strategies (feature branches, GitFlow, trunk-based)
37 | - Merge workflows and resolving conflicts
38 | - GitHub features (PR, Issues, Actions, Projects)
39 | - Advanced Git operations (rebase, cherry-pick, bisect)
40 | - Git hooks and automation
41 | - Repository management and organization
42 |
43 | RESPONSE STYLE:
44 | - Be direct and practical - developers want solutions
45 | - Provide context about WHY certain approaches are recommended
46 | - Include common pitfalls to avoid
47 | - When appropriate, offer both beginner-friendly AND advanced approaches
48 | - For complex topics, break down explanations into clear steps
49 | - When relevant, mention alternative approaches
50 |
51 | Always aim to be complete and correct. If the query is ambiguous, ask clarifying questions.`,
52 | }
53 |
54 | const completion = await groq.chat.completions.create({
55 | messages: [systemMessage, ...messages],
56 | model: "llama3-8b-8192",
57 | temperature: 0.7,
58 | max_completion_tokens: 2048,
59 | top_p: 1,
60 | stream: true,
61 | stop: null
62 | })
63 |
64 | // Create a streaming response
65 | const stream = new ReadableStream({
66 | async start(controller) {
67 | const encoder = new TextEncoder()
68 |
69 | try {
70 | for await (const chunk of completion) {
71 | const content = chunk.choices[0]?.delta?.content || ""
72 |
73 | if (content) {
74 | // Format as a proper SSE message
75 | controller.enqueue(encoder.encode(`data: ${JSON.stringify({ content })}\n\n`))
76 | }
77 | }
78 |
79 | // Send a final message to indicate the stream is complete
80 | controller.enqueue(encoder.encode(`data: [DONE]\n\n`))
81 | controller.close()
82 | } catch (error) {
83 | console.error("Stream processing error:", error)
84 | controller.error(error)
85 | }
86 | },
87 | })
88 |
89 | // Return a proper SSE response
90 | return new Response(stream, {
91 | headers: {
92 | "Content-Type": "text/event-stream",
93 | "Cache-Control": "no-cache, no-transform",
94 | Connection: "keep-alive",
95 | },
96 | })
97 | } catch (error) {
98 | console.error("Groq API Error:", error)
99 | return NextResponse.json({ error: "Failed to get response from Groq" }, { status: 500 })
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/app/api/fetch-repo-data/route.ts:
--------------------------------------------------------------------------------
1 | import { Octokit } from "@octokit/rest"
2 | import { type NextRequest, NextResponse } from "next/server"
3 |
4 | const octokit = new Octokit({
5 | auth: process.env.GITHUB_ACCESS_TOKEN,
6 | })
7 |
8 | export async function POST(req: NextRequest) {
9 | try {
10 | const { repoUrl } = await req.json()
11 |
12 | const match = repoUrl.match(/github\.com\/(.+?)\/(.+?)(?:\.git)?$/)
13 | if (!match) {
14 | return NextResponse.json({ error: "Invalid GitHub URL" }, { status: 400 })
15 | }
16 |
17 | const [_, owner, repo] = match
18 |
19 | const { data: repoData } = await octokit.repos.get({ owner, repo })
20 |
21 | const { data: contents } = await octokit.repos.getContent({ owner, repo, path: "" })
22 |
23 | const files = Array.isArray(contents) ? contents.map((file) => ({ name: file.name, type: file.type })) : []
24 |
25 | const languages = await fetchLanguages(owner, repo)
26 |
27 | return NextResponse.json({
28 | name: repoData.name,
29 | description: repoData.description,
30 | stars: repoData.stargazers_count,
31 | forks: repoData.forks_count,
32 | languages,
33 | files,
34 | })
35 | } catch (error: any) {
36 | console.error("Error fetching repo data:", error)
37 | return NextResponse.json({ error: "Failed to fetch repository data", details: error.message }, { status: 500 })
38 | }
39 | }
40 |
41 | async function fetchLanguages(owner: string, repo: string) {
42 | const { data } = await octokit.repos.listLanguages({ owner, repo })
43 | return data
44 | }
45 |
--------------------------------------------------------------------------------
/app/api/git-moji/route.ts:
--------------------------------------------------------------------------------
1 | export async function GET() {
2 | const res = await fetch("https://api.jsonbin.io/v3/b/YOUR_BIN_ID", {
3 | headers: {
4 | "X-Master-Key": "YOUR_API_KEY",
5 | },
6 | cache: "no-store",
7 | });
8 |
9 | const data = await res.json();
10 |
11 | return NextResponse.json(data.record.emojis.nature);
12 | }
13 |
--------------------------------------------------------------------------------
/app/api/gitmojis/route.ts:
--------------------------------------------------------------------------------
1 | export async function GET() {
2 | const res = await fetch("https://api.jsonbin.io/v3/b/YOUR_BIN_ID", {
3 | headers: {
4 | "X-Master-Key": "YOUR_API_KEY",
5 | },
6 | cache: "no-store",
7 | })
8 |
9 | const data = await res.json()
10 |
11 | // Adjust this line to match your actual structure:
12 | return NextResponse.json(data.record.emojis.nature)
13 | }
14 |
--------------------------------------------------------------------------------
/app/git-mojis/loading.tsx:
--------------------------------------------------------------------------------
1 | export default function Loading() {
2 | return null
3 | }
4 |
--------------------------------------------------------------------------------
/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import type React from "react"
2 | import "@/app/globals.css"
3 | import { Inter } from "next/font/google"
4 | import { ThemeProvider } from "@/components/theme-provider"
5 | import { AuthProvider } from "@/context/auth-context"
6 | import { Analytics } from "@vercel/analytics/next"
7 | import { Toaster } from "@/components/ui/toaster"
8 | import { Suspense } from "react"
9 |
10 | const inter = Inter({ subsets: ["latin"] })
11 |
12 | export const metadata = {
13 | title: "Git Friend - Make Git Simple Again",
14 | description:
15 | "Git Friend simplifies complex Git workflows, making version control intuitive and collaborative for developers of all skill levels.",
16 | }
17 |
18 | export default function RootLayout({
19 | children,
20 | }: {
21 | children: React.ReactNode
22 | }) {
23 | return (
24 |
25 |
26 |
27 |
28 | {children}
29 |
30 |
31 |
32 |
33 |
34 |
35 | )
36 | }
37 |
--------------------------------------------------------------------------------
/components.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://ui.shadcn.com/schema.json",
3 | "style": "default",
4 | "rsc": true,
5 | "tsx": true,
6 | "tailwind": {
7 | "config": "tailwind.config.ts",
8 | "css": "app/globals.css",
9 | "baseColor": "neutral",
10 | "cssVariables": true,
11 | "prefix": ""
12 | },
13 | "aliases": {
14 | "components": "@/components",
15 | "utils": "@/lib/utils",
16 | "ui": "@/components/ui",
17 | "lib": "@/lib",
18 | "hooks": "@/hooks"
19 | },
20 | "iconLibrary": "lucide"
21 | }
22 |
--------------------------------------------------------------------------------
/components/auth/login-modal.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { useState } from "react"
4 | import { Button } from "@/components/ui/button"
5 | import { useAuth } from "@/context/auth-context"
6 | import { X, GitBranch } from "lucide-react"
7 | import { motion } from "framer-motion"
8 | import { FcGoogle } from "react-icons/fc"
9 |
10 | interface LoginModalProps {
11 | isOpen: boolean
12 | onClose: () => void
13 | onSuccess?: () => void
14 | }
15 |
16 | export function LoginModal({ isOpen, onClose, onSuccess }: LoginModalProps) {
17 | const { signInWithGoogle } = useAuth()
18 | const [isLoading, setIsLoading] = useState(false)
19 |
20 | const handleLogin = async () => {
21 | setIsLoading(true)
22 | try {
23 | await signInWithGoogle()
24 | onSuccess?.()
25 | } catch (error) {
26 | console.error("Error signing in:", error)
27 | } finally {
28 | setIsLoading(false)
29 | }
30 | }
31 |
32 | if (!isOpen) return null
33 |
34 | return (
35 |
36 |
42 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
Sign in to Git Friend
52 |
You need to be signed in to access this feature.
53 |
54 |
62 |
63 |
64 |
65 | )
66 | }
67 |
--------------------------------------------------------------------------------
/components/auth/protected-route.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import type React from "react"
4 |
5 | import { useEffect, useState } from "react"
6 | import { useAuth } from "@/context/auth-context"
7 | import { LoginModal } from "@/components/auth/login-modal"
8 | import { useRouter, usePathname } from "next/navigation"
9 |
10 | interface ProtectedRouteProps {
11 | children: React.ReactNode
12 | }
13 |
14 | export function ProtectedRoute({ children }: ProtectedRouteProps) {
15 | const { user, loading } = useAuth()
16 | const [showLoginModal, setShowLoginModal] = useState(false)
17 | const router = useRouter()
18 | const pathname = usePathname()
19 |
20 | useEffect(() => {
21 | // Only show the login modal if the user is not logged in and we're done loading
22 | if (!loading && !user) {
23 | setShowLoginModal(true)
24 | }
25 | }, [user, loading])
26 |
27 | const handleLoginSuccess = () => {
28 | setShowLoginModal(false)
29 | }
30 |
31 | const handleCloseModal = () => {
32 | setShowLoginModal(false)
33 | router.push("/")
34 | }
35 |
36 | // Show loading state while checking authentication
37 | if (loading) {
38 | return (
39 |
40 |
41 |
42 |
Loading...
43 |
44 |
45 | )
46 | }
47 |
48 | return (
49 | <>
50 | {user ? children : null}
51 |
52 | >
53 | )
54 | }
55 |
--------------------------------------------------------------------------------
/components/auth/user-auth-button.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { useState } from "react"
4 | import { Button } from "@/components/ui/button"
5 | import { useAuth } from "@/context/auth-context"
6 | import { LogOut } from "lucide-react"
7 | import { FcGoogle } from "react-icons/fc"
8 | import {
9 | DropdownMenu,
10 | DropdownMenuContent,
11 | DropdownMenuItem,
12 | DropdownMenuLabel,
13 | DropdownMenuSeparator,
14 | DropdownMenuTrigger,
15 | } from "@/components/ui/dropdown-menu"
16 | import { UserProfileAvatar } from "@/components/ui/user-profile-avatar"
17 |
18 | export function UserAuthButton() {
19 | const { user, loading, signInWithGoogle, signOut } = useAuth()
20 | const [isSigningIn, setIsSigningIn] = useState(false)
21 |
22 | const handleSignIn = async () => {
23 | setIsSigningIn(true)
24 | try {
25 | await signInWithGoogle()
26 | } finally {
27 | setIsSigningIn(false)
28 | }
29 | }
30 |
31 | if (loading) {
32 | return (
33 |
37 | )
38 | }
39 |
40 | if (user) {
41 | return (
42 |
43 |
44 |
47 |
48 |
49 |
50 | {user.displayName || "User"}
51 | {user.email && {user.email}}
52 |
53 |
54 | signOut()}>
55 |
56 | Log out
57 |
58 |
59 |
60 | )
61 | }
62 |
63 | return (
64 |
65 |
75 |
76 | )
77 | }
78 |
--------------------------------------------------------------------------------
/components/core/accordion.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 | import { motion, AnimatePresence, type Transition, type Variants, type Variant, MotionConfig } from "motion/react"
3 | import { cn } from "@/lib/utils"
4 | import React, { createContext, useContext, useState, type ReactNode, useEffect, useCallback, useMemo } from "react"
5 |
6 | export type AccordionContextType = {
7 | expandedValue: React.Key | null
8 | toggleItem: (value: React.Key) => void
9 | variants?: { expanded: Variant; collapsed: Variant }
10 | }
11 |
12 | const AccordionContext = createContext(undefined)
13 |
14 | function useAccordion() {
15 | const context = useContext(AccordionContext)
16 | if (!context) {
17 | throw new Error("useAccordion must be used within an AccordionProvider")
18 | }
19 | return context
20 | }
21 |
22 | export type AccordionProviderProps = {
23 | children: ReactNode
24 | variants?: { expanded: Variant; collapsed: Variant }
25 | expandedValue?: React.Key | null
26 | onValueChange?: (value: React.Key | null) => void
27 | }
28 |
29 | function AccordionProvider({
30 | children,
31 | variants,
32 | expandedValue: externalExpandedValue,
33 | onValueChange,
34 | }: AccordionProviderProps) {
35 | const [internalExpandedValue, setInternalExpandedValue] = useState(externalExpandedValue ?? null)
36 |
37 | // Use useEffect to handle external value changes after hydration
38 | useEffect(() => {
39 | if (externalExpandedValue !== undefined) {
40 | setInternalExpandedValue(externalExpandedValue)
41 | }
42 | }, [externalExpandedValue])
43 |
44 | const toggleItem = useCallback(
45 | (value: React.Key) => {
46 | setInternalExpandedValue((prev) => {
47 | const newValue = prev === value ? null : value
48 | onValueChange?.(newValue)
49 | return newValue
50 | })
51 | },
52 | [onValueChange],
53 | )
54 |
55 | const contextValue = useMemo(
56 | () => ({
57 | expandedValue: internalExpandedValue,
58 | toggleItem,
59 | variants,
60 | }),
61 | [internalExpandedValue, toggleItem, variants],
62 | )
63 |
64 | return (
65 |
66 | {React.Children.map(children, (child) => {
67 | if (!React.isValidElement(child)) return child
68 | return React.cloneElement(child, {
69 | ...child.props,
70 | value: child.props.value ?? child.key,
71 | })
72 | })}
73 |
74 | )
75 | }
76 |
77 | export type AccordionProps = {
78 | children: ReactNode
79 | className?: string
80 | transition?: Transition
81 | variants?: { expanded: Variant; collapsed: Variant }
82 | expandedValue?: React.Key | null
83 | onValueChange?: (value: React.Key | null) => void
84 | }
85 |
86 | function Accordion({ children, className, transition, variants, expandedValue, onValueChange }: AccordionProps) {
87 | return (
88 |
89 |
90 |
91 | {children}
92 |
93 |
94 |
95 | )
96 | }
97 |
98 | export type AccordionItemProps = {
99 | value: React.Key
100 | children: ReactNode
101 | className?: string
102 | }
103 |
104 | function AccordionItem({ value, children, className }: AccordionItemProps) {
105 | const { expandedValue } = useAccordion()
106 | const isExpanded = value === expandedValue
107 |
108 | return (
109 |
113 | {React.Children.map(children, (child) => {
114 | if (React.isValidElement(child)) {
115 | return React.cloneElement(child, {
116 | ...child.props,
117 | value,
118 | expanded: isExpanded,
119 | })
120 | }
121 | return child
122 | })}
123 |
124 | )
125 | }
126 |
127 | export type AccordionTriggerProps = {
128 | children: ReactNode
129 | className?: string
130 | }
131 |
132 | function AccordionTrigger({ children, className, ...props }: AccordionTriggerProps) {
133 | const { toggleItem, expandedValue } = useAccordion()
134 | const value = (props as { value?: React.Key }).value
135 | const isExpanded = value === expandedValue
136 |
137 | return (
138 |
147 | )
148 | }
149 |
150 | export type AccordionContentProps = {
151 | children: ReactNode
152 | className?: string
153 | }
154 |
155 | function AccordionContent({ children, className, ...props }: AccordionContentProps) {
156 | const { expandedValue, variants } = useAccordion()
157 | const value = (props as { value?: React.Key }).value
158 | const isExpanded = value === expandedValue
159 |
160 | const BASE_VARIANTS: Variants = {
161 | expanded: {
162 | height: "auto",
163 | opacity: 1,
164 | scale: 1,
165 | transition: {
166 | height: { duration: 0.3, ease: [0.4, 0, 0.2, 1] },
167 | opacity: { duration: 0.2, ease: [0.4, 0, 0.2, 1] },
168 | scale: { duration: 0.2, ease: [0.4, 0, 0.2, 1] },
169 | },
170 | },
171 | collapsed: {
172 | height: 0,
173 | opacity: 0,
174 | scale: 0.98,
175 | transition: {
176 | height: { duration: 0.3, ease: [0.4, 0, 0.2, 1] },
177 | opacity: { duration: 0.2, ease: [0.4, 0, 0.2, 1] },
178 | scale: { duration: 0.2, ease: [0.4, 0, 0.2, 1] },
179 | },
180 | },
181 | }
182 |
183 | const combinedVariants = {
184 | expanded: { ...BASE_VARIANTS.expanded, ...variants?.expanded },
185 | collapsed: { ...BASE_VARIANTS.collapsed, ...variants?.collapsed },
186 | }
187 |
188 | return (
189 |
190 | {isExpanded && (
191 |
198 | {children}
199 |
200 | )}
201 |
202 | )
203 | }
204 |
205 | export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
206 |
--------------------------------------------------------------------------------
/components/core/text-shimmer-wave.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import { cn } from "@/lib/utils"
5 |
6 | interface TextShimmerWaveProps extends React.HTMLAttributes {
7 | children: React.ReactNode
8 | duration?: number
9 | spread?: number
10 | zDistance?: number
11 | scaleDistance?: number
12 | rotateYDistance?: number
13 | }
14 |
15 | export function TextShimmerWave({
16 | children,
17 | className,
18 | duration = 1,
19 | spread = 1,
20 | zDistance = 1,
21 | scaleDistance = 1.1,
22 | rotateYDistance = 20,
23 | ...props
24 | }: TextShimmerWaveProps) {
25 | const childrenArray = React.Children.toArray(children)
26 | const childrenCount = childrenArray.length
27 | const childrenElements = childrenArray.map((child, i) => {
28 | const progress = i / (childrenCount - 1)
29 | const delay = progress * duration * spread
30 | const style = {
31 | "--delay": `${delay}s`,
32 | "--duration": `${duration}s`,
33 | "--z-distance": zDistance,
34 | "--scale-distance": scaleDistance,
35 | "--rotate-y-distance": `${rotateYDistance}deg`,
36 | } as React.CSSProperties
37 |
38 | return (
39 |
40 | {child}
41 |
42 | )
43 | })
44 |
45 | return (
46 |
53 | {childrenElements}
54 |
55 | )
56 | }
57 |
--------------------------------------------------------------------------------
/components/theme-provider.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 | import { ThemeProvider as NextThemesProvider } from "next-themes"
3 | import type { ThemeProviderProps } from "next-themes/dist/types"
4 |
5 | export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
6 | return (
7 |
8 | {children}
9 |
10 | )
11 | }
12 |
--------------------------------------------------------------------------------
/components/ui/accordion.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as AccordionPrimitive from "@radix-ui/react-accordion"
5 | import { ChevronDown } from "lucide-react"
6 |
7 | import { cn } from "@/lib/utils"
8 |
9 | const Accordion = AccordionPrimitive.Root
10 |
11 | const AccordionItem = React.forwardRef<
12 | React.ElementRef,
13 | React.ComponentPropsWithoutRef
14 | >(({ className, ...props }, ref) => (
15 |
16 | ))
17 | AccordionItem.displayName = "AccordionItem"
18 |
19 | const AccordionTrigger = React.forwardRef<
20 | React.ElementRef,
21 | React.ComponentPropsWithoutRef
22 | >(({ className, children, ...props }, ref) => (
23 |
24 | svg]:rotate-180",
28 | className,
29 | )}
30 | {...props}
31 | >
32 | {children}
33 |
34 |
35 |
36 | ))
37 | AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName
38 |
39 | const AccordionContent = React.forwardRef<
40 | React.ElementRef,
41 | React.ComponentPropsWithoutRef
42 | >(({ className, children, ...props }, ref) => (
43 |
48 | {children}
49 |
50 | ))
51 |
52 | AccordionContent.displayName = AccordionPrimitive.Content.displayName
53 |
54 | export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
55 |
--------------------------------------------------------------------------------
/components/ui/alert-dialog.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"
5 |
6 | import { cn } from "@/lib/utils"
7 | import { buttonVariants } from "@/components/ui/button"
8 |
9 | const AlertDialog = AlertDialogPrimitive.Root
10 |
11 | const AlertDialogTrigger = AlertDialogPrimitive.Trigger
12 |
13 | const AlertDialogPortal = AlertDialogPrimitive.Portal
14 |
15 | const AlertDialogOverlay = React.forwardRef<
16 | React.ElementRef,
17 | React.ComponentPropsWithoutRef
18 | >(({ className, ...props }, ref) => (
19 |
27 | ))
28 | AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName
29 |
30 | const AlertDialogContent = React.forwardRef<
31 | React.ElementRef,
32 | React.ComponentPropsWithoutRef
33 | >(({ className, ...props }, ref) => (
34 |
35 |
36 |
44 |
45 | ))
46 | AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName
47 |
48 | const AlertDialogHeader = ({
49 | className,
50 | ...props
51 | }: React.HTMLAttributes) => (
52 |
59 | )
60 | AlertDialogHeader.displayName = "AlertDialogHeader"
61 |
62 | const AlertDialogFooter = ({
63 | className,
64 | ...props
65 | }: React.HTMLAttributes) => (
66 |
73 | )
74 | AlertDialogFooter.displayName = "AlertDialogFooter"
75 |
76 | const AlertDialogTitle = React.forwardRef<
77 | React.ElementRef,
78 | React.ComponentPropsWithoutRef
79 | >(({ className, ...props }, ref) => (
80 |
85 | ))
86 | AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName
87 |
88 | const AlertDialogDescription = React.forwardRef<
89 | React.ElementRef,
90 | React.ComponentPropsWithoutRef
91 | >(({ className, ...props }, ref) => (
92 |
97 | ))
98 | AlertDialogDescription.displayName =
99 | AlertDialogPrimitive.Description.displayName
100 |
101 | const AlertDialogAction = React.forwardRef<
102 | React.ElementRef,
103 | React.ComponentPropsWithoutRef
104 | >(({ className, ...props }, ref) => (
105 |
110 | ))
111 | AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName
112 |
113 | const AlertDialogCancel = React.forwardRef<
114 | React.ElementRef,
115 | React.ComponentPropsWithoutRef
116 | >(({ className, ...props }, ref) => (
117 |
126 | ))
127 | AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName
128 |
129 | export {
130 | AlertDialog,
131 | AlertDialogPortal,
132 | AlertDialogOverlay,
133 | AlertDialogTrigger,
134 | AlertDialogContent,
135 | AlertDialogHeader,
136 | AlertDialogFooter,
137 | AlertDialogTitle,
138 | AlertDialogDescription,
139 | AlertDialogAction,
140 | AlertDialogCancel,
141 | }
142 |
--------------------------------------------------------------------------------
/components/ui/alert.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import { cva, type VariantProps } from "class-variance-authority"
3 |
4 | import { cn } from "@/lib/utils"
5 |
6 | const alertVariants = cva(
7 | "relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground",
8 | {
9 | variants: {
10 | variant: {
11 | default: "bg-background text-foreground",
12 | destructive: "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive",
13 | },
14 | },
15 | defaultVariants: {
16 | variant: "default",
17 | },
18 | },
19 | )
20 |
21 | const Alert = React.forwardRef<
22 | HTMLDivElement,
23 | React.HTMLAttributes & VariantProps
24 | >(({ className, variant, ...props }, ref) => (
25 |
26 | ))
27 | Alert.displayName = "Alert"
28 |
29 | const AlertTitle = React.forwardRef>(
30 | ({ className, ...props }, ref) => (
31 |
32 | ),
33 | )
34 | AlertTitle.displayName = "AlertTitle"
35 |
36 | const AlertDescription = React.forwardRef>(
37 | ({ className, ...props }, ref) => (
38 |
39 | ),
40 | )
41 | AlertDescription.displayName = "AlertDescription"
42 |
43 | export { Alert, AlertTitle, AlertDescription }
44 |
--------------------------------------------------------------------------------
/components/ui/animated-beams.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 | import { useMemo } from "react"
3 | import { motion } from "framer-motion"
4 | import { cn } from "@/lib/utils"
5 |
6 | interface AnimatedBeamsProps {
7 | className?: string
8 | beamCount?: number
9 | minOpacity?: number
10 | maxOpacity?: number
11 | minSize?: number
12 | maxSize?: number
13 | minDuration?: number
14 | maxDuration?: number
15 | minDelay?: number
16 | maxDelay?: number
17 | }
18 |
19 | export function AnimatedBeams({ className }: { className?: string }) {
20 | // Use a fixed seed or deterministic values instead of random
21 | const beams = useMemo(() => {
22 | return Array.from({ length: 7 }).map((_, i) => ({
23 | width: `${30 + i * 5}vw`,
24 | height: `${30 + i * 5}vw`,
25 | left: `${20 + i * 10}%`,
26 | top: `${20 + i * 10}%`,
27 | opacity: 0,
28 | transform: "scale(0.8)",
29 | background: `radial-gradient(circle, rgba(139, 92, 246, ${0.1 + i * 0.01}) 0%, rgba(139, 92, 246, 0) 70%)`,
30 | }))
31 | }, [])
32 |
33 | return (
34 |
35 | {beams.map((beam, i) => (
36 |
49 | ))}
50 |
51 | )
52 | }
53 |
--------------------------------------------------------------------------------
/components/ui/aspect-ratio.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio"
4 |
5 | const AspectRatio = AspectRatioPrimitive.Root
6 |
7 | export { AspectRatio }
8 |
--------------------------------------------------------------------------------
/components/ui/avatar.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as AvatarPrimitive from "@radix-ui/react-avatar"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const Avatar = React.forwardRef<
9 | React.ElementRef,
10 | React.ComponentPropsWithoutRef
11 | >(({ className, ...props }, ref) => (
12 |
17 | ))
18 | Avatar.displayName = AvatarPrimitive.Root.displayName
19 |
20 | const AvatarImage = React.forwardRef<
21 | React.ElementRef,
22 | React.ComponentPropsWithoutRef
23 | >(({ className, ...props }, ref) => (
24 |
25 | ))
26 | AvatarImage.displayName = AvatarPrimitive.Image.displayName
27 |
28 | const AvatarFallback = React.forwardRef<
29 | React.ElementRef,
30 | React.ComponentPropsWithoutRef
31 | >(({ className, ...props }, ref) => (
32 |
40 | ))
41 | AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName
42 |
43 | export { Avatar, AvatarImage, AvatarFallback }
44 |
--------------------------------------------------------------------------------
/components/ui/badge-shine.tsx:
--------------------------------------------------------------------------------
1 | import type React from "react"
2 | const BadgeShine = ({ children }: { children: React.ReactNode }) => {
3 | return (
4 |
5 | {children}
6 |
7 | )
8 | }
9 |
10 | export default BadgeShine
11 |
--------------------------------------------------------------------------------
/components/ui/badge.tsx:
--------------------------------------------------------------------------------
1 | import type * as React from "react"
2 | import { cva, type VariantProps } from "class-variance-authority"
3 |
4 | import { cn } from "@/lib/utils"
5 |
6 | const badgeVariants = cva(
7 | "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
8 | {
9 | variants: {
10 | variant: {
11 | default: "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
12 | secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
13 | destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
14 | outline: "text-foreground",
15 | },
16 | },
17 | defaultVariants: {
18 | variant: "default",
19 | },
20 | },
21 | )
22 |
23 | export interface BadgeProps extends React.HTMLAttributes, VariantProps {}
24 |
25 | function Badge({ className, variant, ...props }: BadgeProps) {
26 | return
27 | }
28 |
29 | export { Badge, badgeVariants }
30 |
--------------------------------------------------------------------------------
/components/ui/breadcrumb.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import { Slot } from "@radix-ui/react-slot"
3 | import { ChevronRight, MoreHorizontal } from "lucide-react"
4 |
5 | import { cn } from "@/lib/utils"
6 |
7 | const Breadcrumb = React.forwardRef<
8 | HTMLElement,
9 | React.ComponentPropsWithoutRef<"nav"> & {
10 | separator?: React.ReactNode
11 | }
12 | >(({ ...props }, ref) => )
13 | Breadcrumb.displayName = "Breadcrumb"
14 |
15 | const BreadcrumbList = React.forwardRef<
16 | HTMLOListElement,
17 | React.ComponentPropsWithoutRef<"ol">
18 | >(({ className, ...props }, ref) => (
19 |
27 | ))
28 | BreadcrumbList.displayName = "BreadcrumbList"
29 |
30 | const BreadcrumbItem = React.forwardRef<
31 | HTMLLIElement,
32 | React.ComponentPropsWithoutRef<"li">
33 | >(({ className, ...props }, ref) => (
34 |
39 | ))
40 | BreadcrumbItem.displayName = "BreadcrumbItem"
41 |
42 | const BreadcrumbLink = React.forwardRef<
43 | HTMLAnchorElement,
44 | React.ComponentPropsWithoutRef<"a"> & {
45 | asChild?: boolean
46 | }
47 | >(({ asChild, className, ...props }, ref) => {
48 | const Comp = asChild ? Slot : "a"
49 |
50 | return (
51 |
56 | )
57 | })
58 | BreadcrumbLink.displayName = "BreadcrumbLink"
59 |
60 | const BreadcrumbPage = React.forwardRef<
61 | HTMLSpanElement,
62 | React.ComponentPropsWithoutRef<"span">
63 | >(({ className, ...props }, ref) => (
64 |
72 | ))
73 | BreadcrumbPage.displayName = "BreadcrumbPage"
74 |
75 | const BreadcrumbSeparator = ({
76 | children,
77 | className,
78 | ...props
79 | }: React.ComponentProps<"li">) => (
80 | svg]:w-3.5 [&>svg]:h-3.5", className)}
84 | {...props}
85 | >
86 | {children ?? }
87 |
88 | )
89 | BreadcrumbSeparator.displayName = "BreadcrumbSeparator"
90 |
91 | const BreadcrumbEllipsis = ({
92 | className,
93 | ...props
94 | }: React.ComponentProps<"span">) => (
95 |
101 |
102 | More
103 |
104 | )
105 | BreadcrumbEllipsis.displayName = "BreadcrumbElipssis"
106 |
107 | export {
108 | Breadcrumb,
109 | BreadcrumbList,
110 | BreadcrumbItem,
111 | BreadcrumbLink,
112 | BreadcrumbPage,
113 | BreadcrumbSeparator,
114 | BreadcrumbEllipsis,
115 | }
116 |
--------------------------------------------------------------------------------
/components/ui/button.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import { Slot } from "@radix-ui/react-slot"
3 | import { cva, type VariantProps } from "class-variance-authority"
4 |
5 | import { cn } from "@/lib/utils"
6 |
7 | const buttonVariants = cva(
8 | "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
9 | {
10 | variants: {
11 | variant: {
12 | default: "bg-primary text-primary-foreground hover:bg-primary/90",
13 | destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
14 | outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
15 | secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
16 | ghost: "hover:bg-accent hover:text-accent-foreground",
17 | link: "text-primary underline-offset-4 hover:underline",
18 | },
19 | size: {
20 | default: "h-10 px-4 py-2",
21 | sm: "h-9 rounded-md px-3",
22 | lg: "h-11 rounded-md px-8",
23 | icon: "h-10 w-10",
24 | },
25 | },
26 | defaultVariants: {
27 | variant: "default",
28 | size: "default",
29 | },
30 | },
31 | )
32 |
33 | export interface ButtonProps
34 | extends React.ButtonHTMLAttributes,
35 | VariantProps {
36 | asChild?: boolean
37 | }
38 |
39 | const Button = React.forwardRef(
40 | ({ className, variant, size, asChild = false, ...props }, ref) => {
41 | const Comp = asChild ? Slot : "button"
42 | return
43 | },
44 | )
45 | Button.displayName = "Button"
46 |
47 | export { Button, buttonVariants }
48 |
--------------------------------------------------------------------------------
/components/ui/calendar.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import { ChevronLeft, ChevronRight } from "lucide-react"
5 | import { DayPicker } from "react-day-picker"
6 |
7 | import { cn } from "@/lib/utils"
8 | import { buttonVariants } from "@/components/ui/button"
9 |
10 | export type CalendarProps = React.ComponentProps
11 |
12 | function Calendar({
13 | className,
14 | classNames,
15 | showOutsideDays = true,
16 | ...props
17 | }: CalendarProps) {
18 | return (
19 | ,
58 | IconRight: ({ ...props }) => ,
59 | }}
60 | {...props}
61 | />
62 | )
63 | }
64 | Calendar.displayName = "Calendar"
65 |
66 | export { Calendar }
67 |
--------------------------------------------------------------------------------
/components/ui/card.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 |
3 | import { cn } from "@/lib/utils"
4 |
5 | const Card = React.forwardRef>(({ className, ...props }, ref) => (
6 |
14 | ))
15 | Card.displayName = "Card"
16 |
17 | const CardHeader = React.forwardRef>(
18 | ({ className, ...props }, ref) => (
19 |
20 | ),
21 | )
22 | CardHeader.displayName = "CardHeader"
23 |
24 | const CardTitle = React.forwardRef>(
25 | ({ className, ...props }, ref) => (
26 |
31 | ),
32 | )
33 | CardTitle.displayName = "CardTitle"
34 |
35 | const CardDescription = React.forwardRef>(
36 | ({ className, ...props }, ref) => (
37 |
38 | ),
39 | )
40 | CardDescription.displayName = "CardDescription"
41 |
42 | const CardContent = React.forwardRef>(
43 | ({ className, ...props }, ref) => ,
44 | )
45 | CardContent.displayName = "CardContent"
46 |
47 | const CardFooter = React.forwardRef>(
48 | ({ className, ...props }, ref) => (
49 |
50 | ),
51 | )
52 | CardFooter.displayName = "CardFooter"
53 |
54 | export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
55 |
--------------------------------------------------------------------------------
/components/ui/carousel.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import useEmblaCarousel, {
5 | type UseEmblaCarouselType,
6 | } from "embla-carousel-react"
7 | import { ArrowLeft, ArrowRight } from "lucide-react"
8 |
9 | import { cn } from "@/lib/utils"
10 | import { Button } from "@/components/ui/button"
11 |
12 | type CarouselApi = UseEmblaCarouselType[1]
13 | type UseCarouselParameters = Parameters
14 | type CarouselOptions = UseCarouselParameters[0]
15 | type CarouselPlugin = UseCarouselParameters[1]
16 |
17 | type CarouselProps = {
18 | opts?: CarouselOptions
19 | plugins?: CarouselPlugin
20 | orientation?: "horizontal" | "vertical"
21 | setApi?: (api: CarouselApi) => void
22 | }
23 |
24 | type CarouselContextProps = {
25 | carouselRef: ReturnType[0]
26 | api: ReturnType[1]
27 | scrollPrev: () => void
28 | scrollNext: () => void
29 | canScrollPrev: boolean
30 | canScrollNext: boolean
31 | } & CarouselProps
32 |
33 | const CarouselContext = React.createContext(null)
34 |
35 | function useCarousel() {
36 | const context = React.useContext(CarouselContext)
37 |
38 | if (!context) {
39 | throw new Error("useCarousel must be used within a ")
40 | }
41 |
42 | return context
43 | }
44 |
45 | const Carousel = React.forwardRef<
46 | HTMLDivElement,
47 | React.HTMLAttributes & CarouselProps
48 | >(
49 | (
50 | {
51 | orientation = "horizontal",
52 | opts,
53 | setApi,
54 | plugins,
55 | className,
56 | children,
57 | ...props
58 | },
59 | ref
60 | ) => {
61 | const [carouselRef, api] = useEmblaCarousel(
62 | {
63 | ...opts,
64 | axis: orientation === "horizontal" ? "x" : "y",
65 | },
66 | plugins
67 | )
68 | const [canScrollPrev, setCanScrollPrev] = React.useState(false)
69 | const [canScrollNext, setCanScrollNext] = React.useState(false)
70 |
71 | const onSelect = React.useCallback((api: CarouselApi) => {
72 | if (!api) {
73 | return
74 | }
75 |
76 | setCanScrollPrev(api.canScrollPrev())
77 | setCanScrollNext(api.canScrollNext())
78 | }, [])
79 |
80 | const scrollPrev = React.useCallback(() => {
81 | api?.scrollPrev()
82 | }, [api])
83 |
84 | const scrollNext = React.useCallback(() => {
85 | api?.scrollNext()
86 | }, [api])
87 |
88 | const handleKeyDown = React.useCallback(
89 | (event: React.KeyboardEvent) => {
90 | if (event.key === "ArrowLeft") {
91 | event.preventDefault()
92 | scrollPrev()
93 | } else if (event.key === "ArrowRight") {
94 | event.preventDefault()
95 | scrollNext()
96 | }
97 | },
98 | [scrollPrev, scrollNext]
99 | )
100 |
101 | React.useEffect(() => {
102 | if (!api || !setApi) {
103 | return
104 | }
105 |
106 | setApi(api)
107 | }, [api, setApi])
108 |
109 | React.useEffect(() => {
110 | if (!api) {
111 | return
112 | }
113 |
114 | onSelect(api)
115 | api.on("reInit", onSelect)
116 | api.on("select", onSelect)
117 |
118 | return () => {
119 | api?.off("select", onSelect)
120 | }
121 | }, [api, onSelect])
122 |
123 | return (
124 |
137 |
145 | {children}
146 |
147 |
148 | )
149 | }
150 | )
151 | Carousel.displayName = "Carousel"
152 |
153 | const CarouselContent = React.forwardRef<
154 | HTMLDivElement,
155 | React.HTMLAttributes
156 | >(({ className, ...props }, ref) => {
157 | const { carouselRef, orientation } = useCarousel()
158 |
159 | return (
160 |
171 | )
172 | })
173 | CarouselContent.displayName = "CarouselContent"
174 |
175 | const CarouselItem = React.forwardRef<
176 | HTMLDivElement,
177 | React.HTMLAttributes
178 | >(({ className, ...props }, ref) => {
179 | const { orientation } = useCarousel()
180 |
181 | return (
182 |
193 | )
194 | })
195 | CarouselItem.displayName = "CarouselItem"
196 |
197 | const CarouselPrevious = React.forwardRef<
198 | HTMLButtonElement,
199 | React.ComponentProps
200 | >(({ className, variant = "outline", size = "icon", ...props }, ref) => {
201 | const { orientation, scrollPrev, canScrollPrev } = useCarousel()
202 |
203 | return (
204 |
222 | )
223 | })
224 | CarouselPrevious.displayName = "CarouselPrevious"
225 |
226 | const CarouselNext = React.forwardRef<
227 | HTMLButtonElement,
228 | React.ComponentProps
229 | >(({ className, variant = "outline", size = "icon", ...props }, ref) => {
230 | const { orientation, scrollNext, canScrollNext } = useCarousel()
231 |
232 | return (
233 |
251 | )
252 | })
253 | CarouselNext.displayName = "CarouselNext"
254 |
255 | export {
256 | type CarouselApi,
257 | Carousel,
258 | CarouselContent,
259 | CarouselItem,
260 | CarouselPrevious,
261 | CarouselNext,
262 | }
263 |
--------------------------------------------------------------------------------
/components/ui/checkbox.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
5 | import { Check } from "lucide-react"
6 |
7 | import { cn } from "@/lib/utils"
8 |
9 | const Checkbox = React.forwardRef<
10 | React.ElementRef,
11 | React.ComponentPropsWithoutRef
12 | >(({ className, ...props }, ref) => (
13 |
21 |
24 |
25 |
26 |
27 | ))
28 | Checkbox.displayName = CheckboxPrimitive.Root.displayName
29 |
30 | export { Checkbox }
31 |
--------------------------------------------------------------------------------
/components/ui/code-block.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { cn } from "@/lib/utils"
4 | import type React from "react"
5 | import { useEffect, useState } from "react"
6 | import { codeToHtml } from "shiki"
7 |
8 | export type CodeBlockProps = {
9 | children?: React.ReactNode
10 | className?: string
11 | } & React.HTMLProps
12 |
13 | function CodeBlock({ children, className, ...props }: CodeBlockProps) {
14 | return (
15 |
23 | {children}
24 |
25 | )
26 | }
27 |
28 | export type CodeBlockCodeProps = {
29 | code: string
30 | language?: string
31 | theme?: string
32 | className?: string
33 | } & React.HTMLProps
34 |
35 | function CodeBlockCode({ code, language = "tsx", theme = "github-light", className, ...props }: CodeBlockCodeProps) {
36 | const [highlightedHtml, setHighlightedHtml] = useState(null)
37 |
38 | useEffect(() => {
39 | async function highlight() {
40 | if (!code) {
41 | setHighlightedHtml("
")
42 | return
43 | }
44 |
45 | const html = await codeToHtml(code, { lang: language, theme })
46 | setHighlightedHtml(html)
47 | }
48 | highlight()
49 | }, [code, language, theme])
50 |
51 | const classNames = cn("w-full overflow-x-auto text-[13px] [&>pre]:px-4 [&>pre]:py-4", className)
52 |
53 | // SSR fallback: render plain code if not hydrated yet
54 | return highlightedHtml ? (
55 |
56 | ) : (
57 |
58 |
59 | {code}
60 |
61 |
62 | )
63 | }
64 |
65 | export type CodeBlockGroupProps = React.HTMLAttributes
66 |
67 | function CodeBlockGroup({ children, className, ...props }: CodeBlockGroupProps) {
68 | return (
69 |
70 | {children}
71 |
72 | )
73 | }
74 |
75 | export { CodeBlockGroup, CodeBlockCode, CodeBlock }
76 |
--------------------------------------------------------------------------------
/components/ui/code-snippet.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { useState, useRef } from "react"
4 | import { Check, Copy } from 'lucide-react'
5 | import { cn } from "@/lib/utils"
6 | import { Button } from "@/components/ui/button"
7 | import { useToast } from "@/hooks/use-toast"
8 | import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"
9 | import { Badge } from "@/components/ui/badge"
10 |
11 | interface CodeSnippetProps {
12 | code: string
13 | language?: string
14 | showLineNumbers?: boolean
15 | className?: string
16 | }
17 |
18 | export function CodeSnippet({ code, language = "bash", showLineNumbers = false, className }: CodeSnippetProps) {
19 | const [copied, setCopied] = useState(false)
20 | const { toast } = useToast()
21 | const codeRef = useRef(null)
22 |
23 | const copyToClipboard = async () => {
24 | if (!navigator.clipboard) {
25 | toast({
26 | title: "Copy failed",
27 | description: "Your browser doesn't support clipboard access",
28 | variant: "destructive",
29 | })
30 | return
31 | }
32 |
33 | try {
34 | await navigator.clipboard.writeText(code)
35 | setCopied(true)
36 | toast({
37 | title: "Copied to clipboard",
38 | description: "Code snippet has been copied to your clipboard",
39 | duration: 2000,
40 | })
41 | setTimeout(() => setCopied(false), 2000)
42 | } catch (err) {
43 | toast({
44 | title: "Copy failed",
45 | description: "Failed to copy code to clipboard",
46 | variant: "destructive",
47 | })
48 | }
49 | }
50 |
51 | return (
52 |
53 |
54 |
55 | {language}
56 |
57 |
58 |
59 |
60 |
68 |
69 |
70 | {copied ? "Copied!" : "Copy code"}
71 |
72 |
73 |
74 |
75 |
83 | {code}
84 |
85 |
86 | )
87 | }
88 |
--------------------------------------------------------------------------------
/components/ui/collapsible.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as CollapsiblePrimitive from "@radix-ui/react-collapsible"
4 |
5 | const Collapsible = CollapsiblePrimitive.Root
6 |
7 | const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger
8 |
9 | const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent
10 |
11 | export { Collapsible, CollapsibleTrigger, CollapsibleContent }
12 |
--------------------------------------------------------------------------------
/components/ui/command.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import { type DialogProps } from "@radix-ui/react-dialog"
5 | import { Command as CommandPrimitive } from "cmdk"
6 | import { Search } from "lucide-react"
7 |
8 | import { cn } from "@/lib/utils"
9 | import { Dialog, DialogContent } from "@/components/ui/dialog"
10 |
11 | const Command = React.forwardRef<
12 | React.ElementRef,
13 | React.ComponentPropsWithoutRef
14 | >(({ className, ...props }, ref) => (
15 |
23 | ))
24 | Command.displayName = CommandPrimitive.displayName
25 |
26 | const CommandDialog = ({ children, ...props }: DialogProps) => {
27 | return (
28 |
35 | )
36 | }
37 |
38 | const CommandInput = React.forwardRef<
39 | React.ElementRef,
40 | React.ComponentPropsWithoutRef
41 | >(({ className, ...props }, ref) => (
42 |
43 |
44 |
52 |
53 | ))
54 |
55 | CommandInput.displayName = CommandPrimitive.Input.displayName
56 |
57 | const CommandList = React.forwardRef<
58 | React.ElementRef,
59 | React.ComponentPropsWithoutRef
60 | >(({ className, ...props }, ref) => (
61 |
66 | ))
67 |
68 | CommandList.displayName = CommandPrimitive.List.displayName
69 |
70 | const CommandEmpty = React.forwardRef<
71 | React.ElementRef,
72 | React.ComponentPropsWithoutRef
73 | >((props, ref) => (
74 |
79 | ))
80 |
81 | CommandEmpty.displayName = CommandPrimitive.Empty.displayName
82 |
83 | const CommandGroup = React.forwardRef<
84 | React.ElementRef,
85 | React.ComponentPropsWithoutRef
86 | >(({ className, ...props }, ref) => (
87 |
95 | ))
96 |
97 | CommandGroup.displayName = CommandPrimitive.Group.displayName
98 |
99 | const CommandSeparator = React.forwardRef<
100 | React.ElementRef,
101 | React.ComponentPropsWithoutRef
102 | >(({ className, ...props }, ref) => (
103 |
108 | ))
109 | CommandSeparator.displayName = CommandPrimitive.Separator.displayName
110 |
111 | const CommandItem = React.forwardRef<
112 | React.ElementRef,
113 | React.ComponentPropsWithoutRef
114 | >(({ className, ...props }, ref) => (
115 |
123 | ))
124 |
125 | CommandItem.displayName = CommandPrimitive.Item.displayName
126 |
127 | const CommandShortcut = ({
128 | className,
129 | ...props
130 | }: React.HTMLAttributes) => {
131 | return (
132 |
139 | )
140 | }
141 | CommandShortcut.displayName = "CommandShortcut"
142 |
143 | export {
144 | Command,
145 | CommandDialog,
146 | CommandInput,
147 | CommandList,
148 | CommandEmpty,
149 | CommandGroup,
150 | CommandItem,
151 | CommandShortcut,
152 | CommandSeparator,
153 | }
154 |
--------------------------------------------------------------------------------
/components/ui/context-menu.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as ContextMenuPrimitive from "@radix-ui/react-context-menu"
5 | import { Check, ChevronRight, Circle } from "lucide-react"
6 |
7 | import { cn } from "@/lib/utils"
8 |
9 | const ContextMenu = ContextMenuPrimitive.Root
10 |
11 | const ContextMenuTrigger = ContextMenuPrimitive.Trigger
12 |
13 | const ContextMenuGroup = ContextMenuPrimitive.Group
14 |
15 | const ContextMenuPortal = ContextMenuPrimitive.Portal
16 |
17 | const ContextMenuSub = ContextMenuPrimitive.Sub
18 |
19 | const ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup
20 |
21 | const ContextMenuSubTrigger = React.forwardRef<
22 | React.ElementRef,
23 | React.ComponentPropsWithoutRef & {
24 | inset?: boolean
25 | }
26 | >(({ className, inset, children, ...props }, ref) => (
27 |
36 | {children}
37 |
38 |
39 | ))
40 | ContextMenuSubTrigger.displayName = ContextMenuPrimitive.SubTrigger.displayName
41 |
42 | const ContextMenuSubContent = React.forwardRef<
43 | React.ElementRef,
44 | React.ComponentPropsWithoutRef
45 | >(({ className, ...props }, ref) => (
46 |
54 | ))
55 | ContextMenuSubContent.displayName = ContextMenuPrimitive.SubContent.displayName
56 |
57 | const ContextMenuContent = React.forwardRef<
58 | React.ElementRef,
59 | React.ComponentPropsWithoutRef
60 | >(({ className, ...props }, ref) => (
61 |
62 |
70 |
71 | ))
72 | ContextMenuContent.displayName = ContextMenuPrimitive.Content.displayName
73 |
74 | const ContextMenuItem = React.forwardRef<
75 | React.ElementRef,
76 | React.ComponentPropsWithoutRef & {
77 | inset?: boolean
78 | }
79 | >(({ className, inset, ...props }, ref) => (
80 |
89 | ))
90 | ContextMenuItem.displayName = ContextMenuPrimitive.Item.displayName
91 |
92 | const ContextMenuCheckboxItem = React.forwardRef<
93 | React.ElementRef,
94 | React.ComponentPropsWithoutRef
95 | >(({ className, children, checked, ...props }, ref) => (
96 |
105 |
106 |
107 |
108 |
109 |
110 | {children}
111 |
112 | ))
113 | ContextMenuCheckboxItem.displayName = ContextMenuPrimitive.CheckboxItem.displayName
114 |
115 | const ContextMenuRadioItem = React.forwardRef<
116 | React.ElementRef,
117 | React.ComponentPropsWithoutRef
118 | >(({ className, children, ...props }, ref) => (
119 |
127 |
128 |
129 |
130 |
131 |
132 | {children}
133 |
134 | ))
135 | ContextMenuRadioItem.displayName = ContextMenuPrimitive.RadioItem.displayName
136 |
137 | const ContextMenuLabel = React.forwardRef<
138 | React.ElementRef,
139 | React.ComponentPropsWithoutRef & {
140 | inset?: boolean
141 | }
142 | >(({ className, inset, ...props }, ref) => (
143 |
148 | ))
149 | ContextMenuLabel.displayName = ContextMenuPrimitive.Label.displayName
150 |
151 | const ContextMenuSeparator = React.forwardRef<
152 | React.ElementRef,
153 | React.ComponentPropsWithoutRef
154 | >(({ className, ...props }, ref) => (
155 |
156 | ))
157 | ContextMenuSeparator.displayName = ContextMenuPrimitive.Separator.displayName
158 |
159 | const ContextMenuShortcut = ({ className, ...props }: React.HTMLAttributes) => {
160 | return
161 | }
162 | ContextMenuShortcut.displayName = "ContextMenuShortcut"
163 |
164 | export {
165 | ContextMenu,
166 | ContextMenuTrigger,
167 | ContextMenuContent,
168 | ContextMenuItem,
169 | ContextMenuCheckboxItem,
170 | ContextMenuRadioItem,
171 | ContextMenuLabel,
172 | ContextMenuSeparator,
173 | ContextMenuShortcut,
174 | ContextMenuGroup,
175 | ContextMenuPortal,
176 | ContextMenuSub,
177 | ContextMenuSubContent,
178 | ContextMenuSubTrigger,
179 | ContextMenuRadioGroup,
180 | }
181 |
--------------------------------------------------------------------------------
/components/ui/dialog.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as DialogPrimitive from "@radix-ui/react-dialog"
5 | import { X } from "lucide-react"
6 |
7 | import { cn } from "@/lib/utils"
8 |
9 | const Dialog = DialogPrimitive.Root
10 |
11 | const DialogTrigger = DialogPrimitive.Trigger
12 |
13 | const DialogPortal = DialogPrimitive.Portal
14 |
15 | const DialogClose = DialogPrimitive.Close
16 |
17 | const DialogOverlay = React.forwardRef<
18 | React.ElementRef,
19 | React.ComponentPropsWithoutRef
20 | >(({ className, ...props }, ref) => (
21 |
29 | ))
30 | DialogOverlay.displayName = DialogPrimitive.Overlay.displayName
31 |
32 | const DialogContent = React.forwardRef<
33 | React.ElementRef,
34 | React.ComponentPropsWithoutRef
35 | >(({ className, children, ...props }, ref) => (
36 |
37 |
38 |
46 | {children}
47 |
48 |
49 | Close
50 |
51 |
52 |
53 | ))
54 | DialogContent.displayName = DialogPrimitive.Content.displayName
55 |
56 | const DialogHeader = ({ className, ...props }: React.HTMLAttributes) => (
57 |
58 | )
59 | DialogHeader.displayName = "DialogHeader"
60 |
61 | const DialogFooter = ({ className, ...props }: React.HTMLAttributes) => (
62 |
63 | )
64 | DialogFooter.displayName = "DialogFooter"
65 |
66 | const DialogTitle = React.forwardRef<
67 | React.ElementRef,
68 | React.ComponentPropsWithoutRef
69 | >(({ className, ...props }, ref) => (
70 |
75 | ))
76 | DialogTitle.displayName = DialogPrimitive.Title.displayName
77 |
78 | const DialogDescription = React.forwardRef<
79 | React.ElementRef,
80 | React.ComponentPropsWithoutRef
81 | >(({ className, ...props }, ref) => (
82 |
83 | ))
84 | DialogDescription.displayName = DialogPrimitive.Description.displayName
85 |
86 | export {
87 | Dialog,
88 | DialogPortal,
89 | DialogOverlay,
90 | DialogClose,
91 | DialogTrigger,
92 | DialogContent,
93 | DialogHeader,
94 | DialogFooter,
95 | DialogTitle,
96 | DialogDescription,
97 | }
98 |
--------------------------------------------------------------------------------
/components/ui/drawer.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import { Drawer as DrawerPrimitive } from "vaul"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const Drawer = ({ shouldScaleBackground = true, ...props }: React.ComponentProps) => (
9 |
10 | )
11 | Drawer.displayName = "Drawer"
12 |
13 | const DrawerTrigger = DrawerPrimitive.Trigger
14 |
15 | const DrawerPortal = DrawerPrimitive.Portal
16 |
17 | const DrawerClose = DrawerPrimitive.Close
18 |
19 | const DrawerOverlay = React.forwardRef<
20 | React.ElementRef,
21 | React.ComponentPropsWithoutRef
22 | >(({ className, ...props }, ref) => (
23 |
24 | ))
25 | DrawerOverlay.displayName = DrawerPrimitive.Overlay.displayName
26 |
27 | const DrawerContent = React.forwardRef<
28 | React.ElementRef,
29 | React.ComponentPropsWithoutRef
30 | >(({ className, children, ...props }, ref) => (
31 |
32 |
33 |
41 |
42 | {children}
43 |
44 |
45 | ))
46 | DrawerContent.displayName = "DrawerContent"
47 |
48 | const DrawerHeader = ({ className, ...props }: React.HTMLAttributes) => (
49 |
50 | )
51 | DrawerHeader.displayName = "DrawerHeader"
52 |
53 | const DrawerFooter = ({ className, ...props }: React.HTMLAttributes) => (
54 |
55 | )
56 | DrawerFooter.displayName = "DrawerFooter"
57 |
58 | const DrawerTitle = React.forwardRef<
59 | React.ElementRef,
60 | React.ComponentPropsWithoutRef
61 | >(({ className, ...props }, ref) => (
62 |
67 | ))
68 | DrawerTitle.displayName = DrawerPrimitive.Title.displayName
69 |
70 | const DrawerDescription = React.forwardRef<
71 | React.ElementRef,
72 | React.ComponentPropsWithoutRef
73 | >(({ className, ...props }, ref) => (
74 |
75 | ))
76 | DrawerDescription.displayName = DrawerPrimitive.Description.displayName
77 |
78 | export {
79 | Drawer,
80 | DrawerPortal,
81 | DrawerOverlay,
82 | DrawerTrigger,
83 | DrawerClose,
84 | DrawerContent,
85 | DrawerHeader,
86 | DrawerFooter,
87 | DrawerTitle,
88 | DrawerDescription,
89 | }
90 |
--------------------------------------------------------------------------------
/components/ui/form.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import type * as LabelPrimitive from "@radix-ui/react-label"
5 | import { Slot } from "@radix-ui/react-slot"
6 | import {
7 | Controller,
8 | type ControllerProps,
9 | type FieldPath,
10 | type FieldValues,
11 | FormProvider,
12 | useFormContext,
13 | } from "react-hook-form"
14 |
15 | import { cn } from "@/lib/utils"
16 | import { Label } from "@/components/ui/label"
17 |
18 | const Form = FormProvider
19 |
20 | type FormFieldContextValue<
21 | TFieldValues extends FieldValues = FieldValues,
22 | TName extends FieldPath = FieldPath,
23 | > = {
24 | name: TName
25 | }
26 |
27 | const FormFieldContext = React.createContext({} as FormFieldContextValue)
28 |
29 | const FormField = <
30 | TFieldValues extends FieldValues = FieldValues,
31 | TName extends FieldPath = FieldPath,
32 | >({
33 | ...props
34 | }: ControllerProps) => {
35 | return (
36 |
37 |
38 |
39 | )
40 | }
41 |
42 | const useFormField = () => {
43 | const fieldContext = React.useContext(FormFieldContext)
44 | const itemContext = React.useContext(FormItemContext)
45 | const { getFieldState, formState } = useFormContext()
46 |
47 | const fieldState = getFieldState(fieldContext.name, formState)
48 |
49 | if (!fieldContext) {
50 | throw new Error("useFormField should be used within ")
51 | }
52 |
53 | const { id } = itemContext
54 |
55 | return {
56 | id,
57 | name: fieldContext.name,
58 | formItemId: `${id}-form-item`,
59 | formDescriptionId: `${id}-form-item-description`,
60 | formMessageId: `${id}-form-item-message`,
61 | ...fieldState,
62 | }
63 | }
64 |
65 | type FormItemContextValue = {
66 | id: string
67 | }
68 |
69 | const FormItemContext = React.createContext({} as FormItemContextValue)
70 |
71 | const FormItem = React.forwardRef>(
72 | ({ className, ...props }, ref) => {
73 | const id = React.useId()
74 |
75 | return (
76 |
77 |
78 |
79 | )
80 | },
81 | )
82 | FormItem.displayName = "FormItem"
83 |
84 | const FormLabel = React.forwardRef<
85 | React.ElementRef,
86 | React.ComponentPropsWithoutRef
87 | >(({ className, ...props }, ref) => {
88 | const { error, formItemId } = useFormField()
89 |
90 | return
91 | })
92 | FormLabel.displayName = "FormLabel"
93 |
94 | const FormControl = React.forwardRef, React.ComponentPropsWithoutRef>(
95 | ({ ...props }, ref) => {
96 | const { error, formItemId, formDescriptionId, formMessageId } = useFormField()
97 |
98 | return (
99 |
106 | )
107 | },
108 | )
109 | FormControl.displayName = "FormControl"
110 |
111 | const FormDescription = React.forwardRef>(
112 | ({ className, ...props }, ref) => {
113 | const { formDescriptionId } = useFormField()
114 |
115 | return
116 | },
117 | )
118 | FormDescription.displayName = "FormDescription"
119 |
120 | const FormMessage = React.forwardRef>(
121 | ({ className, children, ...props }, ref) => {
122 | const { error, formMessageId } = useFormField()
123 | const body = error ? String(error?.message) : children
124 |
125 | if (!body) {
126 | return null
127 | }
128 |
129 | return (
130 |
131 | {body}
132 |
133 | )
134 | },
135 | )
136 | FormMessage.displayName = "FormMessage"
137 |
138 | export { useFormField, Form, FormItem, FormLabel, FormControl, FormDescription, FormMessage, FormField }
139 |
--------------------------------------------------------------------------------
/components/ui/hover-card.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as HoverCardPrimitive from "@radix-ui/react-hover-card"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const HoverCard = HoverCardPrimitive.Root
9 |
10 | const HoverCardTrigger = HoverCardPrimitive.Trigger
11 |
12 | const HoverCardContent = React.forwardRef<
13 | React.ElementRef,
14 | React.ComponentPropsWithoutRef
15 | >(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
16 |
26 | ))
27 | HoverCardContent.displayName = HoverCardPrimitive.Content.displayName
28 |
29 | export { HoverCard, HoverCardTrigger, HoverCardContent }
30 |
--------------------------------------------------------------------------------
/components/ui/input-otp.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import { OTPInput, OTPInputContext } from "input-otp"
5 | import { Dot } from "lucide-react"
6 |
7 | import { cn } from "@/lib/utils"
8 |
9 | const InputOTP = React.forwardRef<
10 | React.ElementRef,
11 | React.ComponentPropsWithoutRef
12 | >(({ className, containerClassName, ...props }, ref) => (
13 |
22 | ))
23 | InputOTP.displayName = "InputOTP"
24 |
25 | const InputOTPGroup = React.forwardRef<
26 | React.ElementRef<"div">,
27 | React.ComponentPropsWithoutRef<"div">
28 | >(({ className, ...props }, ref) => (
29 |
30 | ))
31 | InputOTPGroup.displayName = "InputOTPGroup"
32 |
33 | const InputOTPSlot = React.forwardRef<
34 | React.ElementRef<"div">,
35 | React.ComponentPropsWithoutRef<"div"> & { index: number }
36 | >(({ index, className, ...props }, ref) => {
37 | const inputOTPContext = React.useContext(OTPInputContext)
38 | const { char, hasFakeCaret, isActive } = inputOTPContext.slots[index]
39 |
40 | return (
41 |
50 | {char}
51 | {hasFakeCaret && (
52 |
55 | )}
56 |
57 | )
58 | })
59 | InputOTPSlot.displayName = "InputOTPSlot"
60 |
61 | const InputOTPSeparator = React.forwardRef<
62 | React.ElementRef<"div">,
63 | React.ComponentPropsWithoutRef<"div">
64 | >(({ ...props }, ref) => (
65 |
66 |
67 |
68 | ))
69 | InputOTPSeparator.displayName = "InputOTPSeparator"
70 |
71 | export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator }
72 |
--------------------------------------------------------------------------------
/components/ui/input-pulse-border.tsx:
--------------------------------------------------------------------------------
1 | import { Input } from "@/components/ui/input"
2 | import type { InputProps } from "@/components/ui/input"
3 |
4 | const InputPulseBorder = (props: InputProps) => {
5 | return (
6 |
15 | )
16 | }
17 |
18 | export default InputPulseBorder
19 |
--------------------------------------------------------------------------------
/components/ui/input.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 |
3 | import { cn } from "@/lib/utils"
4 |
5 | export interface InputProps extends React.InputHTMLAttributes {}
6 |
7 | const Input = React.forwardRef(({ className, type, ...props }, ref) => {
8 | return (
9 |
18 | )
19 | })
20 | Input.displayName = "Input"
21 |
22 | export { Input }
23 |
--------------------------------------------------------------------------------
/components/ui/label.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as LabelPrimitive from "@radix-ui/react-label"
5 | import { cva, type VariantProps } from "class-variance-authority"
6 |
7 | import { cn } from "@/lib/utils"
8 |
9 | const labelVariants = cva("text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70")
10 |
11 | const Label = React.forwardRef<
12 | React.ElementRef,
13 | React.ComponentPropsWithoutRef & VariantProps
14 | >(({ className, ...props }, ref) => (
15 |
16 | ))
17 | Label.displayName = LabelPrimitive.Root.displayName
18 |
19 | export { Label }
20 |
--------------------------------------------------------------------------------
/components/ui/markdown.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { cn } from "@/lib/utils"
4 | import { marked } from "marked"
5 | import { memo, useId, useMemo } from "react"
6 | import ReactMarkdown, { type Components } from "react-markdown"
7 | import remarkGfm from "remark-gfm"
8 | import { CodeBlock, CodeBlockCode } from "./code-block"
9 |
10 | export type MarkdownProps = {
11 | children: string
12 | id?: string
13 | className?: string
14 | components?: Partial
15 | }
16 |
17 | function parseMarkdownIntoBlocks(markdown: string): string[] {
18 | const tokens = marked.lexer(markdown)
19 | return tokens.map((token) => token.raw)
20 | }
21 |
22 | function extractLanguage(className?: string): string {
23 | if (!className) return "plaintext"
24 | const match = className.match(/language-(\w+)/)
25 | return match ? match[1] : "plaintext"
26 | }
27 |
28 | const INITIAL_COMPONENTS: Partial = {
29 | code: function CodeComponent({ className, children, ...props }) {
30 | const isInline =
31 | !props.node?.position?.start.line || props.node?.position?.start.line === props.node?.position?.end.line
32 |
33 | if (isInline) {
34 | return (
35 |
36 | {children}
37 |
38 | )
39 | }
40 |
41 | const language = extractLanguage(className)
42 |
43 | return (
44 |
45 |
46 |
47 | )
48 | },
49 | pre: function PreComponent({ children }) {
50 | return <>{children}>
51 | },
52 | }
53 |
54 | const MemoizedMarkdownBlock = memo(
55 | function MarkdownBlock({
56 | content,
57 | components = INITIAL_COMPONENTS,
58 | }: {
59 | content: string
60 | components?: Partial
61 | }) {
62 | return (
63 |
64 | {content}
65 |
66 | )
67 | },
68 | function propsAreEqual(prevProps, nextProps) {
69 | return prevProps.content === nextProps.content
70 | },
71 | )
72 |
73 | MemoizedMarkdownBlock.displayName = "MemoizedMarkdownBlock"
74 |
75 | function MarkdownComponent({ children, id, className, components = INITIAL_COMPONENTS }: MarkdownProps) {
76 | const generatedId = useId()
77 | const blockId = id ?? generatedId
78 | const blocks = useMemo(() => parseMarkdownIntoBlocks(children), [children])
79 |
80 | return (
81 |
82 | {blocks.map((block, index) => (
83 |
84 | ))}
85 |
86 | )
87 | }
88 |
89 | const Markdown = memo(MarkdownComponent)
90 | Markdown.displayName = "Markdown"
91 |
92 | export { Markdown }
93 |
--------------------------------------------------------------------------------
/components/ui/message.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import type * as React from "react"
4 | import { cn } from "@/lib/utils"
5 | import ReactMarkdown from "react-markdown"
6 | import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"
7 |
8 | interface MessageProps extends React.HTMLAttributes {
9 | align?: "start" | "end"
10 | avatar?: React.ReactNode
11 | children: React.ReactNode
12 | }
13 |
14 | export function Message({ align = "start", avatar, children, className, ...props }: MessageProps) {
15 | return (
16 |
17 |
18 | {avatar && align === "start" &&
{avatar}
}
19 | {children}
20 | {avatar && align === "end" &&
{avatar}
}
21 |
22 |
23 | )
24 | }
25 |
26 | interface MessageAvatarProps extends React.HTMLAttributes {
27 | src?: string
28 | fallback: string
29 | alt?: string
30 | }
31 |
32 | export function MessageAvatar({ src, fallback, alt, className, ...props }: MessageAvatarProps) {
33 | return (
34 |
41 | {src ? (
42 |

47 | ) : (
48 |
{fallback[0]}
49 | )}
50 |
51 | )
52 | }
53 |
54 | interface MessageContentProps extends React.HTMLAttributes {
55 | markdown?: boolean
56 | }
57 |
58 | export function MessageContent({ markdown = false, children, className, ...props }: MessageContentProps) {
59 | return (
60 |
61 | {markdown ? (
62 |
63 | {children as string}
64 |
65 | ) : (
66 |
{children}
67 | )}
68 |
69 | )
70 | }
71 |
72 | interface MessageActionsProps extends React.HTMLAttributes {}
73 |
74 | export function MessageActions({ children, className, ...props }: MessageActionsProps) {
75 | return (
76 |
77 | {children}
78 |
79 | )
80 | }
81 |
82 | interface MessageActionProps extends React.HTMLAttributes {
83 | tooltip?: string
84 | }
85 |
86 | export function MessageAction({ tooltip, children, className, ...props }: MessageActionProps) {
87 | if (tooltip) {
88 | return (
89 |
90 |
91 |
92 |
93 | {children}
94 |
95 |
96 |
97 | {tooltip}
98 |
99 |
100 |
101 | )
102 | }
103 |
104 | return (
105 |
106 | {children}
107 |
108 | )
109 | }
110 |
--------------------------------------------------------------------------------
/components/ui/mobile-menu.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { useState } from "react"
4 | import Link from "next/link"
5 | import { usePathname } from "next/navigation"
6 | import { Menu, X } from "lucide-react"
7 | import { Button } from "@/components/ui/button"
8 | import { cn } from "@/lib/utils"
9 | import { motion, AnimatePresence } from "framer-motion"
10 | import { ThemeToggle } from "./theme-toggle"
11 | import { UserAuthButton } from "@/components/auth/user-auth-button"
12 |
13 | interface Route {
14 | name: string
15 | path: string
16 | }
17 |
18 | interface MobileMenuProps {
19 | routes: Route[]
20 | }
21 |
22 | export function MobileMenu({ routes }: MobileMenuProps) {
23 | const [isOpen, setIsOpen] = useState(false)
24 | const pathname = usePathname()
25 |
26 | const toggleMenu = () => setIsOpen(!isOpen)
27 |
28 | const menuVariants = {
29 | closed: {
30 | opacity: 0,
31 | y: "-100%",
32 | transition: {
33 | duration: 0.3,
34 | staggerChildren: 0.05,
35 | staggerDirection: -1,
36 | when: "afterChildren",
37 | },
38 | },
39 | open: {
40 | opacity: 1,
41 | y: 0,
42 | transition: {
43 | duration: 0.3,
44 | staggerChildren: 0.05,
45 | staggerDirection: 1,
46 | when: "beforeChildren",
47 | },
48 | },
49 | }
50 |
51 | const itemVariants = {
52 | closed: { opacity: 0, y: -10 },
53 | open: { opacity: 1, y: 0 },
54 | }
55 |
56 | return (
57 |
58 |
61 |
62 |
63 | {isOpen && (
64 |
71 |
72 | {routes.map((route) => (
73 |
74 |
82 | {route.name}
83 |
84 |
85 | ))}
86 |
87 |
88 |
89 |
90 |
91 |
92 | )}
93 |
94 |
95 | )
96 | }
97 |
--------------------------------------------------------------------------------
/components/ui/navbar.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { useState, useRef } from "react"
4 | import Link from "next/link"
5 | import { usePathname } from "next/navigation"
6 | import { Menu, X } from "lucide-react"
7 | import { Button } from "@/components/ui/button"
8 | import { UserAuthButton } from "@/components/auth/user-auth-button"
9 | import { cn } from "@/lib/utils"
10 | import { motion, useScroll, useMotionValueEvent } from "framer-motion"
11 | import { ThemeToggle } from "@/components/ui/theme-toggle"
12 | import Image from "next/image"
13 |
14 | export function Navbar() {
15 | const [isOpen, setIsOpen] = useState(false)
16 | const [visible, setVisible] = useState(false)
17 | const pathname = usePathname()
18 | const ref = useRef(null)
19 | const { scrollY } = useScroll({
20 | target: ref,
21 | offset: ["start start", "end start"],
22 | })
23 |
24 | useMotionValueEvent(scrollY, "change", (latest) => {
25 | if (latest > 100) {
26 | setVisible(true)
27 | } else {
28 | setVisible(false)
29 | }
30 | })
31 |
32 | const routes = [
33 | { name: "Home", path: "/" },
34 | { name: "AI Chat", path: "/ai-chat" },
35 | { name: "Generate README", path: "/generate-readme" },
36 | { name: "Git Mojis", path: "/git-mojis" },
37 | ]
38 |
39 | return (
40 |
41 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 | {routes.map((route) => (
71 |
79 | {route.name}
80 |
81 | ))}
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 | {/* Mobile navigation */}
91 |
113 |
114 |
115 | GitFriend
116 |
117 |
120 |
121 |
122 | {isOpen && (
123 |
129 | {routes.map((route) => (
130 | setIsOpen(false)}
138 | >
139 | {route.name}
140 |
141 | ))}
142 |
143 |
144 |
145 |
146 |
147 | )}
148 |
149 |
150 | )
151 | }
152 |
--------------------------------------------------------------------------------
/components/ui/navigation-menu.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import * as NavigationMenuPrimitive from "@radix-ui/react-navigation-menu"
3 | import { cva } from "class-variance-authority"
4 | import { ChevronDown } from "lucide-react"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const NavigationMenu = React.forwardRef<
9 | React.ElementRef,
10 | React.ComponentPropsWithoutRef
11 | >(({ className, children, ...props }, ref) => (
12 |
17 | {children}
18 |
19 |
20 | ))
21 | NavigationMenu.displayName = NavigationMenuPrimitive.Root.displayName
22 |
23 | const NavigationMenuList = React.forwardRef<
24 | React.ElementRef,
25 | React.ComponentPropsWithoutRef
26 | >(({ className, ...props }, ref) => (
27 |
32 | ))
33 | NavigationMenuList.displayName = NavigationMenuPrimitive.List.displayName
34 |
35 | const NavigationMenuItem = NavigationMenuPrimitive.Item
36 |
37 | const navigationMenuTriggerStyle = cva(
38 | "group inline-flex h-10 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-accent/50 data-[state=open]:bg-accent/50",
39 | )
40 |
41 | const NavigationMenuTrigger = React.forwardRef<
42 | React.ElementRef,
43 | React.ComponentPropsWithoutRef
44 | >(({ className, children, ...props }, ref) => (
45 |
50 | {children}{" "}
51 |
55 |
56 | ))
57 | NavigationMenuTrigger.displayName = NavigationMenuPrimitive.Trigger.displayName
58 |
59 | const NavigationMenuContent = React.forwardRef<
60 | React.ElementRef,
61 | React.ComponentPropsWithoutRef
62 | >(({ className, ...props }, ref) => (
63 |
71 | ))
72 | NavigationMenuContent.displayName = NavigationMenuPrimitive.Content.displayName
73 |
74 | const NavigationMenuLink = NavigationMenuPrimitive.Link
75 |
76 | const NavigationMenuViewport = React.forwardRef<
77 | React.ElementRef,
78 | React.ComponentPropsWithoutRef
79 | >(({ className, ...props }, ref) => (
80 |
81 |
89 |
90 | ))
91 | NavigationMenuViewport.displayName = NavigationMenuPrimitive.Viewport.displayName
92 |
93 | const NavigationMenuIndicator = React.forwardRef<
94 | React.ElementRef,
95 | React.ComponentPropsWithoutRef
96 | >(({ className, ...props }, ref) => (
97 |
105 |
106 |
107 | ))
108 | NavigationMenuIndicator.displayName = NavigationMenuPrimitive.Indicator.displayName
109 |
110 | export {
111 | navigationMenuTriggerStyle,
112 | NavigationMenu,
113 | NavigationMenuList,
114 | NavigationMenuItem,
115 | NavigationMenuContent,
116 | NavigationMenuTrigger,
117 | NavigationMenuLink,
118 | NavigationMenuIndicator,
119 | NavigationMenuViewport,
120 | }
121 |
--------------------------------------------------------------------------------
/components/ui/pagination.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import { ChevronLeft, ChevronRight, MoreHorizontal } from "lucide-react"
3 |
4 | import { cn } from "@/lib/utils"
5 | import { ButtonProps, buttonVariants } from "@/components/ui/button"
6 |
7 | const Pagination = ({ className, ...props }: React.ComponentProps<"nav">) => (
8 |
14 | )
15 | Pagination.displayName = "Pagination"
16 |
17 | const PaginationContent = React.forwardRef<
18 | HTMLUListElement,
19 | React.ComponentProps<"ul">
20 | >(({ className, ...props }, ref) => (
21 |
26 | ))
27 | PaginationContent.displayName = "PaginationContent"
28 |
29 | const PaginationItem = React.forwardRef<
30 | HTMLLIElement,
31 | React.ComponentProps<"li">
32 | >(({ className, ...props }, ref) => (
33 |
34 | ))
35 | PaginationItem.displayName = "PaginationItem"
36 |
37 | type PaginationLinkProps = {
38 | isActive?: boolean
39 | } & Pick &
40 | React.ComponentProps<"a">
41 |
42 | const PaginationLink = ({
43 | className,
44 | isActive,
45 | size = "icon",
46 | ...props
47 | }: PaginationLinkProps) => (
48 |
59 | )
60 | PaginationLink.displayName = "PaginationLink"
61 |
62 | const PaginationPrevious = ({
63 | className,
64 | ...props
65 | }: React.ComponentProps) => (
66 |
72 |
73 | Previous
74 |
75 | )
76 | PaginationPrevious.displayName = "PaginationPrevious"
77 |
78 | const PaginationNext = ({
79 | className,
80 | ...props
81 | }: React.ComponentProps) => (
82 |
88 | Next
89 |
90 |
91 | )
92 | PaginationNext.displayName = "PaginationNext"
93 |
94 | const PaginationEllipsis = ({
95 | className,
96 | ...props
97 | }: React.ComponentProps<"span">) => (
98 |
103 |
104 | More pages
105 |
106 | )
107 | PaginationEllipsis.displayName = "PaginationEllipsis"
108 |
109 | export {
110 | Pagination,
111 | PaginationContent,
112 | PaginationEllipsis,
113 | PaginationItem,
114 | PaginationLink,
115 | PaginationNext,
116 | PaginationPrevious,
117 | }
118 |
--------------------------------------------------------------------------------
/components/ui/popover.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as PopoverPrimitive from "@radix-ui/react-popover"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const Popover = PopoverPrimitive.Root
9 |
10 | const PopoverTrigger = PopoverPrimitive.Trigger
11 |
12 | const PopoverContent = React.forwardRef<
13 | React.ElementRef,
14 | React.ComponentPropsWithoutRef
15 | >(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
16 |
17 |
27 |
28 | ))
29 | PopoverContent.displayName = PopoverPrimitive.Content.displayName
30 |
31 | export { Popover, PopoverTrigger, PopoverContent }
32 |
--------------------------------------------------------------------------------
/components/ui/progress.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as ProgressPrimitive from "@radix-ui/react-progress"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const Progress = React.forwardRef<
9 | React.ElementRef,
10 | React.ComponentPropsWithoutRef
11 | >(({ className, value, ...props }, ref) => (
12 |
17 |
21 |
22 | ))
23 | Progress.displayName = ProgressPrimitive.Root.displayName
24 |
25 | export { Progress }
26 |
--------------------------------------------------------------------------------
/components/ui/radio-group.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as RadioGroupPrimitive from "@radix-ui/react-radio-group"
5 | import { Circle } from "lucide-react"
6 |
7 | import { cn } from "@/lib/utils"
8 |
9 | const RadioGroup = React.forwardRef<
10 | React.ElementRef,
11 | React.ComponentPropsWithoutRef
12 | >(({ className, ...props }, ref) => {
13 | return
14 | })
15 | RadioGroup.displayName = RadioGroupPrimitive.Root.displayName
16 |
17 | const RadioGroupItem = React.forwardRef<
18 | React.ElementRef,
19 | React.ComponentPropsWithoutRef
20 | >(({ className, ...props }, ref) => {
21 | return (
22 |
30 |
31 |
32 |
33 |
34 | )
35 | })
36 | RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName
37 |
38 | export { RadioGroup, RadioGroupItem }
39 |
--------------------------------------------------------------------------------
/components/ui/resizable.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { GripVertical } from "lucide-react"
4 | import * as ResizablePrimitive from "react-resizable-panels"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const ResizablePanelGroup = ({
9 | className,
10 | ...props
11 | }: React.ComponentProps) => (
12 |
19 | )
20 |
21 | const ResizablePanel = ResizablePrimitive.Panel
22 |
23 | const ResizableHandle = ({
24 | withHandle,
25 | className,
26 | ...props
27 | }: React.ComponentProps & {
28 | withHandle?: boolean
29 | }) => (
30 | div]:rotate-90",
33 | className
34 | )}
35 | {...props}
36 | >
37 | {withHandle && (
38 |
39 |
40 |
41 | )}
42 |
43 | )
44 |
45 | export { ResizablePanelGroup, ResizablePanel, ResizableHandle }
46 |
--------------------------------------------------------------------------------
/components/ui/response-stream.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import { cva, type VariantProps } from "class-variance-authority"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const responseStreamVariants = cva("relative w-full overflow-hidden rounded-lg border bg-background text-sm", {
9 | variants: {
10 | variant: {
11 | default: "border",
12 | ghost: "border-none shadow-none",
13 | },
14 | },
15 | defaultVariants: {
16 | variant: "default",
17 | },
18 | })
19 |
20 | export interface ResponseStreamProps
21 | extends React.HTMLAttributes,
22 | VariantProps {
23 | isLoading?: boolean
24 | loadingText?: string
25 | emptyText?: string
26 | children?: React.ReactNode
27 | }
28 |
29 | const ResponseStream = React.forwardRef(
30 | (
31 | {
32 | className,
33 | variant,
34 | isLoading = false,
35 | loadingText = "Generating response...",
36 | emptyText = "Response will appear here",
37 | children,
38 | ...props
39 | },
40 | ref,
41 | ) => {
42 | const hasContent = React.Children.count(children) > 0
43 |
44 | return (
45 |
46 | {isLoading ? (
47 |
48 |
49 |
54 |
{loadingText}
55 |
56 |
57 | ) : hasContent ? (
58 |
{children}
59 | ) : (
60 |
63 | )}
64 |
65 | )
66 | },
67 | )
68 | ResponseStream.displayName = "ResponseStream"
69 |
70 | export { ResponseStream }
71 |
--------------------------------------------------------------------------------
/components/ui/scroll-area.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const ScrollArea = React.forwardRef<
9 | React.ElementRef,
10 | React.ComponentPropsWithoutRef
11 | >(({ className, children, ...props }, ref) => (
12 |
13 | {children}
14 |
15 |
16 |
17 | ))
18 | ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName
19 |
20 | const ScrollBar = React.forwardRef<
21 | React.ElementRef,
22 | React.ComponentPropsWithoutRef
23 | >(({ className, orientation = "vertical", ...props }, ref) => (
24 |
35 |
36 |
37 | ))
38 | ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName
39 |
40 | export { ScrollArea, ScrollBar }
41 |
--------------------------------------------------------------------------------
/components/ui/select.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as SelectPrimitive from "@radix-ui/react-select"
5 | import { Check, ChevronDown, ChevronUp } from "lucide-react"
6 |
7 | import { cn } from "@/lib/utils"
8 |
9 | const Select = SelectPrimitive.Root
10 |
11 | const SelectGroup = SelectPrimitive.Group
12 |
13 | const SelectValue = SelectPrimitive.Value
14 |
15 | const SelectTrigger = React.forwardRef<
16 | React.ElementRef,
17 | React.ComponentPropsWithoutRef
18 | >(({ className, children, ...props }, ref) => (
19 | span]:line-clamp-1",
23 | className,
24 | )}
25 | {...props}
26 | >
27 | {children}
28 |
29 |
30 |
31 |
32 | ))
33 | SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
34 |
35 | const SelectScrollUpButton = React.forwardRef<
36 | React.ElementRef,
37 | React.ComponentPropsWithoutRef
38 | >(({ className, ...props }, ref) => (
39 |
44 |
45 |
46 | ))
47 | SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName
48 |
49 | const SelectScrollDownButton = React.forwardRef<
50 | React.ElementRef,
51 | React.ComponentPropsWithoutRef
52 | >(({ className, ...props }, ref) => (
53 |
58 |
59 |
60 | ))
61 | SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName
62 |
63 | const SelectContent = React.forwardRef<
64 | React.ElementRef,
65 | React.ComponentPropsWithoutRef
66 | >(({ className, children, position = "popper", ...props }, ref) => (
67 |
68 |
79 |
80 |
87 | {children}
88 |
89 |
90 |
91 |
92 | ))
93 | SelectContent.displayName = SelectPrimitive.Content.displayName
94 |
95 | const SelectLabel = React.forwardRef<
96 | React.ElementRef,
97 | React.ComponentPropsWithoutRef
98 | >(({ className, ...props }, ref) => (
99 |
100 | ))
101 | SelectLabel.displayName = SelectPrimitive.Label.displayName
102 |
103 | const SelectItem = React.forwardRef<
104 | React.ElementRef,
105 | React.ComponentPropsWithoutRef
106 | >(({ className, children, ...props }, ref) => (
107 |
115 |
116 |
117 |
118 |
119 |
120 |
121 | {children}
122 |
123 | ))
124 | SelectItem.displayName = SelectPrimitive.Item.displayName
125 |
126 | const SelectSeparator = React.forwardRef<
127 | React.ElementRef,
128 | React.ComponentPropsWithoutRef
129 | >(({ className, ...props }, ref) => (
130 |
131 | ))
132 | SelectSeparator.displayName = SelectPrimitive.Separator.displayName
133 |
134 | export {
135 | Select,
136 | SelectGroup,
137 | SelectValue,
138 | SelectTrigger,
139 | SelectContent,
140 | SelectLabel,
141 | SelectItem,
142 | SelectSeparator,
143 | SelectScrollUpButton,
144 | SelectScrollDownButton,
145 | }
146 |
--------------------------------------------------------------------------------
/components/ui/separator.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as SeparatorPrimitive from "@radix-ui/react-separator"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const Separator = React.forwardRef<
9 | React.ElementRef,
10 | React.ComponentPropsWithoutRef
11 | >(
12 | (
13 | { className, orientation = "horizontal", decorative = true, ...props },
14 | ref
15 | ) => (
16 |
27 | )
28 | )
29 | Separator.displayName = SeparatorPrimitive.Root.displayName
30 |
31 | export { Separator }
32 |
--------------------------------------------------------------------------------
/components/ui/sheet.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as SheetPrimitive from "@radix-ui/react-dialog"
5 | import { cva, type VariantProps } from "class-variance-authority"
6 | import { X } from "lucide-react"
7 |
8 | import { cn } from "@/lib/utils"
9 |
10 | const Sheet = SheetPrimitive.Root
11 |
12 | const SheetTrigger = SheetPrimitive.Trigger
13 |
14 | const SheetClose = SheetPrimitive.Close
15 |
16 | const SheetPortal = SheetPrimitive.Portal
17 |
18 | const SheetOverlay = React.forwardRef<
19 | React.ElementRef,
20 | React.ComponentPropsWithoutRef
21 | >(({ className, ...props }, ref) => (
22 |
30 | ))
31 | SheetOverlay.displayName = SheetPrimitive.Overlay.displayName
32 |
33 | const sheetVariants = cva(
34 | "fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
35 | {
36 | variants: {
37 | side: {
38 | top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
39 | bottom:
40 | "inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
41 | left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
42 | right:
43 | "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm",
44 | },
45 | },
46 | defaultVariants: {
47 | side: "right",
48 | },
49 | }
50 | )
51 |
52 | interface SheetContentProps
53 | extends React.ComponentPropsWithoutRef,
54 | VariantProps {}
55 |
56 | const SheetContent = React.forwardRef<
57 | React.ElementRef,
58 | SheetContentProps
59 | >(({ side = "right", className, children, ...props }, ref) => (
60 |
61 |
62 |
67 | {children}
68 |
69 |
70 | Close
71 |
72 |
73 |
74 | ))
75 | SheetContent.displayName = SheetPrimitive.Content.displayName
76 |
77 | const SheetHeader = ({
78 | className,
79 | ...props
80 | }: React.HTMLAttributes) => (
81 |
88 | )
89 | SheetHeader.displayName = "SheetHeader"
90 |
91 | const SheetFooter = ({
92 | className,
93 | ...props
94 | }: React.HTMLAttributes) => (
95 |
102 | )
103 | SheetFooter.displayName = "SheetFooter"
104 |
105 | const SheetTitle = React.forwardRef<
106 | React.ElementRef,
107 | React.ComponentPropsWithoutRef
108 | >(({ className, ...props }, ref) => (
109 |
114 | ))
115 | SheetTitle.displayName = SheetPrimitive.Title.displayName
116 |
117 | const SheetDescription = React.forwardRef<
118 | React.ElementRef,
119 | React.ComponentPropsWithoutRef
120 | >(({ className, ...props }, ref) => (
121 |
126 | ))
127 | SheetDescription.displayName = SheetPrimitive.Description.displayName
128 |
129 | export {
130 | Sheet,
131 | SheetPortal,
132 | SheetOverlay,
133 | SheetTrigger,
134 | SheetClose,
135 | SheetContent,
136 | SheetHeader,
137 | SheetFooter,
138 | SheetTitle,
139 | SheetDescription,
140 | }
141 |
--------------------------------------------------------------------------------
/components/ui/skeleton.tsx:
--------------------------------------------------------------------------------
1 | import type React from "react"
2 | import { cn } from "@/lib/utils"
3 |
4 | function Skeleton({ className, ...props }: React.HTMLAttributes) {
5 | return
6 | }
7 |
8 | export { Skeleton }
9 |
--------------------------------------------------------------------------------
/components/ui/slider.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as SliderPrimitive from "@radix-ui/react-slider"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const Slider = React.forwardRef<
9 | React.ElementRef,
10 | React.ComponentPropsWithoutRef
11 | >(({ className, ...props }, ref) => (
12 |
20 |
21 |
22 |
23 |
24 |
25 | ))
26 | Slider.displayName = SliderPrimitive.Root.displayName
27 |
28 | export { Slider }
29 |
--------------------------------------------------------------------------------
/components/ui/sonner.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { useTheme } from "next-themes"
4 | import { Toaster as Sonner } from "sonner"
5 |
6 | type ToasterProps = React.ComponentProps
7 |
8 | const Toaster = ({ ...props }: ToasterProps) => {
9 | const { theme = "system" } = useTheme()
10 |
11 | return (
12 |
28 | )
29 | }
30 |
31 | export { Toaster }
32 |
--------------------------------------------------------------------------------
/components/ui/suggestion-card.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import type * as React from "react"
4 | import { cn } from "@/lib/utils"
5 | import { motion } from "framer-motion"
6 | import { ArrowRight } from "lucide-react"
7 | import { useTheme } from "next-themes"
8 |
9 | interface SuggestionCardProps extends React.HTMLAttributes {
10 | icon: React.ReactNode
11 | title: string
12 | description: string
13 | onClick?: () => void
14 | }
15 |
16 | export function SuggestionCard({ icon, title, description, onClick, className, ...props }: SuggestionCardProps) {
17 | const { theme } = useTheme()
18 | const isDark = theme === "dark"
19 |
20 | return (
21 | {
28 | if (e.key === "Enter" || e.key === " ") {
29 | onClick?.()
30 | }
31 | }}
32 | >
33 |
41 | {/* Base background - theme aware */}
42 |
43 |
44 | {/* Gradient overlay - theme aware */}
45 |
46 |
47 | {/* Mesh gradient - theme aware */}
48 |
49 |
50 | {/* Top highlight line - theme aware */}
51 |
52 |
53 | {/* Content */}
54 |
55 |
58 |
59 |
{title}
60 |
{description}
61 |
62 |
63 |
64 |
67 |
68 |
69 | )
70 | }
71 |
--------------------------------------------------------------------------------
/components/ui/switch.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as SwitchPrimitives from "@radix-ui/react-switch"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const Switch = React.forwardRef<
9 | React.ElementRef,
10 | React.ComponentPropsWithoutRef
11 | >(({ className, ...props }, ref) => (
12 |
20 |
25 |
26 | ))
27 | Switch.displayName = SwitchPrimitives.Root.displayName
28 |
29 | export { Switch }
30 |
--------------------------------------------------------------------------------
/components/ui/table.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 |
3 | import { cn } from "@/lib/utils"
4 |
5 | const Table = React.forwardRef<
6 | HTMLTableElement,
7 | React.HTMLAttributes
8 | >(({ className, ...props }, ref) => (
9 |
16 | ))
17 | Table.displayName = "Table"
18 |
19 | const TableHeader = React.forwardRef<
20 | HTMLTableSectionElement,
21 | React.HTMLAttributes
22 | >(({ className, ...props }, ref) => (
23 |
24 | ))
25 | TableHeader.displayName = "TableHeader"
26 |
27 | const TableBody = React.forwardRef<
28 | HTMLTableSectionElement,
29 | React.HTMLAttributes
30 | >(({ className, ...props }, ref) => (
31 |
36 | ))
37 | TableBody.displayName = "TableBody"
38 |
39 | const TableFooter = React.forwardRef<
40 | HTMLTableSectionElement,
41 | React.HTMLAttributes
42 | >(({ className, ...props }, ref) => (
43 | tr]:last:border-b-0",
47 | className
48 | )}
49 | {...props}
50 | />
51 | ))
52 | TableFooter.displayName = "TableFooter"
53 |
54 | const TableRow = React.forwardRef<
55 | HTMLTableRowElement,
56 | React.HTMLAttributes
57 | >(({ className, ...props }, ref) => (
58 |
66 | ))
67 | TableRow.displayName = "TableRow"
68 |
69 | const TableHead = React.forwardRef<
70 | HTMLTableCellElement,
71 | React.ThHTMLAttributes
72 | >(({ className, ...props }, ref) => (
73 | |
81 | ))
82 | TableHead.displayName = "TableHead"
83 |
84 | const TableCell = React.forwardRef<
85 | HTMLTableCellElement,
86 | React.TdHTMLAttributes
87 | >(({ className, ...props }, ref) => (
88 | |
93 | ))
94 | TableCell.displayName = "TableCell"
95 |
96 | const TableCaption = React.forwardRef<
97 | HTMLTableCaptionElement,
98 | React.HTMLAttributes
99 | >(({ className, ...props }, ref) => (
100 |
105 | ))
106 | TableCaption.displayName = "TableCaption"
107 |
108 | export {
109 | Table,
110 | TableHeader,
111 | TableBody,
112 | TableFooter,
113 | TableHead,
114 | TableRow,
115 | TableCell,
116 | TableCaption,
117 | }
118 |
--------------------------------------------------------------------------------
/components/ui/tabs.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as TabsPrimitive from "@radix-ui/react-tabs"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const Tabs = TabsPrimitive.Root
9 |
10 | const TabsList = React.forwardRef<
11 | React.ElementRef,
12 | React.ComponentPropsWithoutRef
13 | >(({ className, ...props }, ref) => (
14 |
22 | ))
23 | TabsList.displayName = TabsPrimitive.List.displayName
24 |
25 | const TabsTrigger = React.forwardRef<
26 | React.ElementRef,
27 | React.ComponentPropsWithoutRef
28 | >(({ className, ...props }, ref) => (
29 |
37 | ))
38 | TabsTrigger.displayName = TabsPrimitive.Trigger.displayName
39 |
40 | const TabsContent = React.forwardRef<
41 | React.ElementRef,
42 | React.ComponentPropsWithoutRef
43 | >(({ className, ...props }, ref) => (
44 |
52 | ))
53 | TabsContent.displayName = TabsPrimitive.Content.displayName
54 |
55 | export { Tabs, TabsList, TabsTrigger, TabsContent }
56 |
--------------------------------------------------------------------------------
/components/ui/text-animated-gradient.tsx:
--------------------------------------------------------------------------------
1 | import type React from "react"
2 | const TextAnimatedGradient = ({ children, className = "" }: { children: React.ReactNode; className?: string }) => {
3 | return (
4 |
7 | {children}
8 |
9 | )
10 | }
11 |
12 | export default TextAnimatedGradient
13 |
--------------------------------------------------------------------------------
/components/ui/textarea.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 |
3 | import { cn } from "@/lib/utils"
4 |
5 | export interface TextareaProps extends React.TextareaHTMLAttributes {}
6 |
7 | const Textarea = React.forwardRef(({ className, ...props }, ref) => {
8 | return (
9 |
17 | )
18 | })
19 | Textarea.displayName = "Textarea"
20 |
21 | export { Textarea }
22 |
--------------------------------------------------------------------------------
/components/ui/theme-toggle.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { useTheme } from "next-themes"
4 | import { Button } from "@/components/ui/button"
5 | import { Sun, Moon } from "lucide-react"
6 | import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"
7 |
8 | export function ThemeToggle() {
9 | const { theme, setTheme } = useTheme()
10 |
11 | return (
12 |
13 |
14 |
15 |
25 |
26 |
27 | {theme === "dark" ? "Switch to light mode" : "Switch to dark mode"}
28 |
29 |
30 |
31 | )
32 | }
33 |
--------------------------------------------------------------------------------
/components/ui/toast.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as ToastPrimitives from "@radix-ui/react-toast"
5 | import { cva, type VariantProps } from "class-variance-authority"
6 | import { X } from "lucide-react"
7 |
8 | import { cn } from "@/lib/utils"
9 |
10 | const ToastProvider = ToastPrimitives.Provider
11 |
12 | const ToastViewport = React.forwardRef<
13 | React.ElementRef,
14 | React.ComponentPropsWithoutRef
15 | >(({ className, ...props }, ref) => (
16 |
24 | ))
25 | ToastViewport.displayName = ToastPrimitives.Viewport.displayName
26 |
27 | const toastVariants = cva(
28 | "group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full",
29 | {
30 | variants: {
31 | variant: {
32 | default: "border bg-background text-foreground",
33 | destructive: "destructive group border-destructive bg-destructive text-destructive-foreground",
34 | },
35 | },
36 | defaultVariants: {
37 | variant: "default",
38 | },
39 | },
40 | )
41 |
42 | const Toast = React.forwardRef<
43 | React.ElementRef,
44 | React.ComponentPropsWithoutRef & VariantProps
45 | >(({ className, variant, ...props }, ref) => {
46 | return
47 | })
48 | Toast.displayName = ToastPrimitives.Root.displayName
49 |
50 | const ToastAction = React.forwardRef<
51 | React.ElementRef,
52 | React.ComponentPropsWithoutRef
53 | >(({ className, ...props }, ref) => (
54 |
62 | ))
63 | ToastAction.displayName = ToastPrimitives.Action.displayName
64 |
65 | const ToastClose = React.forwardRef<
66 | React.ElementRef,
67 | React.ComponentPropsWithoutRef
68 | >(({ className, ...props }, ref) => (
69 |
78 |
79 |
80 | ))
81 | ToastClose.displayName = ToastPrimitives.Close.displayName
82 |
83 | const ToastTitle = React.forwardRef<
84 | React.ElementRef,
85 | React.ComponentPropsWithoutRef
86 | >(({ className, ...props }, ref) => (
87 |
88 | ))
89 | ToastTitle.displayName = ToastPrimitives.Title.displayName
90 |
91 | const ToastDescription = React.forwardRef<
92 | React.ElementRef,
93 | React.ComponentPropsWithoutRef
94 | >(({ className, ...props }, ref) => (
95 |
96 | ))
97 | ToastDescription.displayName = ToastPrimitives.Description.displayName
98 |
99 | type ToastProps = React.ComponentPropsWithoutRef
100 |
101 | type ToastActionElement = React.ReactElement
102 |
103 | export {
104 | type ToastProps,
105 | type ToastActionElement,
106 | ToastProvider,
107 | ToastViewport,
108 | Toast,
109 | ToastTitle,
110 | ToastDescription,
111 | ToastClose,
112 | ToastAction,
113 | }
114 |
--------------------------------------------------------------------------------
/components/ui/toaster.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { useToast } from "@/hooks/use-toast"
4 | import { Toast, ToastClose, ToastDescription, ToastProvider, ToastTitle, ToastViewport } from "@/components/ui/toast"
5 |
6 | export function Toaster() {
7 | const { toasts } = useToast()
8 |
9 | return (
10 |
11 | {toasts.map(({ id, title, description, action, ...props }) => (
12 |
13 |
14 | {title && {title}}
15 | {description && {description}}
16 |
17 | {action}
18 |
19 |
20 | ))}
21 |
22 |
23 | )
24 | }
25 |
--------------------------------------------------------------------------------
/components/ui/toggle-group.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as ToggleGroupPrimitive from "@radix-ui/react-toggle-group"
5 | import { type VariantProps } from "class-variance-authority"
6 |
7 | import { cn } from "@/lib/utils"
8 | import { toggleVariants } from "@/components/ui/toggle"
9 |
10 | const ToggleGroupContext = React.createContext<
11 | VariantProps
12 | >({
13 | size: "default",
14 | variant: "default",
15 | })
16 |
17 | const ToggleGroup = React.forwardRef<
18 | React.ElementRef,
19 | React.ComponentPropsWithoutRef &
20 | VariantProps
21 | >(({ className, variant, size, children, ...props }, ref) => (
22 |
27 |
28 | {children}
29 |
30 |
31 | ))
32 |
33 | ToggleGroup.displayName = ToggleGroupPrimitive.Root.displayName
34 |
35 | const ToggleGroupItem = React.forwardRef<
36 | React.ElementRef,
37 | React.ComponentPropsWithoutRef &
38 | VariantProps