├── .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) =>