From 263840570d8d169ab465e97758d1ba8acae92c87 Mon Sep 17 00:00:00 2001 From: Owen Gretzinger Date: Fri, 11 Jul 2025 16:11:33 -0400 Subject: [PATCH] feat: add landing page --- README.md | 5 +- landing-page/.gitignore | 27 + landing-page/app/components/community.tsx | 68 + landing-page/app/components/features.tsx | 78 + landing-page/app/components/final-cta.tsx | 60 + landing-page/app/components/footer.tsx | 137 + landing-page/app/components/header.tsx | 50 + landing-page/app/components/hero.tsx | 95 + landing-page/app/components/how-it-works.tsx | 67 + landing-page/app/components/pricing.tsx | 140 + landing-page/app/components/privacy.tsx | 70 + landing-page/app/globals.css | 59 + landing-page/app/layout.tsx | 39 + landing-page/app/page.tsx | 25 + landing-page/components.json | 21 + landing-page/components/theme-provider.tsx | 11 + landing-page/components/ui/accordion.tsx | 58 + landing-page/components/ui/alert-dialog.tsx | 141 + landing-page/components/ui/alert.tsx | 59 + landing-page/components/ui/aspect-ratio.tsx | 7 + landing-page/components/ui/avatar.tsx | 50 + landing-page/components/ui/badge.tsx | 36 + landing-page/components/ui/breadcrumb.tsx | 115 + landing-page/components/ui/button.tsx | 56 + landing-page/components/ui/calendar.tsx | 66 + landing-page/components/ui/card.tsx | 79 + landing-page/components/ui/carousel.tsx | 262 ++ landing-page/components/ui/chart.tsx | 365 ++ landing-page/components/ui/checkbox.tsx | 30 + landing-page/components/ui/collapsible.tsx | 11 + landing-page/components/ui/command.tsx | 153 + landing-page/components/ui/context-menu.tsx | 200 + landing-page/components/ui/dialog.tsx | 122 + landing-page/components/ui/drawer.tsx | 118 + landing-page/components/ui/dropdown-menu.tsx | 200 + landing-page/components/ui/form.tsx | 178 + landing-page/components/ui/hover-card.tsx | 29 + landing-page/components/ui/input-otp.tsx | 71 + landing-page/components/ui/input.tsx | 22 + landing-page/components/ui/label.tsx | 26 + landing-page/components/ui/menubar.tsx | 236 ++ .../components/ui/navigation-menu.tsx | 128 + landing-page/components/ui/pagination.tsx | 117 + landing-page/components/ui/popover.tsx | 31 + landing-page/components/ui/progress.tsx | 28 + landing-page/components/ui/radio-group.tsx | 44 + landing-page/components/ui/resizable.tsx | 45 + landing-page/components/ui/scroll-area.tsx | 48 + landing-page/components/ui/select.tsx | 160 + landing-page/components/ui/separator.tsx | 31 + landing-page/components/ui/sheet.tsx | 140 + landing-page/components/ui/sidebar.tsx | 763 ++++ landing-page/components/ui/skeleton.tsx | 15 + landing-page/components/ui/slider.tsx | 28 + landing-page/components/ui/sonner.tsx | 31 + landing-page/components/ui/switch.tsx | 29 + landing-page/components/ui/table.tsx | 117 + landing-page/components/ui/tabs.tsx | 55 + landing-page/components/ui/textarea.tsx | 22 + landing-page/components/ui/toast.tsx | 129 + landing-page/components/ui/toaster.tsx | 35 + landing-page/components/ui/toggle-group.tsx | 61 + landing-page/components/ui/toggle.tsx | 45 + landing-page/components/ui/tooltip.tsx | 30 + landing-page/components/ui/use-mobile.tsx | 19 + landing-page/components/ui/use-toast.ts | 194 + landing-page/hooks/use-mobile.tsx | 19 + landing-page/hooks/use-toast.ts | 194 + landing-page/lib/utils.ts | 6 + landing-page/next.config.mjs | 14 + landing-page/package.json | 70 + landing-page/pnpm-lock.yaml | 3545 +++++++++++++++++ landing-page/postcss.config.mjs | 8 + landing-page/public/placeholder-logo.png | Bin 0 -> 568 bytes landing-page/public/placeholder-logo.svg | 1 + landing-page/public/placeholder-user.jpg | Bin 0 -> 1635 bytes landing-page/public/placeholder.jpg | Bin 0 -> 1064 bytes landing-page/public/placeholder.svg | 1 + landing-page/styles/globals.css | 94 + landing-page/tailwind.config.ts | 98 + landing-page/tsconfig.json | 27 + 81 files changed, 10062 insertions(+), 2 deletions(-) create mode 100644 landing-page/.gitignore create mode 100644 landing-page/app/components/community.tsx create mode 100644 landing-page/app/components/features.tsx create mode 100644 landing-page/app/components/final-cta.tsx create mode 100644 landing-page/app/components/footer.tsx create mode 100644 landing-page/app/components/header.tsx create mode 100644 landing-page/app/components/hero.tsx create mode 100644 landing-page/app/components/how-it-works.tsx create mode 100644 landing-page/app/components/pricing.tsx create mode 100644 landing-page/app/components/privacy.tsx create mode 100644 landing-page/app/globals.css create mode 100644 landing-page/app/layout.tsx create mode 100644 landing-page/app/page.tsx create mode 100644 landing-page/components.json create mode 100644 landing-page/components/theme-provider.tsx create mode 100644 landing-page/components/ui/accordion.tsx create mode 100644 landing-page/components/ui/alert-dialog.tsx create mode 100644 landing-page/components/ui/alert.tsx create mode 100644 landing-page/components/ui/aspect-ratio.tsx create mode 100644 landing-page/components/ui/avatar.tsx create mode 100644 landing-page/components/ui/badge.tsx create mode 100644 landing-page/components/ui/breadcrumb.tsx create mode 100644 landing-page/components/ui/button.tsx create mode 100644 landing-page/components/ui/calendar.tsx create mode 100644 landing-page/components/ui/card.tsx create mode 100644 landing-page/components/ui/carousel.tsx create mode 100644 landing-page/components/ui/chart.tsx create mode 100644 landing-page/components/ui/checkbox.tsx create mode 100644 landing-page/components/ui/collapsible.tsx create mode 100644 landing-page/components/ui/command.tsx create mode 100644 landing-page/components/ui/context-menu.tsx create mode 100644 landing-page/components/ui/dialog.tsx create mode 100644 landing-page/components/ui/drawer.tsx create mode 100644 landing-page/components/ui/dropdown-menu.tsx create mode 100644 landing-page/components/ui/form.tsx create mode 100644 landing-page/components/ui/hover-card.tsx create mode 100644 landing-page/components/ui/input-otp.tsx create mode 100644 landing-page/components/ui/input.tsx create mode 100644 landing-page/components/ui/label.tsx create mode 100644 landing-page/components/ui/menubar.tsx create mode 100644 landing-page/components/ui/navigation-menu.tsx create mode 100644 landing-page/components/ui/pagination.tsx create mode 100644 landing-page/components/ui/popover.tsx create mode 100644 landing-page/components/ui/progress.tsx create mode 100644 landing-page/components/ui/radio-group.tsx create mode 100644 landing-page/components/ui/resizable.tsx create mode 100644 landing-page/components/ui/scroll-area.tsx create mode 100644 landing-page/components/ui/select.tsx create mode 100644 landing-page/components/ui/separator.tsx create mode 100644 landing-page/components/ui/sheet.tsx create mode 100644 landing-page/components/ui/sidebar.tsx create mode 100644 landing-page/components/ui/skeleton.tsx create mode 100644 landing-page/components/ui/slider.tsx create mode 100644 landing-page/components/ui/sonner.tsx create mode 100644 landing-page/components/ui/switch.tsx create mode 100644 landing-page/components/ui/table.tsx create mode 100644 landing-page/components/ui/tabs.tsx create mode 100644 landing-page/components/ui/textarea.tsx create mode 100644 landing-page/components/ui/toast.tsx create mode 100644 landing-page/components/ui/toaster.tsx create mode 100644 landing-page/components/ui/toggle-group.tsx create mode 100644 landing-page/components/ui/toggle.tsx create mode 100644 landing-page/components/ui/tooltip.tsx create mode 100644 landing-page/components/ui/use-mobile.tsx create mode 100644 landing-page/components/ui/use-toast.ts create mode 100644 landing-page/hooks/use-mobile.tsx create mode 100644 landing-page/hooks/use-toast.ts create mode 100644 landing-page/lib/utils.ts create mode 100644 landing-page/next.config.mjs create mode 100644 landing-page/package.json create mode 100644 landing-page/pnpm-lock.yaml create mode 100644 landing-page/postcss.config.mjs create mode 100644 landing-page/public/placeholder-logo.png create mode 100644 landing-page/public/placeholder-logo.svg create mode 100644 landing-page/public/placeholder-user.jpg create mode 100644 landing-page/public/placeholder.jpg create mode 100644 landing-page/public/placeholder.svg create mode 100644 landing-page/styles/globals.css create mode 100644 landing-page/tailwind.config.ts create mode 100644 landing-page/tsconfig.json diff --git a/README.md b/README.md index 622a55d..5cd5097 100644 --- a/README.md +++ b/README.md @@ -21,13 +21,14 @@ Implemented: Todo: -- Auto migration of meeting files (incase of format changes) +- Markdown formatting +- Auto generate notes when recording is stopped +- Different note templates Later: - Cool recording indicator (dancing bars) - Connecting to your Google calendar -- Different note templates - AI chat for asking questions about a meeting - Ability to use different models - Ability to use different STT providers diff --git a/landing-page/.gitignore b/landing-page/.gitignore new file mode 100644 index 0000000..f650315 --- /dev/null +++ b/landing-page/.gitignore @@ -0,0 +1,27 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules + +# next.js +/.next/ +/out/ + +# production +/build + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files +.env* + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts \ No newline at end of file diff --git a/landing-page/app/components/community.tsx b/landing-page/app/components/community.tsx new file mode 100644 index 0000000..7567e22 --- /dev/null +++ b/landing-page/app/components/community.tsx @@ -0,0 +1,68 @@ +import { Button } from "@/components/ui/button" +import { Card, CardContent } from "@/components/ui/card" +import { Star, GitFork, Users, Heart } from "lucide-react" + +export default function Community() { + return ( +
+
+
+

Join the Open-Source Community

+

+ Meetingnotes is built by the community for the community. Star us on GitHub, contribute features, or report + issues. +

+
+ +
+ + + +

1.2k

+

GitHub Stars

+
+
+ + + + +

180

+

Forks

+
+
+ + + + +

50+

+

Contributors

+
+
+
+ +
+ + +
+ +
+ +

Help Build the Future

+

+ Add integrations, templates, or custom models! Every contribution makes Meetingnotes better for everyone. +

+
+ "As an open-source project, Meetingnotes is transparent and customizable—perfect for privacy-focused users." +
+ – Developer & Contributor +
+
+
+ ) +} diff --git a/landing-page/app/components/features.tsx b/landing-page/app/components/features.tsx new file mode 100644 index 0000000..250af79 --- /dev/null +++ b/landing-page/app/components/features.tsx @@ -0,0 +1,78 @@ +import { Card, CardContent } from "@/components/ui/card" +import { Mic, Lightbulb, Settings, FolderOpen } from "lucide-react" + +export default function Features() { + const features = [ + { + icon: , + title: "Live Transcription", + description: + "Records mic and system audio using Deepgram for real-time transcripts. No meeting bots required—transcribe directly from your computer's audio.", + image: "/placeholder.svg?height=200&width=300", + }, + { + icon: , + title: "AI-Enhanced Notes", + description: + "Generates summaries and highlights from transcripts plus your manual notes, powered by OpenAI. Get intelligent insights from every meeting.", + image: "/placeholder.svg?height=200&width=300", + }, + { + icon: , + title: "Customization & Privacy", + description: + "Edit system prompts, add personal blurbs, and store everything locally. Use your own API keys for complete control and privacy.", + image: "/placeholder.svg?height=200&width=300", + }, + { + icon: , + title: "Easy Management", + description: + "Search, delete, copy notes/transcripts, and auto-update the app. Organize your meeting history with powerful management tools.", + image: "/placeholder.svg?height=200&width=300", + }, + ] + + return ( +
+
+
+

Powerful Features, All Free and Open Source

+

+ Meetingnotes handles your meeting notes effortlessly, from live transcription to AI-generated summaries. + Customize it to fit your workflow, and contribute to make it even better. +

+
+ +
+ {features.map((feature, index) => ( + + +
+
{feature.icon}
+
+

{feature.title}

+

{feature.description}

+ {`${feature.title} +
+
+
+
+ ))} +
+ +
+

Coming Soon

+

+ Google Calendar integration, note templates, AI chat for questions, and more integrations (email, Slack). +

+

Contribute on GitHub to help build these features!

+
+
+
+ ) +} diff --git a/landing-page/app/components/final-cta.tsx b/landing-page/app/components/final-cta.tsx new file mode 100644 index 0000000..79084b9 --- /dev/null +++ b/landing-page/app/components/final-cta.tsx @@ -0,0 +1,60 @@ +import { Button } from "@/components/ui/button" +import { Download, Github, ArrowRight } from "lucide-react" + +export default function FinalCTA() { + return ( +
+
+

+ Ready for Smarter, +
+ Free Meeting Notes? +

+ +

+ Download Meetingnotes today and experience AI-powered notes without the cost. It's open source, so make it + your own. +

+ +
+ + +
+ +
+
+

100% Free Forever

+

+ No hidden costs, no subscriptions, no premium tiers. Just free, powerful meeting notes. +

+
+ +
+

Privacy First

+

+ Your data never leaves your device. Complete control over your meeting notes and transcripts. +

+
+ +
+

Open Source

+

+ Transparent, customizable, and community-driven. Contribute and help shape the future. +

+
+
+
+
+ ) +} diff --git a/landing-page/app/components/footer.tsx b/landing-page/app/components/footer.tsx new file mode 100644 index 0000000..8ddddb7 --- /dev/null +++ b/landing-page/app/components/footer.tsx @@ -0,0 +1,137 @@ +import { Github, Twitter, Mail, Heart } from "lucide-react" + +export default function Footer() { + return ( + + ) +} diff --git a/landing-page/app/components/header.tsx b/landing-page/app/components/header.tsx new file mode 100644 index 0000000..4d6c5fc --- /dev/null +++ b/landing-page/app/components/header.tsx @@ -0,0 +1,50 @@ +import { Button } from "@/components/ui/button" +import { Github, Download } from "lucide-react" + +export default function Header() { + return ( +
+
+
+
+ MN +
+ Meetingnotes +
+ + + +
+ + +
+
+
+ ) +} diff --git a/landing-page/app/components/hero.tsx b/landing-page/app/components/hero.tsx new file mode 100644 index 0000000..11d9214 --- /dev/null +++ b/landing-page/app/components/hero.tsx @@ -0,0 +1,95 @@ +import { Button } from "@/components/ui/button" +import { Badge } from "@/components/ui/badge" +import { Download, Github, Star, Shield, Code, Zap } from "lucide-react" + +export default function Hero() { + return ( +
+
+ + ✨ Open-Source Alternative to Granola – Completely Free! + + +

+ The Free, Open-Source +
+ AI Notetaker for Busy +
+ Engineers +

+ +

+ Meetingnotes records your meetings, creates live transcripts, and generates enhanced notes using AI—all on + your device, with zero costs. Bring your OpenAI API key. +

+ +
+
+ + 100% Free + No subscriptions, ever +
+
+ + 100% Private + All data stored locally +
+
+ + 100% Open Source + Contribute on GitHub +
+
+ +
+ + +
+ +
+
+
+
+
+ + + +
+

Demo Video

+

See Meetingnotes in action

+
+
+

+ Watch how Meetingnotes transcribes meetings and generates AI-enhanced notes in real-time +

+
+
+ +
+
+ + 1.2k stars on GitHub +
+ + 500+ downloads + + 50+ contributors +
+
+
+ ) +} diff --git a/landing-page/app/components/how-it-works.tsx b/landing-page/app/components/how-it-works.tsx new file mode 100644 index 0000000..6d092ba --- /dev/null +++ b/landing-page/app/components/how-it-works.tsx @@ -0,0 +1,67 @@ +import { Card, CardContent } from "@/components/ui/card" +import { Download, Key, Play, FileText } from "lucide-react" + +export default function HowItWorks() { + const steps = [ + { + number: "01", + icon: , + title: "Download & Setup", + description: + "Download and install on macOS. Enter your Deepgram and OpenAI API keys (stored securely on-device).", + }, + { + number: "02", + icon: , + title: "Start Recording", + description: "Start a meeting—Meetingnotes records audio and shows a live transcript in real-time.", + }, + { + number: "03", + icon: , + title: "Add Your Notes", + description: "Add your own notes during the call to complement the automatic transcription.", + }, + { + number: "04", + icon: , + title: "Get AI Summary", + description: "End the meeting—AI generates enhanced notes instantly. Edit, copy, or search later.", + }, + ] + + return ( +
+
+
+

How meetingnotes Works

+

+ Get started in minutes with our simple, privacy-first approach to meeting notes. +

+
+ +
+ {steps.map((step, index) => ( + + +
{step.number}
+
+ {step.icon} +
+

{step.title}

+

{step.description}

+
+
+ ))} +
+ +
+
+ "meetingnotes saves me hours every week—best part? It's free and open source!" +
+ – Open-Source Contributor +
+
+
+ ) +} diff --git a/landing-page/app/components/pricing.tsx b/landing-page/app/components/pricing.tsx new file mode 100644 index 0000000..d60bacf --- /dev/null +++ b/landing-page/app/components/pricing.tsx @@ -0,0 +1,140 @@ +import { Card, CardContent } from "@/components/ui/card" +import { Button } from "@/components/ui/button" +import { Check, DollarSign, Key, Shield } from "lucide-react" + +export default function Pricing() { + return ( +
+
+
+

Pricing That Makes Sense

+

+ No subscriptions, no hidden fees, no premium tiers. Just bring your own API keys and enjoy unlimited + AI-powered meeting notes. +

+
+ +
+ +
+ ✨ COMPLETELY FREE FOREVER +
+ + +
+
$0
+
Forever and always
+
+ +
+
+

What's Included

+
    +
  • + + Unlimited meeting recordings +
  • +
  • + + Real-time AI transcription +
  • +
  • + + AI-enhanced meeting summaries +
  • +
  • + + Complete data privacy (local storage) +
  • +
  • + + Full source code access +
  • +
  • + + Customizable AI prompts +
  • +
  • + + Regular updates and improvements +
  • +
+
+ +
+

How It Works

+
+
+ +
+

Bring Your Own API Keys

+

+ Use your Deepgram and OpenAI API keys. Pay only for what you use, directly to the providers. +

+
+
+ +
+ +
+

Your Keys, Your Control

+

+ API keys are stored securely on your device. We never see or store your credentials. +

+
+
+ +
+ +
+

Typical Costs

+

+ ~$0.28/hour using Deepgram Nova-3 and GPT-4o-mini. Pay only for what you use, no markup or + subscriptions. +

+
+
+
+
+
+ +
+

Compare to Alternatives

+
+
+
Meetingnotes
+
$0 + API costs
+
~$0.28/hour
+
+
+
Granola
+
$18/month
+
Subscription required
+
+
+
Fathom
+
$15/month
+
Limited features
+
+
+
Fireflies
+
$10/month
+
Usage limits
+
+
+
+ + + +

+ No credit card required • No account needed • No data collection +

+
+
+
+
+
+ ) +} diff --git a/landing-page/app/components/privacy.tsx b/landing-page/app/components/privacy.tsx new file mode 100644 index 0000000..7bcdb89 --- /dev/null +++ b/landing-page/app/components/privacy.tsx @@ -0,0 +1,70 @@ +import { Shield, Laptop, Lock } from "lucide-react" + +export default function Privacy() { + const platforms = [ + { name: "macOS", logo: "/placeholder.svg?height=40&width=40" }, + { name: "Google Meet", logo: "/placeholder.svg?height=40&width=40" }, + { name: "Zoom", logo: "/placeholder.svg?height=40&width=40" }, + { name: "Microsoft Teams", logo: "/placeholder.svg?height=40&width=40" }, + { name: "Slack", logo: "/placeholder.svg?height=40&width=40" }, + ] + + return ( +
+
+
+

Works on macOS with Full Privacy – No Bots, No Cloud

+

+ Meetingnotes transcribes directly from your computer's audio. No meeting bots join your calls, and all data + stays on your device. +

+
+ +
+ {platforms.map((platform, index) => ( +
+ {`${platform.name} + {platform.name} +
+ ))} +
+ +
+
+
+ +
+

Your Data, Your Control

+

+ Nothing leaves your machine. All transcripts and notes are stored locally on your device. +

+
+ +
+
+ +
+

No Meeting Bots

+

+ Records directly from your computer's audio without joining any bots to your meetings. +

+
+ +
+
+ +
+

Secure API Keys

+

+ Your Deepgram and OpenAI API keys are stored securely on your device, never shared. +

+
+
+
+
+ ) +} diff --git a/landing-page/app/globals.css b/landing-page/app/globals.css new file mode 100644 index 0000000..b7bd914 --- /dev/null +++ b/landing-page/app/globals.css @@ -0,0 +1,59 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + :root { + --background: 0 0% 0%; + --foreground: 0 0% 100%; + --card: 0 0% 5%; + --card-foreground: 0 0% 95%; + --popover: 0 0% 5%; + --popover-foreground: 0 0% 95%; + --primary: 217 91% 60%; + --primary-foreground: 0 0% 100%; + --secondary: 0 0% 15%; + --secondary-foreground: 0 0% 95%; + --muted: 0 0% 15%; + --muted-foreground: 0 0% 60%; + --accent: 0 0% 15%; + --accent-foreground: 0 0% 95%; + --destructive: 0 84% 60%; + --destructive-foreground: 0 0% 100%; + --border: 0 0% 20%; + --input: 0 0% 20%; + --ring: 217 91% 60%; + --radius: 0.5rem; + } +} + +@layer base { + * { + @apply border-border; + } + body { + @apply bg-background text-foreground; + } +} + +html { + scroll-behavior: smooth; +} + +/* Custom scrollbar for dark theme */ +::-webkit-scrollbar { + width: 8px; +} + +::-webkit-scrollbar-track { + background: #111111; +} + +::-webkit-scrollbar-thumb { + background: #333333; + border-radius: 4px; +} + +::-webkit-scrollbar-thumb:hover { + background: #555555; +} diff --git a/landing-page/app/layout.tsx b/landing-page/app/layout.tsx new file mode 100644 index 0000000..c216838 --- /dev/null +++ b/landing-page/app/layout.tsx @@ -0,0 +1,39 @@ +import type React from "react" +import type { Metadata } from "next" +import { Inter } from "next/font/google" +import "./globals.css" + +const inter = Inter({ subsets: ["latin"] }) + +export const metadata: Metadata = { + title: "Meetingnotes - Free Open-Source AI Meeting Notetaker", + description: + "Free, open-source macOS app for AI-powered meeting notes. Transcribe, summarize, and store everything locally with complete privacy. Created by Owen Gretzinger.", + keywords: "meeting notes, AI transcription, open source, privacy, macOS, free, Granola alternative, Owen Gretzinger", + authors: [{ name: "Owen Gretzinger" }], + openGraph: { + title: "Meetingnotes - Free Open-Source AI Meeting Notetaker", + description: + "Free, open-source macOS app for AI-powered meeting notes. Created by Owen Gretzinger for privacy-focused professionals.", + type: "website", + url: "https://meetingnotes.dev", + }, + twitter: { + card: "summary_large_image", + title: "Meetingnotes - Free Open-Source AI Meeting Notetaker", + description: "Free, open-source macOS app for AI-powered meeting notes by Owen Gretzinger.", + }, + generator: 'v0.dev' +} + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + {children} + + ) +} diff --git a/landing-page/app/page.tsx b/landing-page/app/page.tsx new file mode 100644 index 0000000..7c9dbd6 --- /dev/null +++ b/landing-page/app/page.tsx @@ -0,0 +1,25 @@ +import Header from "./components/header" +import Hero from "./components/hero" +import Features from "./components/features" +import HowItWorks from "./components/how-it-works" +import Privacy from "./components/privacy" +import Pricing from "./components/pricing" +import Community from "./components/community" +import FinalCTA from "./components/final-cta" +import Footer from "./components/footer" + +export default function Home() { + return ( +
+
+ + + + + + + +
+
+ ) +} diff --git a/landing-page/components.json b/landing-page/components.json new file mode 100644 index 0000000..d9ef0ae --- /dev/null +++ b/landing-page/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "default", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "tailwind.config.ts", + "css": "app/globals.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} \ No newline at end of file diff --git a/landing-page/components/theme-provider.tsx b/landing-page/components/theme-provider.tsx new file mode 100644 index 0000000..55c2f6e --- /dev/null +++ b/landing-page/components/theme-provider.tsx @@ -0,0 +1,11 @@ +'use client' + +import * as React from 'react' +import { + ThemeProvider as NextThemesProvider, + type ThemeProviderProps, +} from 'next-themes' + +export function ThemeProvider({ children, ...props }: ThemeProviderProps) { + return {children} +} diff --git a/landing-page/components/ui/accordion.tsx b/landing-page/components/ui/accordion.tsx new file mode 100644 index 0000000..24c788c --- /dev/null +++ b/landing-page/components/ui/accordion.tsx @@ -0,0 +1,58 @@ +"use client" + +import * as React from "react" +import * as AccordionPrimitive from "@radix-ui/react-accordion" +import { ChevronDown } from "lucide-react" + +import { cn } from "@/lib/utils" + +const Accordion = AccordionPrimitive.Root + +const AccordionItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AccordionItem.displayName = "AccordionItem" + +const AccordionTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + svg]:rotate-180", + className + )} + {...props} + > + {children} + + + +)) +AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName + +const AccordionContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + +
{children}
+
+)) + +AccordionContent.displayName = AccordionPrimitive.Content.displayName + +export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } diff --git a/landing-page/components/ui/alert-dialog.tsx b/landing-page/components/ui/alert-dialog.tsx new file mode 100644 index 0000000..25e7b47 --- /dev/null +++ b/landing-page/components/ui/alert-dialog.tsx @@ -0,0 +1,141 @@ +"use client" + +import * as React from "react" +import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog" + +import { cn } from "@/lib/utils" +import { buttonVariants } from "@/components/ui/button" + +const AlertDialog = AlertDialogPrimitive.Root + +const AlertDialogTrigger = AlertDialogPrimitive.Trigger + +const AlertDialogPortal = AlertDialogPrimitive.Portal + +const AlertDialogOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName + +const AlertDialogContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + + +)) +AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName + +const AlertDialogHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +AlertDialogHeader.displayName = "AlertDialogHeader" + +const AlertDialogFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +AlertDialogFooter.displayName = "AlertDialogFooter" + +const AlertDialogTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName + +const AlertDialogDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogDescription.displayName = + AlertDialogPrimitive.Description.displayName + +const AlertDialogAction = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName + +const AlertDialogCancel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName + +export { + AlertDialog, + AlertDialogPortal, + AlertDialogOverlay, + AlertDialogTrigger, + AlertDialogContent, + AlertDialogHeader, + AlertDialogFooter, + AlertDialogTitle, + AlertDialogDescription, + AlertDialogAction, + AlertDialogCancel, +} diff --git a/landing-page/components/ui/alert.tsx b/landing-page/components/ui/alert.tsx new file mode 100644 index 0000000..41fa7e0 --- /dev/null +++ b/landing-page/components/ui/alert.tsx @@ -0,0 +1,59 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const alertVariants = cva( + "relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground", + { + variants: { + variant: { + default: "bg-background text-foreground", + destructive: + "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +const Alert = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes & VariantProps +>(({ className, variant, ...props }, ref) => ( +
+)) +Alert.displayName = "Alert" + +const AlertTitle = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +AlertTitle.displayName = "AlertTitle" + +const AlertDescription = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +AlertDescription.displayName = "AlertDescription" + +export { Alert, AlertTitle, AlertDescription } diff --git a/landing-page/components/ui/aspect-ratio.tsx b/landing-page/components/ui/aspect-ratio.tsx new file mode 100644 index 0000000..d6a5226 --- /dev/null +++ b/landing-page/components/ui/aspect-ratio.tsx @@ -0,0 +1,7 @@ +"use client" + +import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio" + +const AspectRatio = AspectRatioPrimitive.Root + +export { AspectRatio } diff --git a/landing-page/components/ui/avatar.tsx b/landing-page/components/ui/avatar.tsx new file mode 100644 index 0000000..51e507b --- /dev/null +++ b/landing-page/components/ui/avatar.tsx @@ -0,0 +1,50 @@ +"use client" + +import * as React from "react" +import * as AvatarPrimitive from "@radix-ui/react-avatar" + +import { cn } from "@/lib/utils" + +const Avatar = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +Avatar.displayName = AvatarPrimitive.Root.displayName + +const AvatarImage = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AvatarImage.displayName = AvatarPrimitive.Image.displayName + +const AvatarFallback = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName + +export { Avatar, AvatarImage, AvatarFallback } diff --git a/landing-page/components/ui/badge.tsx b/landing-page/components/ui/badge.tsx new file mode 100644 index 0000000..f000e3e --- /dev/null +++ b/landing-page/components/ui/badge.tsx @@ -0,0 +1,36 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const badgeVariants = cva( + "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", + { + variants: { + variant: { + default: + "border-transparent bg-primary text-primary-foreground hover:bg-primary/80", + secondary: + "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80", + destructive: + "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80", + outline: "text-foreground", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +export interface BadgeProps + extends React.HTMLAttributes, + VariantProps {} + +function Badge({ className, variant, ...props }: BadgeProps) { + return ( +
+ ) +} + +export { Badge, badgeVariants } diff --git a/landing-page/components/ui/breadcrumb.tsx b/landing-page/components/ui/breadcrumb.tsx new file mode 100644 index 0000000..60e6c96 --- /dev/null +++ b/landing-page/components/ui/breadcrumb.tsx @@ -0,0 +1,115 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { ChevronRight, MoreHorizontal } from "lucide-react" + +import { cn } from "@/lib/utils" + +const Breadcrumb = React.forwardRef< + HTMLElement, + React.ComponentPropsWithoutRef<"nav"> & { + separator?: React.ReactNode + } +>(({ ...props }, ref) =>