diff --git a/app/(auth)/layout.tsx b/app/(auth)/layout.tsx new file mode 100644 index 0000000..ab2da5a --- /dev/null +++ b/app/(auth)/layout.tsx @@ -0,0 +1,40 @@ +import Link from "next/link"; + +export default function AuthLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( +
+ {/* Background ambient lighting */} +
+
+
+
+
+ + {/* Header brand link */} +
+ +
+ V +
+ + Veodit + + +
+ + {/* Auth Form Container */} +
+ {children} +
+ + {/* Footer copyright */} +
+ © {new Date().getFullYear()} Veodit. Open-source AI Video Editor. +
+
+ ); +} diff --git a/app/(auth)/sign-in/[[...sign-in]]/page.tsx b/app/(auth)/sign-in/[[...sign-in]]/page.tsx new file mode 100644 index 0000000..26a7f32 --- /dev/null +++ b/app/(auth)/sign-in/[[...sign-in]]/page.tsx @@ -0,0 +1,30 @@ +import { SignIn } from "@clerk/nextjs"; + +export default function SignInPage() { + return ( + + ); +} diff --git a/app/(auth)/sign-up/[[...sign-up]]/page.tsx b/app/(auth)/sign-up/[[...sign-up]]/page.tsx new file mode 100644 index 0000000..d57d042 --- /dev/null +++ b/app/(auth)/sign-up/[[...sign-up]]/page.tsx @@ -0,0 +1,28 @@ +import { SignUp } from "@clerk/nextjs"; + +export default function SignUpPage() { + return ( + + ); +} diff --git a/app/(protected)/dashboard/page.tsx b/app/(protected)/dashboard/page.tsx new file mode 100644 index 0000000..6326036 --- /dev/null +++ b/app/(protected)/dashboard/page.tsx @@ -0,0 +1,243 @@ +import { currentUser } from "@clerk/nextjs/server"; +import Image from "next/image"; +import Link from "next/link"; +import { + Upload, + Sparkles, + Zap, + Film, + Video, + Clock, + ArrowRight, + TrendingUp, + Sliders, + CheckCircle2, +} from "lucide-react"; +import { getUserDisplayName, getUserPrimaryEmail, getUserAvatarUrl } from "@/lib/clerk"; + +export default async function DashboardPage() { + const user = await currentUser(); + const displayName = getUserDisplayName(user); + const firstName = user?.firstName || displayName; + const primaryEmail = getUserPrimaryEmail(user); + const avatarUrl = getUserAvatarUrl(user); + + return ( +
+ {/* Welcome Banner */} +
+
+
+
+ {avatarUrl ? ( +
+ {displayName} +
+ ) : ( +
+ {firstName.charAt(0).toUpperCase()} +
+ )} + +
+
+ + AI Workspace Active +
+

+ Welcome back, {firstName}! +

+

+ {primaryEmail || "Ready to transform long videos into viral shorts?"} +

+
+
+ + + + Upload New Video + +
+
+ + {/* Stats Overview Grid */} +
+ {/* User Identity / Account */} +
+
+ Account + +
+

{displayName}

+

{primaryEmail}

+
+ + {/* AI Credits */} +
+
+ AI Credits + +
+
+ 850 + / 1,000 credits remaining +
+
+
+
+
+ + {/* Total Shorts Generated */} +
+
+ Clips Generated + +
+

24

+

+ + +12 clips this week +

+
+ + {/* Time Saved */} +
+
+ Time Saved + +
+

18.5 hrs

+

Compared to manual editing

+
+
+ + {/* Main Content Dashboard Area */} +
+ {/* Upload Video CTA Area */} +
+ {/* Upload Dropzone Placeholder */} +
+
+ +
+

+ Drop your long video here or click to browse +

+

+ Supports MP4, MOV, WebM up to 4K resolution (max 2GB). Podcasts, streams & vlogs. +

+
+ + AI Auto-Cut, Silence Removal & Auto-Captions enabled +
+
+ + {/* Recent Projects Placeholder */} +
+
+

+

+ + View All + +
+ +
+ {[ + { name: "Tech Podcast Ep. 42", duration: "48:12", clips: 6, status: "Ready", time: "2 hours ago" }, + { name: "Product Launch Livestream", duration: "1:15:30", clips: 10, status: "Processing", time: "5 hours ago" }, + { name: "React 19 Deep Dive", duration: "32:45", clips: 4, status: "Ready", time: "1 day ago" }, + ].map((project, idx) => ( +
+
+
+ +
+
+

{project.name}

+

+ {project.duration} • {project.clips} clips generated • {project.time} +

+
+
+ + {project.status} + +
+ ))} +
+
+
+ + {/* Right Sidebar: Recent Exports & Quick Presets */} +
+ {/* Export Queue / Recent Exports */} +
+

+ + Recent Exports +

+ +
+ {[ + { title: "Short #1 - AI Breakthroughs", platform: "YouTube Shorts", quality: "4K 60fps" }, + { title: "Reel #2 - Key Takeaways", platform: "Instagram Reels", quality: "1080p" }, + { title: "Clip #3 - Coding Demo", platform: "TikTok", quality: "1080p" }, + ].map((exportItem, i) => ( +
+
+ {exportItem.title} + {exportItem.quality} +
+
+ {exportItem.platform} + Download +
+
+ ))} +
+
+ + {/* Preset Export Settings */} +
+

+ + Auto-Cut Presets +

+
+ {[ + "High Energy Hooks (15-30s)", + "Detailed Explainers (30-60s)", + "Viral Quotes & Punchlines", + ].map((preset, i) => ( +
+ {preset} + +
+ ))} +
+
+
+
+
+ ); +} diff --git a/app/(protected)/layout.tsx b/app/(protected)/layout.tsx new file mode 100644 index 0000000..6a3b3dd --- /dev/null +++ b/app/(protected)/layout.tsx @@ -0,0 +1,20 @@ +import { auth } from "@clerk/nextjs/server"; +import { ProtectedHeader } from "@/components/layout/ProtectedHeader"; + +export default async function ProtectedLayout({ + children, +}: { + children: React.ReactNode; +}) { + // Resource-based server-side protection + await auth.protect(); + + return ( +
+ +
+ {children} +
+
+ ); +} diff --git a/app/(protected)/profile/page.tsx b/app/(protected)/profile/page.tsx new file mode 100644 index 0000000..0d46193 --- /dev/null +++ b/app/(protected)/profile/page.tsx @@ -0,0 +1,42 @@ +import { UserProfile } from "@clerk/nextjs"; +import { dark } from "@clerk/themes"; + +export default function ProfilePage() { + return ( +
+
+

+ User Profile & Security +

+

+ Manage your account credentials, security preferences, and active sessions. +

+
+ +
+ +
+
+ ); +} diff --git a/app/(protected)/settings/page.tsx b/app/(protected)/settings/page.tsx new file mode 100644 index 0000000..4f1ec03 --- /dev/null +++ b/app/(protected)/settings/page.tsx @@ -0,0 +1,146 @@ +"use client"; + +import { useState } from "react"; +import { Sliders, Key, Monitor, Sparkles, Check, Copy } from "lucide-react"; +import { Button } from "@/components/ui/button"; + +export default function SettingsPage() { + const [copied, setCopied] = useState(false); + const apiKey = "veodit_live_89f2a019e481b7a2d3c90e1f2b4c6a8"; + + const handleCopyKey = () => { + navigator.clipboard.writeText(apiKey); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + }; + + return ( +
+
+

+ Application & API Settings +

+

+ Configure default AI video processing parameters, export presets, and API keys. +

+
+ + {/* AI Processing Settings */} +
+
+
+ +
+
+

AI Auto-Cut Settings

+

Customize how AI evaluates and slices video segments

+
+
+ +
+
+
+

Automatic Silence Removal

+

Remove pauses longer than 0.5 seconds automatically

+
+ +
+ +
+
+

Filler Word Removal

+

Detect and trim "um", "uh", and repeated hesitations

+
+ +
+ +
+
+

Animated Auto Captions

+

Generate word-by-word highlighted subtitles

+
+ +
+
+
+ + {/* Default Export Settings */} +
+
+
+ +
+
+

Export Defaults

+

Set default rendering quality and aspect ratio

+
+
+ +
+
+ + +
+ +
+ + +
+
+
+ + {/* API Keys */} +
+
+
+ +
+
+

API Access & Keys

+

Integrate Veodit AI rendering pipeline into your applications

+
+
+ +
+ +
+ + +
+

+ Keep your API keys confidential. Do not expose them in public frontend repositories. +

+
+
+
+ ); +} diff --git a/app/layout.tsx b/app/layout.tsx index 4ecb241..0fd974a 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,5 +1,7 @@ import type { Metadata } from "next"; import { Inter, Geist_Mono } from "next/font/google"; +import { ClerkProvider } from "@clerk/nextjs"; +import { dark } from "@clerk/themes"; import "./globals.css"; import { cn } from "@/lib/utils"; @@ -47,18 +49,40 @@ export const metadata: Metadata = { export default function RootLayout({ children }: LayoutProps<"/">) { return ( - - - {children} - - + + + {children} + + + ); } diff --git a/components/auth/SignInButton.tsx b/components/auth/SignInButton.tsx new file mode 100644 index 0000000..f55ac54 --- /dev/null +++ b/components/auth/SignInButton.tsx @@ -0,0 +1,29 @@ +"use client"; + +import Link from "next/link"; +import { LogIn } from "lucide-react"; +import { cn } from "@/lib/utils"; + +interface SignInButtonProps { + className?: string; + children?: React.ReactNode; +} + +export function SignInButton({ className, children }: SignInButtonProps) { + return ( + + {children || ( + <> + + Sign In + + )} + + ); +} diff --git a/components/auth/SignUpButton.tsx b/components/auth/SignUpButton.tsx new file mode 100644 index 0000000..d95d702 --- /dev/null +++ b/components/auth/SignUpButton.tsx @@ -0,0 +1,29 @@ +"use client"; + +import Link from "next/link"; +import { ChevronRight } from "lucide-react"; +import { cn } from "@/lib/utils"; + +interface SignUpButtonProps { + className?: string; + children?: React.ReactNode; +} + +export function SignUpButton({ className, children }: SignUpButtonProps) { + return ( + + {children || ( + <> + Get Started + + + )} + + ); +} diff --git a/components/auth/UserMenu.tsx b/components/auth/UserMenu.tsx new file mode 100644 index 0000000..e427f81 --- /dev/null +++ b/components/auth/UserMenu.tsx @@ -0,0 +1,38 @@ +"use client"; + +import { UserButton } from "@clerk/nextjs"; +import { LayoutDashboard, User, Settings } from "lucide-react"; + +export function UserMenu() { + return ( + + + } + href="/dashboard" + /> + } + href="/profile" + /> + } + href="/settings" + /> + + + ); +} diff --git a/components/landing/Header.tsx b/components/landing/Header.tsx index 47e36a9..e707950 100644 --- a/components/landing/Header.tsx +++ b/components/landing/Header.tsx @@ -3,15 +3,17 @@ import { useState, useEffect } from "react"; import Link from "next/link"; import { motion, AnimatePresence } from "motion/react"; -import { Menu, X, ChevronRight } from "lucide-react"; +import { Menu, X, ChevronRight, LayoutDashboard } from "lucide-react"; +import { useAuth } from "@clerk/nextjs"; import { GitHubIcon } from "@/components/icons/github"; -import { Button } from "@/components/ui/button"; +import { UserMenu } from "@/components/auth/UserMenu"; import { NAV_LINKS } from "@/constants"; import { cn } from "@/lib/utils"; export function Header() { const [scrolled, setScrolled] = useState(false); const [mobileOpen, setMobileOpen] = useState(false); + const { isSignedIn, isLoaded } = useAuth(); useEffect(() => { const onScroll = () => setScrolled(window.scrollY > 20); @@ -22,7 +24,9 @@ export function Header() { // Lock body scroll when mobile nav is open useEffect(() => { document.body.style.overflow = mobileOpen ? "hidden" : ""; - return () => { document.body.style.overflow = ""; }; + return () => { + document.body.style.overflow = ""; + }; }, [mobileOpen]); return ( @@ -45,7 +49,7 @@ export function Header() { - {/* Desktop Nav */} + {/* Desktop Nav Links */}
{NAV_LINKS.map((link) => ( - {/* Desktop Right */} + {/* Desktop Right Auth Navigation */}
GitHub - - Log in - - + + {isLoaded && !isSignedIn && ( + <> + + Sign In + + + Get Started + + + + )} + + {isLoaded && isSignedIn && ( + <> + + + Dashboard + + + + )}
{/* Mobile Menu Toggle */} @@ -114,6 +137,7 @@ export function Header() { {link.label} ))} +
GitHub - setMobileOpen(false)} - > - Log in - - + + {isLoaded && !isSignedIn && ( + <> + setMobileOpen(false)} + > + Sign In + + setMobileOpen(false)} + > + Get Started + + + + )} + + {isLoaded && isSignedIn && ( + <> + setMobileOpen(false)} + > + + Dashboard + +
+ Account + +
+ + )}
diff --git a/components/landing/Showcase.tsx b/components/landing/Showcase.tsx index 7d68bda..0466107 100644 --- a/components/landing/Showcase.tsx +++ b/components/landing/Showcase.tsx @@ -209,7 +209,7 @@ export function Showcase() { key={i} className="flex-1 bg-emerald-500/30 rounded-full" style={{ - height: `${20 + Math.sin(i * 0.5) * 40 + Math.random() * 30}%`, + height: `${25 + Math.abs(Math.sin(i * 0.4) * 45 + Math.cos(i * 0.75) * 25)}%`, }} /> ))} diff --git a/components/layout/ProtectedHeader.tsx b/components/layout/ProtectedHeader.tsx new file mode 100644 index 0000000..92d9dff --- /dev/null +++ b/components/layout/ProtectedHeader.tsx @@ -0,0 +1,93 @@ +"use client"; + +import Link from "next/link"; +import { usePathname } from "next/navigation"; +import { LayoutDashboard, User, Settings, Sparkles } from "lucide-react"; +import { UserMenu } from "@/components/auth/UserMenu"; +import { cn } from "@/lib/utils"; + +const PROTECTED_NAV_LINKS = [ + { label: "Dashboard", href: "/dashboard", icon: LayoutDashboard }, + { label: "Profile", href: "/profile", icon: User }, + { label: "Settings", href: "/settings", icon: Settings }, +]; + +export function ProtectedHeader() { + const pathname = usePathname(); + + return ( +
+
+ {/* Brand */} +
+ +
+ V +
+ + Veodit + + + + {/* Application Navigation */} + +
+ + {/* Right Header Actions */} +
+ + + New Video + + + {/* Clerk User Menu */} + +
+
+ + {/* Mobile Navigation Bar */} +
+ {PROTECTED_NAV_LINKS.map((link) => { + const Icon = link.icon; + const isActive = pathname === link.href; + return ( + + + {link.label} + + ); + })} +
+
+ ); +} diff --git a/lib/clerk.ts b/lib/clerk.ts new file mode 100644 index 0000000..95fec64 --- /dev/null +++ b/lib/clerk.ts @@ -0,0 +1,29 @@ +import type { User } from "@clerk/nextjs/server"; + +export function getUserDisplayName(user: User | null | undefined): string { + if (!user) return "Creator"; + if (user.firstName && user.lastName) { + return `${user.firstName} ${user.lastName}`; + } + if (user.firstName) return user.firstName; + if (user.username) return user.username; + if (user.emailAddresses && user.emailAddresses.length > 0) { + return user.emailAddresses[0].emailAddress.split("@")[0]; + } + return "Creator"; +} + +export function getUserPrimaryEmail(user: User | null | undefined): string { + if (!user || !user.emailAddresses || user.emailAddresses.length === 0) { + return ""; + } + const primary = user.emailAddresses.find( + (e) => e.id === user.primaryEmailAddressId + ); + return primary ? primary.emailAddress : user.emailAddresses[0].emailAddress; +} + +export function getUserAvatarUrl(user: User | null | undefined): string { + if (!user) return ""; + return user.imageUrl || ""; +} diff --git a/middleware.ts b/middleware.ts new file mode 100644 index 0000000..254f2d7 --- /dev/null +++ b/middleware.ts @@ -0,0 +1,12 @@ +import { clerkMiddleware } from "@clerk/nextjs/server"; + +export default clerkMiddleware(); + +export const config = { + matcher: [ + // Skip Next.js internals and all static files, unless found in search params + "/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|svg|png|jpg|jpeg|webp|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)", + // Always run for API routes + "/(api|trpc)(.*)", + ], +}; diff --git a/next.config.ts b/next.config.ts index e9ffa30..972586b 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,7 +1,18 @@ import type { NextConfig } from "next"; const nextConfig: NextConfig = { - /* config options here */ + images: { + remotePatterns: [ + { + protocol: "https", + hostname: "img.clerk.com", + }, + { + protocol: "https", + hostname: "images.clerk.dev", + }, + ], + }, }; export default nextConfig; diff --git a/package-lock.json b/package-lock.json index ec6ef10..49861ea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,8 @@ "version": "0.1.0", "dependencies": { "@base-ui/react": "^1.7.0", + "@clerk/nextjs": "^7.6.4", + "@clerk/themes": "^2.4.57", "@shadcn/react": "^0.2.1", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", @@ -526,6 +528,134 @@ } } }, + "node_modules/@clerk/backend": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/@clerk/backend/-/backend-3.15.0.tgz", + "integrity": "sha512-m8/X0hAjX9HyBD0IdfIFIxP1fbbSvnq5DB6mPLeIc53Ya9XCLn/hlQkaIM8pobKsM/jquRt9DQLT9IvV6z6bFQ==", + "license": "MIT", + "dependencies": { + "@clerk/shared": "^4.25.10", + "standardwebhooks": "^1.0.0", + "tslib": "2.8.1" + }, + "engines": { + "node": ">=20.9.0" + } + }, + "node_modules/@clerk/nextjs": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@clerk/nextjs/-/nextjs-7.6.4.tgz", + "integrity": "sha512-eDo55Yheel2xoYSvVRsg8SKiZV5sB57NIfX2tTuSRCHh2/pG20nZ2wUHg7CdVQ7xtwtIp9qkmGScSkjm/T4++Q==", + "license": "MIT", + "dependencies": { + "@clerk/backend": "^3.15.0", + "@clerk/react": "^6.12.10", + "@clerk/shared": "^4.25.10", + "server-only": "0.0.1", + "tslib": "2.8.1" + }, + "engines": { + "node": ">=20.9.0" + }, + "peerDependencies": { + "next": "^15.2.8 || ^15.3.8 || ^15.4.10 || ^15.5.9 || ^15.6.0-0 || ^16.0.10 || ^16.1.0-0", + "react": "^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0", + "react-dom": "^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0" + } + }, + "node_modules/@clerk/react": { + "version": "6.12.10", + "resolved": "https://registry.npmjs.org/@clerk/react/-/react-6.12.10.tgz", + "integrity": "sha512-VDYyCyzRdMsNW6qQsMp1+L2VOXxFwpLfWBxOWPvUNOYCruecnAZjUYEJWS+E+i5MAPhSew9HjuweS4bO0FclTQ==", + "license": "MIT", + "dependencies": { + "@clerk/shared": "^4.25.10", + "tslib": "2.8.1" + }, + "engines": { + "node": ">=20.9.0" + }, + "peerDependencies": { + "react": "^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0", + "react-dom": "^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0" + } + }, + "node_modules/@clerk/shared": { + "version": "4.25.10", + "resolved": "https://registry.npmjs.org/@clerk/shared/-/shared-4.25.10.tgz", + "integrity": "sha512-Vjqk74nQGJ8y+cm+eSshoBzRvBhIzuKLOs3Gt13gEHdyQ6yV798Dl2SrBKt0jw2u6I1tOcD3s8/i9P4yJYo0dw==", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "^5.100.6", + "dequal": "2.0.3", + "glob-to-regexp": "0.4.1", + "js-cookie": "3.0.7" + }, + "engines": { + "node": ">=20.9.0" + }, + "peerDependencies": { + "react": "^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0", + "react-dom": "^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/@clerk/themes": { + "version": "2.4.57", + "resolved": "https://registry.npmjs.org/@clerk/themes/-/themes-2.4.57.tgz", + "integrity": "sha512-Nb3bO79rMTU/MPVTC/dde6LG27/IgOMKIYi5KSvAmO4ZUHlj0OWufu6CMvz5OYVZ0YdyMnTBU2aPGRUiRzO+2w==", + "license": "MIT", + "dependencies": { + "@clerk/shared": "^3.47.2", + "tslib": "2.8.1" + }, + "engines": { + "node": ">=18.17.0" + } + }, + "node_modules/@clerk/themes/node_modules/@clerk/shared": { + "version": "3.47.8", + "resolved": "https://registry.npmjs.org/@clerk/shared/-/shared-3.47.8.tgz", + "integrity": "sha512-cPURU1it/Eal4uXO9SOcHzw9sfE6Opi21W8EJUg+Ri80Q6JKtK8qBmyOpIyqgf09EanexhTzS4xrmYlvn2p7Iw==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "csstype": "3.1.3", + "dequal": "2.0.3", + "glob-to-regexp": "0.4.1", + "js-cookie": "3.0.7", + "std-env": "^3.9.0", + "swr": "2.3.4" + }, + "engines": { + "node": ">=18.17.0" + }, + "peerDependencies": { + "react": "^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0", + "react-dom": "^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "node_modules/@clerk/themes/node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, "node_modules/@date-fns/tz": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@date-fns/tz/-/tz-1.5.0.tgz", @@ -2304,6 +2434,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@stablelib/base64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/base64/-/base64-1.0.1.tgz", + "integrity": "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==", + "license": "MIT" + }, "node_modules/@standard-schema/spec": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", @@ -2596,6 +2732,16 @@ "tailwindcss": "4.3.3" } }, + "node_modules/@tanstack/query-core": { + "version": "5.101.4", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.101.4.tgz", + "integrity": "sha512-gNwcvOJcRbLWPOLG/2OBm+zM+Yv+MKsXKEOWC57USuZDEsI71hEErQsiEGx5wX9rzWWkfwM0fVSPoiIFSsxfiw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, "node_modules/@ts-morph/common": { "version": "0.27.0", "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.27.0.tgz", @@ -4705,6 +4851,15 @@ "node": ">= 0.8" } }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -5713,6 +5868,12 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-sha256": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-sha256/-/fast-sha256-1.3.0.tgz", + "integrity": "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==", + "license": "Unlicense" + }, "node_modules/fast-uri": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.5.tgz", @@ -6110,6 +6271,12 @@ "node": ">=10.13.0" } }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "license": "BSD-2-Clause" + }, "node_modules/globals": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", @@ -7043,6 +7210,15 @@ "url": "https://github.com/sponsors/panva" } }, + "node_modules/js-cookie": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.7.tgz", + "integrity": "sha512-z/wZZgDrkNV1eA0ULjM/F9/50Ya8fbzgKneSpoPsXSGd0KnpdtHfOZWK+GcwLk+EZbS4F9RBhU+K2RgzuDaItw==", + "license": "MIT", + "engines": { + "node": ">=20" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -9218,6 +9394,12 @@ "url": "https://opencollective.com/express" } }, + "node_modules/server-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/server-only/-/server-only-0.0.1.tgz", + "integrity": "sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==", + "license": "MIT" + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -9569,6 +9751,16 @@ "dev": true, "license": "MIT" }, + "node_modules/standardwebhooks": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/standardwebhooks/-/standardwebhooks-1.0.0.tgz", + "integrity": "sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==", + "license": "MIT", + "dependencies": { + "@stablelib/base64": "^1.0.0", + "fast-sha256": "^1.3.0" + } + }, "node_modules/statuses": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", @@ -9578,6 +9770,12 @@ "node": ">= 0.8" } }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "license": "MIT" + }, "node_modules/stdin-discarder": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", @@ -9892,6 +10090,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/swr": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/swr/-/swr-2.3.4.tgz", + "integrity": "sha512-bYd2lrhc+VarcpkgWclcUi92wYCpOgMws9Sd1hG1ntAu0NEy+14CbotuFjshBU2kt9rYj9TSmDcybpxpeTU1fg==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.3", + "use-sync-external-store": "^1.4.0" + }, + "peerDependencies": { + "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/systeminformation": { "version": "5.33.1", "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.33.1.tgz", diff --git a/package.json b/package.json index bd2a89b..cd4b07c 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,8 @@ }, "dependencies": { "@base-ui/react": "^1.7.0", + "@clerk/nextjs": "^7.6.4", + "@clerk/themes": "^2.4.57", "@shadcn/react": "^0.2.1", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1",