mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
wip: Website Improvements (#8303)
Co-authored-by: Matt Rubens <mrubens@users.noreply.github.com>
This commit is contained in:
parent
8485548f53
commit
7fd01ab2a0
13 changed files with 722 additions and 6539 deletions
File diff suppressed because it is too large
Load diff
|
Before Width: | Height: | Size: 226 KiB After Width: | Height: | Size: 10 KiB |
File diff suppressed because it is too large
Load diff
|
Before Width: | Height: | Size: 226 KiB After Width: | Height: | Size: 10 KiB |
|
|
@ -3,7 +3,6 @@
|
|||
import { getVSCodeDownloads } from "@/lib/stats"
|
||||
|
||||
import { Button } from "@/components/ui"
|
||||
import { AnimatedText } from "@/components/animated-text"
|
||||
import {
|
||||
AnimatedBackground,
|
||||
InstallSection,
|
||||
|
|
@ -12,6 +11,8 @@ import {
|
|||
FAQSection,
|
||||
CodeExample,
|
||||
} from "@/components/homepage"
|
||||
import { EXTERNAL_LINKS } from "@/lib/constants"
|
||||
import { ArrowRight } from "lucide-react"
|
||||
|
||||
// Invalidate cache when a request comes in, at most once every hour.
|
||||
export const revalidate = 3600
|
||||
|
|
@ -21,28 +22,18 @@ export default async function Home() {
|
|||
|
||||
return (
|
||||
<>
|
||||
<section className="relative flex h-[calc(125vh-theme(spacing.12))] items-center overflow-hidden md:h-[calc(100svh-theme(spacing.12))] lg:h-[calc(100vh-theme(spacing.12))]">
|
||||
<section className="relative flex h-[calc(125vh-theme(spacing.12))] items-center overflow-hidden md:h-[calc(80svh-theme(spacing.12))]">
|
||||
<AnimatedBackground />
|
||||
<div className="container relative z-10 mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="grid gap-8 md:gap-12 lg:grid-cols-2 lg:gap-16">
|
||||
<div className="flex flex-col justify-center space-y-6 sm:space-y-8">
|
||||
<div className="container relative flex items-center h-full z-10 mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="grid h-full relative gap-8 md:gap-12 lg:grid-cols-2 lg:gap-16">
|
||||
<div className="flex flex-col px-4 justify-center space-y-6 sm:space-y-8">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold tracking-tight sm:text-4xl md:text-5xl lg:text-6xl">
|
||||
<span className="block">Your</span>
|
||||
<AnimatedText className="bg-gradient-to-r from-blue-400 to-cyan-400 bg-clip-text text-transparent">
|
||||
AI-Powered
|
||||
</AnimatedText>
|
||||
<span className="block">Dev Team, in Your Editor</span>
|
||||
<AnimatedText className="bg-gradient-to-r from-blue-400 to-cyan-400 bg-clip-text text-transparent">
|
||||
and Beyond
|
||||
</AnimatedText>
|
||||
<h1 className="text-3xl font-bold tracking-tight mt-8 sm:text-4xl md:text-5xl lg:text-6xl lg:mt-0">
|
||||
An entire AI-powered dev team. In your editor and beyond.
|
||||
</h1>
|
||||
<p className="mt-4 max-w-md text-base text-muted-foreground sm:mt-6 sm:text-lg">
|
||||
Supercharge your software development with AI that{" "}
|
||||
<AnimatedText className="bg-gradient-to-r from-blue-400 to-cyan-400 bg-clip-text text-transparent">
|
||||
understands your codebase
|
||||
</AnimatedText>{" "}
|
||||
and helps you write, refactor, and debug with ease in your editor and in the cloud.
|
||||
Roo's model-agnostic, specialized modes and fine-grained auto-approval controls
|
||||
give you the tools (and the confidence) to get AI working for you.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-col space-y-3 sm:flex-row sm:space-x-4 sm:space-y-0">
|
||||
|
|
@ -53,18 +44,8 @@ export default async function Home() {
|
|||
href="https://marketplace.visualstudio.com/items?itemName=RooVeterinaryInc.roo-cline"
|
||||
target="_blank"
|
||||
className="flex w-full items-center justify-center">
|
||||
Install Roo Code
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="ml-2 h-4 w-4"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M10.293 5.293a1 1 0 011.414 0l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414-1.414L12.586 11H5a1 1 0 110-2h7.586l-2.293-2.293a1 1 0 010-1.414z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
Install Extension
|
||||
<ArrowRight className="ml-2" />
|
||||
</a>
|
||||
</Button>
|
||||
<Button
|
||||
|
|
@ -72,24 +53,24 @@ export default async function Home() {
|
|||
size="lg"
|
||||
className="w-full sm:w-auto bg-white/20 dark:bg-white/10 backdrop-blur-sm border border-black/40 dark:border-white/30 hover:border-blue-400 hover:bg-white/30 dark:hover:bg-white/20 hover:shadow-[0_0_20px_rgba(59,130,246,0.5)] transition-all duration-300">
|
||||
<a
|
||||
href="https://roocode.com/enterprise"
|
||||
href={EXTERNAL_LINKS.CLOUD_APP_SIGNUP}
|
||||
target="_blank"
|
||||
className="flex w-full items-center justify-center">
|
||||
For Enterprise
|
||||
Get started with Cloud
|
||||
<ArrowRight className="ml-2" />
|
||||
</a>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="relative mt-8 flex items-center justify-center lg:mt-0">
|
||||
<div className="absolute inset-0 flex items-center justify-center">
|
||||
<div className="h-[250px] w-[250px] rounded-full bg-blue-500/20 blur-[100px] sm:h-[300px] sm:w-[300px] md:h-[350px] md:w-[350px]" />
|
||||
<div className="relative flex items-center mx-auto h-full mt-8 lg:mt-0">
|
||||
<div className="flex items-center justify-center">
|
||||
<CodeExample />
|
||||
</div>
|
||||
<CodeExample />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<div id="features">
|
||||
<div id="product">
|
||||
<Features />
|
||||
</div>
|
||||
<div id="testimonials">
|
||||
|
|
|
|||
328
apps/web-roo-code/src/app/pricing/page.tsx
Normal file
328
apps/web-roo-code/src/app/pricing/page.tsx
Normal file
|
|
@ -0,0 +1,328 @@
|
|||
import { Users, Building2, ArrowRight, Star, LucideIcon, Check, Cloud } from "lucide-react"
|
||||
import type { Metadata } from "next"
|
||||
import Link from "next/link"
|
||||
|
||||
import { Button } from "@/components/ui"
|
||||
import { AnimatedBackground } from "@/components/homepage"
|
||||
import { ContactForm } from "@/components/enterprise/contact-form"
|
||||
import { SEO } from "@/lib/seo"
|
||||
import { EXTERNAL_LINKS } from "@/lib/constants"
|
||||
|
||||
const TITLE = "Pricing - Roo Code Cloud"
|
||||
const DESCRIPTION =
|
||||
"Simple, transparent pricing for Roo Code Cloud. The VS Code extension is free forever. Choose the cloud plan that fits your needs."
|
||||
const PATH = "/pricing"
|
||||
const OG_IMAGE = SEO.ogImage
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: TITLE,
|
||||
description: DESCRIPTION,
|
||||
alternates: {
|
||||
canonical: `${SEO.url}${PATH}`,
|
||||
},
|
||||
openGraph: {
|
||||
title: TITLE,
|
||||
description: DESCRIPTION,
|
||||
url: `${SEO.url}${PATH}`,
|
||||
siteName: SEO.name,
|
||||
images: [
|
||||
{
|
||||
url: OG_IMAGE.url,
|
||||
width: OG_IMAGE.width,
|
||||
height: OG_IMAGE.height,
|
||||
alt: OG_IMAGE.alt,
|
||||
},
|
||||
],
|
||||
locale: SEO.locale,
|
||||
type: "website",
|
||||
},
|
||||
twitter: {
|
||||
card: SEO.twitterCard,
|
||||
title: TITLE,
|
||||
description: DESCRIPTION,
|
||||
images: [OG_IMAGE.url],
|
||||
},
|
||||
keywords: [
|
||||
...SEO.keywords,
|
||||
"pricing",
|
||||
"plans",
|
||||
"subscription",
|
||||
"cloud pricing",
|
||||
"AI development pricing",
|
||||
"team pricing",
|
||||
"enterprise pricing",
|
||||
],
|
||||
}
|
||||
|
||||
interface PricingTier {
|
||||
name: string
|
||||
icon: LucideIcon
|
||||
price: string
|
||||
period?: string
|
||||
trial?: string
|
||||
cancellation?: string
|
||||
description: string
|
||||
featuresIntro?: string
|
||||
features: string[]
|
||||
cta: {
|
||||
text: string
|
||||
href?: string
|
||||
isContactForm?: boolean
|
||||
}
|
||||
}
|
||||
|
||||
const pricingTiers: PricingTier[] = [
|
||||
{
|
||||
name: "Cloud Free",
|
||||
icon: Cloud,
|
||||
price: "$0",
|
||||
cancellation: "Cancel anytime",
|
||||
description: "For folks just getting started",
|
||||
features: [
|
||||
"Token usage analytics",
|
||||
"Access your task history across devices",
|
||||
"Follow your tasks from anywhere",
|
||||
"Community support",
|
||||
],
|
||||
cta: {
|
||||
text: "Get started",
|
||||
href: EXTERNAL_LINKS.CLOUD_APP_SIGNUP,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Pro",
|
||||
icon: Star,
|
||||
price: "$20",
|
||||
period: "/mo",
|
||||
trial: "Free 14-day trial · ",
|
||||
cancellation: "Cancel anytime",
|
||||
description: "For pro Roo coders",
|
||||
featuresIntro: "Everything in Free, plus:",
|
||||
features: [
|
||||
"Roomote Control",
|
||||
"Start, stop and control tasks from anywhere",
|
||||
"Course-correct Roo from afar",
|
||||
"Paid support",
|
||||
],
|
||||
cta: {
|
||||
text: "Get started",
|
||||
href: EXTERNAL_LINKS.CLOUD_APP_SIGNUP + "?redirect_url=/billing",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Team",
|
||||
icon: Users,
|
||||
price: "$99",
|
||||
period: "/mo",
|
||||
trial: "Free 14-day trial · ",
|
||||
cancellation: "Cancel anytime",
|
||||
description: "For AI-forward teams",
|
||||
featuresIntro: "Everything in Pro, plus:",
|
||||
features: ["Unlimited users (no per-seat cost)", "Shared configuration & policies", "Centralized billing"],
|
||||
cta: {
|
||||
text: "Get started",
|
||||
href: EXTERNAL_LINKS.CLOUD_APP_SIGNUP + "?redirect_url=/billing",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Enterprise",
|
||||
icon: Building2,
|
||||
price: "Custom",
|
||||
description: "For complex orgs",
|
||||
featuresIntro: "Everything in Team, plus:",
|
||||
features: [
|
||||
"SAML SSO provisioning",
|
||||
"Custom integrations and terms",
|
||||
"Security questionnaires and all that fun stuff",
|
||||
"Dedicated support",
|
||||
],
|
||||
cta: {
|
||||
text: "Talk to Sales",
|
||||
isContactForm: true,
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
export default function PricingPage() {
|
||||
return (
|
||||
<>
|
||||
<AnimatedBackground />
|
||||
|
||||
{/* Hero Section */}
|
||||
<section className="relative overflow-hidden pt-16 pb-12">
|
||||
<div className="container relative z-10 mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="text-center">
|
||||
<h1 className="text-5xl font-bold tracking-tight">Roo Code Cloud Pricing</h1>
|
||||
<p className="mx-auto mt-4 max-w-2xl text-lg text-muted-foreground">
|
||||
Simple, transparent pricing that scales with your needs.
|
||||
<br />
|
||||
Free 14-day trials to kick the tires.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Free Extension Notice */}
|
||||
<div className="mx-auto max-w-6xl">
|
||||
<div className="rounded-xl p-4 mb-8 text-center bg-gradient-to-r from-blue-500/10 via-cyan-500/10 to-purple-500/10 border border-blue-500/20 dark:border-white/20">
|
||||
<p className="text-center">
|
||||
<strong className="font-semibold">The Roo Code extension is free! </strong>
|
||||
Roo Code Cloud is an optional service which takes it to the next level.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Pricing Tiers */}
|
||||
<section className="">
|
||||
<div className="container mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="mx-auto grid max-w-6xl gap-4 lg:grid-cols-4">
|
||||
{pricingTiers.map((tier) => {
|
||||
const Icon = tier.icon
|
||||
return (
|
||||
<div
|
||||
key={tier.name}
|
||||
className="relative p-6 flex flex-col justify-start bg-background border rounded-2xl transition-all hover:shadow-lg">
|
||||
<div className="mb-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<h3 className="text-2xl font-bold tracking-tight">{tier.name}</h3>
|
||||
<Icon className="size-6" />
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground">{tier.description}</p>
|
||||
</div>
|
||||
|
||||
<div className="grow mb-8">
|
||||
<p className="text-sm text-muted-foreground font-light mb-2">
|
||||
{tier.featuresIntro}
|
||||
</p>
|
||||
<ul className="space-y-3 my-0">
|
||||
{tier.features.map((feature) => (
|
||||
<li key={feature} className="flex items-start gap-2">
|
||||
<Check className="mt-0.5 h-4 w-4 text-muted-foreground shrink-0" />
|
||||
<span className="text-sm">{feature}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<p className="text-2xl mt-0 mb-1 tracking-tight">
|
||||
<strong>{tier.price}</strong>
|
||||
{tier.period}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground mb-4">
|
||||
{tier.trial}
|
||||
{tier.cancellation}
|
||||
</p>
|
||||
|
||||
{tier.cta.isContactForm ? (
|
||||
<ContactForm
|
||||
formType="demo"
|
||||
buttonText={tier.cta.text}
|
||||
buttonClassName="w-full transition-all duration-300"
|
||||
/>
|
||||
) : (
|
||||
<Button size="lg" className="w-full transition-all duration-300" asChild>
|
||||
<Link href={tier.cta.href!} className="flex items-center justify-center">
|
||||
{tier.cta.text}
|
||||
</Link>
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Additional Information */}
|
||||
<section className="bg-background py-16 my-16 border-t border-b relative z-50">
|
||||
<div className="container mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="mx-auto max-w-3xl text-center">
|
||||
<h2 className="text-3xl font-bold tracking-tight sm:text-4xl">Frequently Asked Questions</h2>
|
||||
<p className="mt-4 text-lg text-muted-foreground">Got questions about our pricing?</p>
|
||||
</div>
|
||||
<div className="mx-auto mt-12 grid max-w-5xl gap-8 md:grid-cols-2">
|
||||
<div className="rounded-lg border border-border bg-card p-6">
|
||||
<h3 className="font-semibold">Wait, is Roo Code free or not?</h3>
|
||||
<p className="mt-2 text-sm text-muted-foreground">
|
||||
Yes! The Roo Code VS Code extension is open source and free forever. The extension acts
|
||||
as a powerful AI coding assistant right in your editor. These are the prices for Roo
|
||||
Code Cloud.
|
||||
</p>
|
||||
</div>
|
||||
<div className="rounded-lg border border-border bg-card p-6">
|
||||
<h3 className="font-semibold">Is there a free trial?</h3>
|
||||
<p className="mt-2 text-sm text-muted-foreground">
|
||||
Yes, all paid plans come with a 14-day free trial.
|
||||
</p>
|
||||
</div>
|
||||
<div className="rounded-lg border border-border bg-card p-6">
|
||||
<h3 className="font-semibold">Do I need a credit card for the free trial?</h3>
|
||||
<p className="mt-2 text-sm text-muted-foreground">
|
||||
Yes, but you won't be charged until your trial ends. You can cancel anytime with
|
||||
one click .
|
||||
</p>
|
||||
</div>
|
||||
<div className="rounded-lg border border-border bg-card p-6">
|
||||
<h3 className="font-semibold">What payment methods do you accept?</h3>
|
||||
<p className="mt-2 text-sm text-muted-foreground">
|
||||
We accept all major credit cards, debit cards, and can arrange invoice billing for
|
||||
Enterprise customers.
|
||||
</p>
|
||||
</div>
|
||||
<div className="rounded-lg border border-border bg-card p-6">
|
||||
<h3 className="font-semibold">Can I change plans anytime?</h3>
|
||||
<p className="mt-2 text-sm text-muted-foreground">
|
||||
Yes, you can upgrade or downgrade your plan at any time. Changes will be reflected in
|
||||
your next billing cycle.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-12 text-center">
|
||||
<p className="text-muted-foreground">
|
||||
Still have questions?{" "}
|
||||
<a
|
||||
href={EXTERNAL_LINKS.DISCORD}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="font-medium text-blue-500 hover:text-blue-600 dark:text-blue-400 dark:hover:text-blue-300">
|
||||
Join our Discord
|
||||
</a>{" "}
|
||||
or{" "}
|
||||
<Link
|
||||
href="/enterprise#contact"
|
||||
className="font-medium text-blue-500 hover:text-blue-600 dark:text-blue-400 dark:hover:text-blue-300">
|
||||
contact our sales team
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* CTA Section */}
|
||||
<section className="py-20">
|
||||
<div className="container mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="mx-auto max-w-4xl rounded-3xl border border-border/50 bg-gradient-to-br from-blue-500/5 via-cyan-500/5 to-purple-500/5 p-8 text-center shadow-2xl backdrop-blur-xl dark:border-white/20 dark:bg-gradient-to-br dark:from-gray-800 dark:via-gray-900 dark:to-black sm:p-12">
|
||||
<h2 className="mb-4 text-3xl font-bold tracking-tight sm:text-4xl">Try Roo Code Cloud now</h2>
|
||||
<p className="mx-auto mb-8 max-w-2xl text-lg text-muted-foreground">Code from anywhere.</p>
|
||||
<div className="flex flex-col justify-center space-y-4 sm:flex-row sm:space-x-4 sm:space-y-0">
|
||||
<Button
|
||||
size="lg"
|
||||
className="bg-black text-white hover:bg-gray-800 hover:shadow-lg hover:shadow-black/20 dark:bg-white dark:text-black dark:hover:bg-gray-200 dark:hover:shadow-white/20 transition-all duration-300"
|
||||
asChild>
|
||||
<a
|
||||
href={EXTERNAL_LINKS.CLOUD_APP_SIGNUP}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center justify-center">
|
||||
Create a free Cloud account
|
||||
<ArrowRight className="ml-2 h-4 w-4" />
|
||||
</a>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
@ -64,43 +64,18 @@ export function Footer() {
|
|||
<ul className="mt-6 space-y-4">
|
||||
<li>
|
||||
<ScrollButton
|
||||
targetId="features"
|
||||
targetId="product"
|
||||
className="text-sm leading-6 text-muted-foreground transition-colors hover:text-foreground">
|
||||
Features
|
||||
</ScrollButton>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/enterprise"
|
||||
className="text-sm leading-6 text-muted-foreground transition-colors hover:text-foreground">
|
||||
Enterprise
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href={EXTERNAL_LINKS.EVALS}
|
||||
href={EXTERNAL_LINKS.DOCUMENTATION}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-sm leading-6 text-muted-foreground transition-colors hover:text-foreground">
|
||||
Evals
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href={EXTERNAL_LINKS.SECURITY}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-sm leading-6 text-muted-foreground transition-colors hover:text-foreground">
|
||||
Security
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href={EXTERNAL_LINKS.INTEGRATIONS}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-sm leading-6 text-muted-foreground transition-colors hover:text-foreground">
|
||||
Integrations
|
||||
Docs
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
|
|
@ -112,11 +87,45 @@ export function Footer() {
|
|||
Changelog
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href={EXTERNAL_LINKS.TESTIMONIALS}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-sm leading-6 text-muted-foreground transition-colors hover:text-foreground">
|
||||
Testimonials
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/enterprise"
|
||||
className="text-sm leading-6 text-muted-foreground transition-colors hover:text-foreground">
|
||||
Enterprise
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href={EXTERNAL_LINKS.SECURITY}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-sm leading-6 text-muted-foreground transition-colors hover:text-foreground">
|
||||
Security Center
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="mt-10 md:mt-0">
|
||||
<h3 className="text-sm font-semibold uppercase leading-6 text-foreground">Resources</h3>
|
||||
<ul className="mt-6 space-y-4">
|
||||
<li>
|
||||
<a
|
||||
href={EXTERNAL_LINKS.EVALS}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-sm leading-6 text-muted-foreground transition-colors hover:text-foreground">
|
||||
Evals
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href={EXTERNAL_LINKS.FAQ}
|
||||
|
|
@ -126,15 +135,6 @@ export function Footer() {
|
|||
FAQ
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href={EXTERNAL_LINKS.DOCUMENTATION}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-sm leading-6 text-muted-foreground transition-colors hover:text-foreground">
|
||||
Docs
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href={EXTERNAL_LINKS.TUTORIALS}
|
||||
|
|
@ -194,24 +194,6 @@ export function Footer() {
|
|||
Careers
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href={EXTERNAL_LINKS.BLOG}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-sm leading-6 text-muted-foreground transition-colors hover:text-foreground">
|
||||
Blog
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href={EXTERNAL_LINKS.TESTIMONIALS}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-sm leading-6 text-muted-foreground transition-colors hover:text-foreground">
|
||||
Testimonials
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/terms"
|
||||
|
|
|
|||
|
|
@ -8,12 +8,12 @@ import { useState } from "react"
|
|||
import { RxGithubLogo } from "react-icons/rx"
|
||||
import { VscVscode } from "react-icons/vsc"
|
||||
import { HiMenu } from "react-icons/hi"
|
||||
import { IoClose } from "react-icons/io5"
|
||||
|
||||
import { EXTERNAL_LINKS } from "@/lib/constants"
|
||||
import { useLogoSrc } from "@/lib/hooks/use-logo-src"
|
||||
import { ScrollButton } from "@/components/ui"
|
||||
import ThemeToggle from "@/components/chromes/theme-toggle"
|
||||
import { ChevronDown, Cloud, X } from "lucide-react"
|
||||
|
||||
interface NavBarProps {
|
||||
stars: string | null
|
||||
|
|
@ -26,56 +26,66 @@ export function NavBar({ stars, downloads }: NavBarProps) {
|
|||
|
||||
return (
|
||||
<header className="sticky top-0 z-50 border-b border-border bg-background/80 backdrop-blur-md">
|
||||
<div className="container mx-auto flex h-16 items-center justify-between px-4 sm:px-6 lg:px-8">
|
||||
<div className="container flex h-16 items-center justify-between px-4 sm:px-6 lg:px-8">
|
||||
<div className="flex items-center">
|
||||
<Link href="/" className="flex items-center">
|
||||
<Image src={logoSrc} alt="Roo Code Logo" width={120} height={40} className="h-8 w-auto" />
|
||||
<Image src={logoSrc} alt="Roo Code Logo" width={130} height={24} className="h-[24px] w-auto" />
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Desktop Navigation */}
|
||||
<nav className="hidden text-sm font-medium md:flex md:items-center md:space-x-3 xl:space-x-8">
|
||||
{/* note: features and testimonials links are hidden for screen sizes smaller than lg */}
|
||||
<nav className="grow ml-6 hidden text-sm font-medium md:flex md:items-center">
|
||||
<ScrollButton
|
||||
targetId="features"
|
||||
className="text-muted-foreground transition-transform duration-200 hover:scale-105 hover:text-foreground max-lg:hidden">
|
||||
Features
|
||||
targetId="product"
|
||||
className="text-muted-foreground px-4 py-6 transition-transform duration-200 hover:scale-105 hover:text-foreground max-lg:hidden">
|
||||
Product
|
||||
</ScrollButton>
|
||||
<ScrollButton
|
||||
targetId="testimonials"
|
||||
className="text-muted-foreground transition-transform duration-200 hover:scale-105 hover:text-foreground max-lg:hidden">
|
||||
Testimonials
|
||||
</ScrollButton>
|
||||
<Link
|
||||
href="/evals"
|
||||
className="text-muted-foreground transition-transform duration-200 hover:scale-105 hover:text-foreground">
|
||||
Evals
|
||||
</Link>
|
||||
<Link
|
||||
href="/enterprise"
|
||||
className="text-muted-foreground transition-transform duration-200 hover:scale-105 hover:text-foreground">
|
||||
Enterprise
|
||||
</Link>
|
||||
<a
|
||||
href={EXTERNAL_LINKS.DOCUMENTATION}
|
||||
target="_blank"
|
||||
className="text-muted-foreground transition-transform duration-200 hover:scale-105 hover:text-foreground">
|
||||
className="text-muted-foreground px-4 py-6 transition-transform duration-200 hover:scale-105 hover:text-foreground">
|
||||
Docs
|
||||
</a>
|
||||
<a
|
||||
href={EXTERNAL_LINKS.DISCORD}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-muted-foreground transition-transform duration-200 hover:scale-105 hover:text-foreground">
|
||||
Community
|
||||
</a>
|
||||
<a
|
||||
href={EXTERNAL_LINKS.CLOUD_APP}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-muted-foreground transition-transform duration-200 hover:scale-105 hover:text-foreground">
|
||||
Cloud
|
||||
</a>
|
||||
<Link
|
||||
href="/pricing"
|
||||
className="text-muted-foreground px-4 py-6 transition-transform duration-200 hover:scale-105 hover:text-foreground">
|
||||
Pricing
|
||||
</Link>
|
||||
{/* Resources Dropdown */}
|
||||
<div className="relative group">
|
||||
<button className="flex items-center px-4 py-6 gap-1 text-muted-foreground transition-transform duration-200 hover:scale-105 hover:text-foreground">
|
||||
Resources
|
||||
<ChevronDown className="size-3" />
|
||||
</button>
|
||||
{/* Dropdown Menu */}
|
||||
<div className="absolute left-0 top-12 mt-2 w-40 rounded-md border border-border bg-background py-1 shadow-lg opacity-0 -translate-y-2 pointer-events-none group-hover:opacity-100 group-hover:translate-y-0 group-hover:pointer-events-auto transition-all duration-200">
|
||||
<ScrollButton
|
||||
targetId="faq"
|
||||
className="block px-4 py-2 text-sm text-muted-foreground transition-colors hover:bg-accent hover:text-foreground">
|
||||
FAQ
|
||||
</ScrollButton>
|
||||
<Link
|
||||
href="/evals"
|
||||
className="block px-4 py-2 text-sm text-muted-foreground transition-colors hover:bg-accent hover:text-foreground">
|
||||
Evals
|
||||
</Link>
|
||||
<a
|
||||
href={EXTERNAL_LINKS.DISCORD}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="block px-4 py-2 text-sm text-muted-foreground transition-colors hover:bg-accent hover:text-foreground">
|
||||
Discord
|
||||
</a>
|
||||
<a
|
||||
href={EXTERNAL_LINKS.SECURITY}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="block px-4 py-2 text-sm text-muted-foreground transition-colors hover:bg-accent hover:text-foreground"
|
||||
onClick={() => setIsMenuOpen(false)}>
|
||||
Trust Center
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div className="hidden md:flex md:items-center md:space-x-4">
|
||||
|
|
@ -92,108 +102,128 @@ export function NavBar({ stars, downloads }: NavBarProps) {
|
|||
<Link
|
||||
href={EXTERNAL_LINKS.MARKETPLACE}
|
||||
target="_blank"
|
||||
className="hidden items-center gap-1.5 rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground transition-all duration-200 hover:bg-primary/80 hover:shadow-lg hover:scale-105 md:flex">
|
||||
className="hidden items-center gap-1.5 rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground transition-all duration-200 hover:shadow-lg hover:scale-105 md:flex">
|
||||
<VscVscode className="-mr-[2px] mt-[1px] h-4 w-4" />
|
||||
<span>
|
||||
Install <span className="font-black max-lg:text-xs">·</span>
|
||||
</span>
|
||||
{downloads !== null && <span>{downloads}</span>}
|
||||
</Link>
|
||||
<a
|
||||
href={EXTERNAL_LINKS.CLOUD_APP_LOGIN}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="hidden items-center gap-1.5 rounded-md py-2 text-sm border border-primary-background px-4 font-medium text-primary-background transition-all duration-200 hover:shadow-lg hover:scale-105 md:flex">
|
||||
<Cloud className="inline h-4 w-4" />
|
||||
Log in
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{/* Mobile Menu Button */}
|
||||
<button
|
||||
aria-expanded={isMenuOpen}
|
||||
onClick={() => setIsMenuOpen(!isMenuOpen)}
|
||||
className="flex items-center justify-center rounded-full p-2 transition-colors hover:bg-accent md:hidden"
|
||||
className="relative z-10 flex items-center justify-center rounded-full p-2 transition-colors hover:bg-accent md:hidden"
|
||||
aria-label="Toggle mobile menu">
|
||||
{isMenuOpen ? <IoClose className="h-6 w-6" /> : <HiMenu className="h-6 w-6" />}
|
||||
<HiMenu className={`h-6 w-6 ${isMenuOpen ? "hidden" : "block"}`} />
|
||||
<X className={`h-6 w-6 ${isMenuOpen ? "block" : "hidden"}`} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Mobile Menu Panel */}
|
||||
{/* Mobile Menu Panel - Full Screen */}
|
||||
<div
|
||||
className={`absolute left-0 right-0 top-16 z-50 transform border-b border-border bg-background shadow-lg backdrop-blur-none transition-all duration-200 md:hidden ${isMenuOpen ? "translate-y-0 opacity-100" : "pointer-events-none -translate-y-2 opacity-0"}`}>
|
||||
<nav className="flex flex-col py-2">
|
||||
<ScrollButton
|
||||
targetId="features"
|
||||
className="w-full px-8 py-3 text-left text-sm font-medium text-foreground/80 transition-colors hover:bg-accent hover:text-foreground"
|
||||
onClick={() => setIsMenuOpen(false)}>
|
||||
Features
|
||||
</ScrollButton>
|
||||
<ScrollButton
|
||||
targetId="testimonials"
|
||||
className="w-full px-8 py-3 text-left text-sm font-medium text-foreground/80 transition-colors hover:bg-accent hover:text-foreground"
|
||||
onClick={() => setIsMenuOpen(false)}>
|
||||
Testimonials
|
||||
</ScrollButton>
|
||||
<Link
|
||||
href="/evals"
|
||||
className="w-full px-8 py-3 text-left text-sm font-medium text-foreground/80 transition-colors hover:bg-accent hover:text-foreground"
|
||||
onClick={() => setIsMenuOpen(false)}>
|
||||
Evals
|
||||
</Link>
|
||||
<Link
|
||||
href="/enterprise"
|
||||
className="w-full px-8 py-3 text-left text-sm font-medium text-foreground/80 transition-colors hover:bg-accent hover:text-foreground"
|
||||
onClick={() => setIsMenuOpen(false)}>
|
||||
Enterprise
|
||||
</Link>
|
||||
<a
|
||||
href={EXTERNAL_LINKS.SECURITY}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="w-full px-8 py-3 text-left text-sm font-medium text-foreground/80 transition-colors hover:bg-accent hover:text-foreground"
|
||||
onClick={() => setIsMenuOpen(false)}>
|
||||
Security
|
||||
</a>
|
||||
<a
|
||||
href={EXTERNAL_LINKS.DOCUMENTATION}
|
||||
target="_blank"
|
||||
className="w-full px-8 py-3 text-left text-sm font-medium text-foreground/80 transition-colors hover:bg-accent hover:text-foreground"
|
||||
onClick={() => setIsMenuOpen(false)}>
|
||||
Docs
|
||||
</a>
|
||||
<a
|
||||
href={EXTERNAL_LINKS.DISCORD}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="w-full px-8 py-3 text-left text-sm font-medium text-foreground/80 transition-colors hover:bg-accent hover:text-foreground"
|
||||
onClick={() => setIsMenuOpen(false)}>
|
||||
Community
|
||||
</a>
|
||||
<a
|
||||
href={EXTERNAL_LINKS.CLOUD_APP}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="w-full px-8 py-3 text-left text-sm font-medium text-foreground/80 transition-colors hover:bg-accent hover:text-foreground"
|
||||
onClick={() => setIsMenuOpen(false)}>
|
||||
Cloud
|
||||
</a>
|
||||
|
||||
<hr className="mx-8 my-2 border-t border-border/50" />
|
||||
|
||||
{/* Icons & Stats */}
|
||||
<div className="flex items-center justify-center gap-8 px-8 py-3">
|
||||
<Link
|
||||
href={EXTERNAL_LINKS.GITHUB}
|
||||
target="_blank"
|
||||
className="inline-flex items-center gap-2 rounded-md p-2 text-sm font-medium text-foreground/80 transition-colors hover:bg-accent hover:text-foreground"
|
||||
className={`fixed top-16 left-0 bg-background right-0 z-[100] transition-all duration-200 pointer-events-none md:hidden ${isMenuOpen ? "block h-dvh" : "hidden"}`}>
|
||||
<nav className="flex flex-col justify-between h-full pb-16 overflow-y-auto bg-background pointer-events-auto">
|
||||
{/* Main navigation items */}
|
||||
<div className="grow-1 py-4 font-semibold text-lg">
|
||||
<ScrollButton
|
||||
targetId="product"
|
||||
className="block w-full p-5 py-3 text-left text-foreground active:opacity-50"
|
||||
onClick={() => setIsMenuOpen(false)}>
|
||||
<RxGithubLogo className="h-5 w-5" />
|
||||
{stars !== null && <span>{stars}</span>}
|
||||
Product
|
||||
</ScrollButton>
|
||||
<a
|
||||
href={EXTERNAL_LINKS.DOCUMENTATION}
|
||||
target="_blank"
|
||||
className="block w-full p-5 text-left text-foreground active:opacity-50"
|
||||
onClick={() => setIsMenuOpen(false)}>
|
||||
Docs
|
||||
</a>
|
||||
<Link
|
||||
href="/pricing"
|
||||
className="block w-full p-5 text-left text-foreground active:opacity-50"
|
||||
onClick={() => setIsMenuOpen(false)}>
|
||||
Pricing
|
||||
</Link>
|
||||
<div className="flex items-center rounded-md p-2 transition-colors hover:bg-accent">
|
||||
<ThemeToggle />
|
||||
|
||||
{/* Resources Section */}
|
||||
<div className="mt-4 w-full">
|
||||
<div className="px-5 pb-2 pt-4 text-sm font-semibold uppercase tracking-wider text-muted-foreground">
|
||||
Resources
|
||||
</div>
|
||||
<ScrollButton
|
||||
targetId="faq"
|
||||
className="block w-full p-5 py-3 text-left text-foreground active:opacity-50"
|
||||
onClick={() => setIsMenuOpen(false)}>
|
||||
FAQ
|
||||
</ScrollButton>
|
||||
<Link
|
||||
href="/evals"
|
||||
className="block w-full p-5 py-3 text-left text-foreground active:opacity-50"
|
||||
onClick={() => setIsMenuOpen(false)}>
|
||||
Evals
|
||||
</Link>
|
||||
<a
|
||||
href={EXTERNAL_LINKS.DISCORD}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="block w-full p-5 py-3 text-left text-foreground active:opacity-50"
|
||||
onClick={() => setIsMenuOpen(false)}>
|
||||
Discord
|
||||
</a>
|
||||
<a
|
||||
href={EXTERNAL_LINKS.SECURITY}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="block w-full p-5 py-3 text-left text-foreground active:opacity-50"
|
||||
onClick={() => setIsMenuOpen(false)}>
|
||||
Security Center
|
||||
</a>
|
||||
</div>
|
||||
<Link
|
||||
href={EXTERNAL_LINKS.MARKETPLACE}
|
||||
</div>
|
||||
|
||||
{/* Bottom section with Cloud Login and stats */}
|
||||
<div className="border-t border-border">
|
||||
<div className="flex items-center justify-around px-6 pt-2">
|
||||
<Link
|
||||
href={EXTERNAL_LINKS.GITHUB}
|
||||
target="_blank"
|
||||
className="inline-flex items-center gap-2 rounded-md p-3 text-sm font-medium text-muted-foreground transition-colors hover:bg-accent hover:text-foreground"
|
||||
onClick={() => setIsMenuOpen(false)}>
|
||||
<RxGithubLogo className="h-6 w-6" />
|
||||
{stars !== null && <span>{stars}</span>}
|
||||
</Link>
|
||||
<div className="flex items-center rounded-md p-3 transition-colors hover:bg-accent">
|
||||
<ThemeToggle />
|
||||
</div>
|
||||
<Link
|
||||
href={EXTERNAL_LINKS.MARKETPLACE}
|
||||
target="_blank"
|
||||
className="inline-flex items-center gap-2 rounded-md p-3 text-sm font-medium text-muted-foreground transition-colors hover:bg-accent hover:text-foreground"
|
||||
onClick={() => setIsMenuOpen(false)}>
|
||||
<VscVscode className="h-6 w-6" />
|
||||
{downloads !== null && <span>{downloads}</span>}
|
||||
</Link>
|
||||
</div>
|
||||
<a
|
||||
href={EXTERNAL_LINKS.CLOUD_APP_LOGIN}
|
||||
target="_blank"
|
||||
className="inline-flex items-center gap-2 rounded-md p-2 text-sm font-medium text-foreground/80 transition-colors hover:bg-accent hover:text-foreground"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center justify-center gap-2 rounded-lg border border-primary bg-background p-4 mx-4 mb-4 text-base font-semibold text-primary"
|
||||
onClick={() => setIsMenuOpen(false)}>
|
||||
<VscVscode className="h-5 w-5" />
|
||||
{downloads !== null && <span>{downloads}</span>}
|
||||
</Link>
|
||||
<Cloud className="h-5 w-5" />
|
||||
Log in
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,113 +0,0 @@
|
|||
"use client"
|
||||
|
||||
import { useEffect, useState, useCallback } from "react"
|
||||
import useEmblaCarousel from "embla-carousel-react"
|
||||
import Autoplay from "embla-carousel-autoplay"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { ChevronLeft, ChevronRight } from "lucide-react"
|
||||
import { features } from "@/components/homepage/features"
|
||||
|
||||
export function FeaturesMobile() {
|
||||
// configure autoplay with Embla
|
||||
const autoplayPlugin = Autoplay({
|
||||
delay: 5000,
|
||||
stopOnInteraction: true,
|
||||
stopOnMouseEnter: true,
|
||||
rootNode: (emblaRoot) => emblaRoot,
|
||||
})
|
||||
|
||||
const [emblaRef, emblaApi] = useEmblaCarousel(
|
||||
{
|
||||
loop: true,
|
||||
containScroll: "trimSnaps",
|
||||
},
|
||||
[autoplayPlugin],
|
||||
)
|
||||
|
||||
const [selectedIndex, setSelectedIndex] = useState(0)
|
||||
const [scrollSnaps, setScrollSnaps] = useState<number[]>([])
|
||||
|
||||
const scrollTo = useCallback((index: number) => emblaApi && emblaApi.scrollTo(index), [emblaApi])
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
const onInit = useCallback((emblaApi: any) => {
|
||||
setScrollSnaps(emblaApi.scrollSnapList())
|
||||
}, [])
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
const onSelect = useCallback((emblaApi: any) => {
|
||||
setSelectedIndex(emblaApi.selectedScrollSnap())
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (!emblaApi) return
|
||||
|
||||
onInit(emblaApi)
|
||||
onSelect(emblaApi)
|
||||
emblaApi.on("reInit", onInit)
|
||||
emblaApi.on("select", onSelect)
|
||||
|
||||
return () => {
|
||||
emblaApi.off("reInit", onInit)
|
||||
emblaApi.off("select", onSelect)
|
||||
}
|
||||
}, [emblaApi, onInit, onSelect])
|
||||
|
||||
return (
|
||||
<div className="md:hidden">
|
||||
<div className="relative px-4">
|
||||
<div className="overflow-hidden" ref={emblaRef}>
|
||||
<div className="flex">
|
||||
{features.map((feature, index) => (
|
||||
<div className="flex min-w-0 flex-[0_0_100%] px-4" key={index}>
|
||||
<div className="relative h-full min-h-[280px] rounded-2xl border border-border/50 bg-background/30 p-6 backdrop-blur-xl transition-all duration-300 hover:border-border hover:bg-background/50 dark:hover:border-border/80 dark:hover:bg-background/40">
|
||||
<div className="mb-2 inline-flex items-center justify-center rounded-xl bg-gradient-to-r from-blue-500/10 to-cyan-500/10 p-2.5 dark:from-blue-500/20 dark:to-cyan-500/20">
|
||||
<div className="rounded-lg bg-gradient-to-r from-blue-500/80 to-cyan-500/80 p-2.5">
|
||||
<div className="text-white">{feature.icon}</div>
|
||||
</div>
|
||||
</div>
|
||||
<h3 className="mb-3 text-xl font-medium text-foreground/90">{feature.title}</h3>
|
||||
<p className="leading-relaxed text-muted-foreground">{feature.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Navigation Controls */}
|
||||
<div className="mt-6 flex items-center justify-between px-4">
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className="h-8 w-8 rounded-full border-border/50 bg-background/80 hover:bg-background"
|
||||
onClick={() => emblaApi?.scrollPrev()}>
|
||||
<ChevronLeft className="h-4 w-4 text-foreground/80" />
|
||||
<span className="sr-only">Previous slide</span>
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className="h-8 w-8 rounded-full border-border/50 bg-background/80 hover:bg-background"
|
||||
onClick={() => emblaApi?.scrollNext()}>
|
||||
<ChevronRight className="h-4 w-4 text-foreground/80" />
|
||||
<span className="sr-only">Next slide</span>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2">
|
||||
{scrollSnaps.map((_, index) => (
|
||||
<button
|
||||
key={index}
|
||||
type="button"
|
||||
className={`h-3 w-3 rounded-full border border-border p-0 ${index === selectedIndex ? "bg-foreground" : "bg-background"}`}
|
||||
onClick={() => scrollTo(index)}
|
||||
aria-label={`Go to slide ${index + 1}`}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -1,71 +1,48 @@
|
|||
"use client"
|
||||
|
||||
import { motion } from "framer-motion"
|
||||
import { Bot, Code, Brain, Wrench, Terminal, Puzzle, Globe, Shield, Zap } from "lucide-react"
|
||||
import { FeaturesMobile } from "./features-mobile"
|
||||
|
||||
import { ReactNode } from "react"
|
||||
import { Brain, Shield, Users2, ReplaceAll, Keyboard, LucideIcon, CheckCheck } from "lucide-react"
|
||||
|
||||
export interface Feature {
|
||||
icon: ReactNode
|
||||
icon: LucideIcon
|
||||
title: string
|
||||
description: string
|
||||
}
|
||||
|
||||
export const features: Feature[] = [
|
||||
{
|
||||
icon: <Bot className="h-6 w-6 text-white" />,
|
||||
title: "Your AI Dev Team in VS Code",
|
||||
icon: Users2,
|
||||
title: "Specialized Modes",
|
||||
description:
|
||||
"Roo Code puts a team of agentic AI assistants directly in your editor, with the power to plan, write, and fix code across multiple files.",
|
||||
"Planning, Architecture, Debugging and beyond: Roo's modes stay on-task and deliver. Create your own modes or download from the marketplace.",
|
||||
},
|
||||
{
|
||||
icon: <Code className="h-6 w-6 text-white" />,
|
||||
title: "Multiple Specialized Modes",
|
||||
description:
|
||||
"From coding to debugging to architecture, Roo Code has a mode for every dev scenario—just switch on the fly.",
|
||||
icon: ReplaceAll,
|
||||
title: "Model-Agnostic",
|
||||
description: "Bring your own model key or use local inference — no markup, lock-in, no restrictions.",
|
||||
},
|
||||
{
|
||||
icon: <Brain className="h-6 w-6 text-white" />,
|
||||
icon: CheckCheck,
|
||||
title: "Granular auto-approval",
|
||||
description: "Control each action and make Roo as autonomous as you want as you build confidence. Or go YOLO.",
|
||||
},
|
||||
{
|
||||
icon: Keyboard,
|
||||
title: "Highly Customizable",
|
||||
description:
|
||||
"Fine-tune settings for Roo to work for you, like inference context, model properties, slash commands and more.",
|
||||
},
|
||||
{
|
||||
icon: Brain,
|
||||
title: "Deep Project-wide Context",
|
||||
description:
|
||||
"Roo Code reads your entire codebase, preserving valid code through diff-based edits for seamless multi-file refactors.",
|
||||
},
|
||||
{
|
||||
icon: <Wrench className="h-6 w-6 text-white" />,
|
||||
title: "Open-Source and Model-Agnostic",
|
||||
icon: Shield,
|
||||
title: "Secure and Private by Design",
|
||||
description:
|
||||
"Bring your own model or use local AI—no vendor lock-in. Roo Code is free, open, and adaptable to your needs.",
|
||||
},
|
||||
{
|
||||
icon: <Terminal className="h-6 w-6 text-white" />,
|
||||
title: "Guarded Command Execution",
|
||||
description:
|
||||
"Approve or deny commands as needed. Roo Code automates your dev workflow while keeping oversight firmly in your hands.",
|
||||
},
|
||||
{
|
||||
icon: <Puzzle className="h-6 w-6 text-white" />,
|
||||
title: "Fully Customizable",
|
||||
description:
|
||||
"Create or tweak modes, define usage rules, and shape Roo Code's behavior precisely—your code, your way.",
|
||||
},
|
||||
{
|
||||
icon: <Globe className="h-6 w-6 text-white" />,
|
||||
title: "Automated Browser Actions",
|
||||
description:
|
||||
"Seamlessly test and verify your web app directly from VS Code—Roo Code can open a browser, run checks, and more.",
|
||||
},
|
||||
{
|
||||
icon: <Shield className="h-6 w-6 text-white" />,
|
||||
title: "Secure by Design",
|
||||
description:
|
||||
"Security-first from the ground up, Roo Code meets rigorous standards without slowing you down. Monitoring and strict policies keep your code safe at scale.",
|
||||
},
|
||||
{
|
||||
icon: <Zap className="h-6 w-6 text-white" />,
|
||||
title: "Seamless Setup and Workflows",
|
||||
description:
|
||||
"Get started in minutes—no heavy configs. Roo Code fits alongside your existing tools and dev flow, while supercharging your productivity.",
|
||||
"Open source and local-first. No code leaves your machine unless you say so. SOC 2 Type II compliant.",
|
||||
},
|
||||
]
|
||||
|
||||
|
|
@ -81,21 +58,6 @@ export function Features() {
|
|||
},
|
||||
}
|
||||
|
||||
const itemVariants = {
|
||||
hidden: {
|
||||
opacity: 0,
|
||||
y: 20,
|
||||
},
|
||||
visible: {
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
transition: {
|
||||
duration: 0.6,
|
||||
ease: [0.21, 0.45, 0.27, 0.9],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const backgroundVariants = {
|
||||
hidden: {
|
||||
opacity: 0,
|
||||
|
|
@ -118,11 +80,11 @@ export function Features() {
|
|||
viewport={{ once: true }}
|
||||
variants={backgroundVariants}>
|
||||
<div className="absolute inset-y-0 left-1/2 h-full w-full max-w-[1200px] -translate-x-1/2">
|
||||
<div className="absolute left-1/2 top-1/2 h-[800px] w-full -translate-x-1/2 -translate-y-1/2 rounded-[100%] bg-blue-500/10 blur-[120px]" />
|
||||
<div className="absolute left-1/2 top-1/2 h-[800px] w-full -translate-x-1/2 -translate-y-1/2 rounded-[100%] bg-blue-500/10 dark:bg-blue-700/30 blur-[120px]" />
|
||||
</div>
|
||||
</motion.div>
|
||||
<div className="container relative z-10 mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="mx-auto mb-24 max-w-3xl text-center">
|
||||
<div className="mx-auto mb-12 md:mb-24 max-w-4xl text-center">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
|
|
@ -132,40 +94,36 @@ export function Features() {
|
|||
ease: [0.21, 0.45, 0.27, 0.9],
|
||||
}}>
|
||||
<h2 className="text-4xl font-bold tracking-tight sm:text-5xl">
|
||||
Powerful features for modern developers.
|
||||
Power and flexibility to get stuff done.
|
||||
</h2>
|
||||
<p className="mt-6 text-lg text-muted-foreground">
|
||||
Everything you need to build faster and write better code.
|
||||
The features you need to build, debug and ship faster – without compromising quality.
|
||||
</p>
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
{/* Mobile Carousel */}
|
||||
<FeaturesMobile />
|
||||
|
||||
{/* Desktop Grid */}
|
||||
<motion.div
|
||||
className="relative mx-auto hidden max-w-[1200px] md:block"
|
||||
className="relative mx-auto md:max-w-[1200px]"
|
||||
variants={containerVariants}
|
||||
initial="hidden"
|
||||
whileInView="visible"
|
||||
viewport={{ once: true }}>
|
||||
<div className="grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3 lg:gap-8">
|
||||
{features.map((feature, index) => (
|
||||
<motion.div key={index} variants={itemVariants} className="group relative">
|
||||
<div className="absolute -inset-px rounded-2xl bg-gradient-to-r from-blue-500/30 via-cyan-500/30 to-purple-500/30 opacity-0 blur-sm transition-all duration-500 group-hover:opacity-100 dark:from-blue-500/50 dark:via-cyan-500/50 dark:to-purple-500/50" />
|
||||
<div className="relative h-full rounded-2xl border border-border/50 bg-background/30 p-8 backdrop-blur-xl transition-all duration-300 hover:border-border hover:bg-background/50 dark:hover:border-border/80 dark:hover:bg-background/40">
|
||||
<div className="mb-5 inline-flex items-center justify-center rounded-xl bg-gradient-to-r from-blue-500/10 to-cyan-500/10 p-2.5 dark:from-blue-500/20 dark:to-cyan-500/20">
|
||||
<div className="rounded-lg bg-gradient-to-r from-blue-500/80 to-cyan-500/80 p-2.5">
|
||||
{feature.icon}
|
||||
</div>
|
||||
</div>
|
||||
<h3 className="mb-3 text-xl font-medium text-foreground/90">{feature.title}</h3>
|
||||
<p className="leading-relaxed text-muted-foreground">{feature.description}</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
<ul className="grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3 lg:gap-8">
|
||||
{features.map((feature, index) => {
|
||||
const Icon = feature.icon
|
||||
return (
|
||||
<li
|
||||
key={index}
|
||||
className="relative h-full border border-border rounded-2xl bg-background p-8 transition-all duration-300">
|
||||
<Icon className="size-6 text-foreground/80" />
|
||||
<h3 className="mb-3 mt-3 text-xl font-semibold text-foreground">{feature.title}</h3>
|
||||
<p className="leading-relaxed font-light text-muted-foreground">
|
||||
{feature.description}
|
||||
</p>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
|
|
|
|||
|
|
@ -2,9 +2,7 @@ export * from "./animated-background"
|
|||
export * from "./code-example"
|
||||
export * from "./company-logos"
|
||||
export * from "./faq-section"
|
||||
export * from "./features-mobile"
|
||||
export * from "./features"
|
||||
export * from "./install-section"
|
||||
export * from "./testimonials-mobile"
|
||||
export * from "./testimonials"
|
||||
export * from "./whats-new-button"
|
||||
|
|
|
|||
|
|
@ -46,12 +46,13 @@ export function InstallSection({ downloads }: InstallSectionProps) {
|
|||
|
||||
<div className="relative text-center">
|
||||
{/* Updated h2 to match other sections */}
|
||||
<h2 className="bg-gradient-to-b from-foreground to-foreground/70 bg-clip-text text-4xl font-bold tracking-tight text-transparent sm:text-5xl">
|
||||
Install Roo Code — Open & Flexible
|
||||
<h2 className="text-3xl font-bold tracking-tight text-foreground sm:text-5xl">
|
||||
Install Roo Code now
|
||||
</h2>
|
||||
<p className="mt-6 text-lg text-muted-foreground">
|
||||
Roo Code is open-source, model-agnostic, and developer-focused. Install from the VS Code
|
||||
Marketplace or the CLI in minutes, then bring your own AI model.
|
||||
Install from the VSCode Marketplace or the CLI in minutes, then bring your own AI model.
|
||||
<br />
|
||||
Roo Code is also compatible with all VSCode forks.
|
||||
</p>
|
||||
|
||||
<div className="mt-12 flex flex-col items-center justify-center gap-6">
|
||||
|
|
@ -64,7 +65,7 @@ export function InstallSection({ downloads }: InstallSectionProps) {
|
|||
<div className="relative flex items-center gap-3">
|
||||
<VscVscode className="h-6 w-6 sm:h-7 sm:w-7" />
|
||||
<span className="flex flex-wrap items-center gap-2">
|
||||
<span>VSCode Marketplace</span>
|
||||
<span>From VSCode Marketplace</span>
|
||||
{downloads !== null && (
|
||||
<>
|
||||
<span className="font-black opacity-60">·</span>
|
||||
|
|
@ -80,7 +81,7 @@ export function InstallSection({ downloads }: InstallSectionProps) {
|
|||
<div className="absolute -inset-px rounded-xl bg-gradient-to-r from-blue-500/50 via-cyan-500/50 to-purple-500/50 opacity-30 blur-sm transition-all duration-500 group-hover:opacity-60 dark:opacity-40 dark:group-hover:opacity-70" />
|
||||
<div className="relative overflow-hidden rounded-xl border border-border bg-background/80 shadow-lg backdrop-blur-xl transition-all duration-500 ease-out group-hover:border-blue-500/50 group-hover:shadow-xl group-hover:shadow-blue-500/10 dark:border-border/50 dark:bg-background/60 dark:group-hover:border-blue-400/50">
|
||||
<div className="border-b border-border/50 bg-muted/30 px-4 py-3 dark:bg-muted/20">
|
||||
<div className="text-sm font-medium text-foreground">Install via CLI</div>
|
||||
<div className="text-sm font-medium text-foreground">or via CLI</div>
|
||||
</div>
|
||||
<div className="overflow-x-auto bg-background/50 dark:bg-background/30">
|
||||
<pre className="p-4">
|
||||
|
|
|
|||
|
|
@ -1,63 +0,0 @@
|
|||
import useEmblaCarousel from "embla-carousel-react"
|
||||
import AutoScroll from "embla-carousel-auto-scroll"
|
||||
import { testimonials } from "@/components/homepage/testimonials"
|
||||
|
||||
export function TestimonialsMobile() {
|
||||
const [emblaRef] = useEmblaCarousel({ loop: true }, [
|
||||
AutoScroll({
|
||||
playOnInit: true,
|
||||
speed: 1, // pixels per second - slower for smoother scrolling
|
||||
stopOnInteraction: true,
|
||||
stopOnMouseEnter: true,
|
||||
}),
|
||||
])
|
||||
|
||||
return (
|
||||
<div className="md:hidden">
|
||||
<div className="overflow-hidden px-4" ref={emblaRef}>
|
||||
<div className="flex">
|
||||
{testimonials.map((testimonial) => (
|
||||
<div className="min-w-0 flex-[0_0_100%] px-4" key={testimonial.id}>
|
||||
<div className="relative rounded-2xl border border-border/50 bg-background/30 p-8 backdrop-blur-xl dark:border-border/70 dark:bg-background/40">
|
||||
<svg
|
||||
className="absolute left-8 top-8 h-8 w-8 text-blue-500/30 dark:text-blue-400/50"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 32 32">
|
||||
<defs>
|
||||
<filter id="glow-mobile">
|
||||
<feGaussianBlur stdDeviation="3" result="coloredBlur" />
|
||||
<feMerge>
|
||||
<feMergeNode in="coloredBlur" />
|
||||
<feMergeNode in="SourceGraphic" />
|
||||
</feMerge>
|
||||
</filter>
|
||||
</defs>
|
||||
<path
|
||||
d="M9.352 4C4.456 7.456 1 13.12 1 19.36c0 5.088 3.072 8.064 6.624 8.064 3.36 0 5.856-2.688 5.856-5.856 0-3.168-2.208-5.472-5.088-5.472-.576 0-1.344.096-1.536.192.48-3.264 3.552-7.104 6.624-9.024L9.352 4zm16.512 0c-4.8 3.456-8.256 9.12-8.256 15.36 0 5.088 3.072 8.064 6.624 8.064 3.264 0 5.856-2.688 5.856-5.856 0-3.168-2.304-5.472-5.184-5.472-.576 0-1.248.096-1.44.192.48-3.264 3.456-7.104 6.528-9.024L25.864 4z"
|
||||
className="dark:filter dark:drop-shadow-[0_0_8px_rgba(96,165,250,0.4)]"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
<blockquote className="mt-12">
|
||||
<p className="text-lg font-light italic leading-relaxed text-muted-foreground dark:text-foreground/70">
|
||||
"{testimonial.quote}"
|
||||
</p>
|
||||
|
||||
<footer className="mt-6">
|
||||
<div className="h-px w-12 bg-gradient-to-r from-blue-500/50 to-transparent dark:from-blue-400/70" />
|
||||
<p className="mt-4 font-medium text-foreground/90 dark:text-foreground">
|
||||
{testimonial.name}
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground dark:text-muted-foreground/80">
|
||||
{testimonial.role} at {testimonial.company}
|
||||
</p>
|
||||
</footer>
|
||||
</blockquote>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -1,9 +1,10 @@
|
|||
"use client"
|
||||
|
||||
import { useRef } from "react"
|
||||
import { useRef, useCallback, useEffect } from "react"
|
||||
import { motion } from "framer-motion"
|
||||
import Image from "next/image"
|
||||
import { TestimonialsMobile } from "./testimonials-mobile"
|
||||
import useEmblaCarousel from "embla-carousel-react"
|
||||
import AutoPlay from "embla-carousel-autoplay"
|
||||
import { ChevronLeft, ChevronRight } from "lucide-react"
|
||||
|
||||
export interface Testimonial {
|
||||
id: number
|
||||
|
|
@ -47,26 +48,66 @@ export const testimonials: Testimonial[] = [
|
|||
|
||||
export function Testimonials() {
|
||||
const containerRef = useRef<HTMLDivElement>(null)
|
||||
const [emblaRef, emblaApi] = useEmblaCarousel(
|
||||
{
|
||||
loop: true,
|
||||
align: "center",
|
||||
skipSnaps: false,
|
||||
containScroll: false,
|
||||
},
|
||||
[
|
||||
AutoPlay({
|
||||
playOnInit: true,
|
||||
delay: 4000,
|
||||
stopOnInteraction: true,
|
||||
stopOnMouseEnter: true,
|
||||
stopOnFocusIn: true,
|
||||
}),
|
||||
],
|
||||
)
|
||||
|
||||
const scrollPrev = useCallback(() => {
|
||||
if (emblaApi) emblaApi.scrollPrev()
|
||||
}, [emblaApi])
|
||||
|
||||
const scrollNext = useCallback(() => {
|
||||
if (emblaApi) emblaApi.scrollNext()
|
||||
}, [emblaApi])
|
||||
|
||||
// Re-init auto-play on user interaction
|
||||
useEffect(() => {
|
||||
if (!emblaApi) return
|
||||
|
||||
const autoPlay = emblaApi?.plugins()?.autoPlay as
|
||||
| {
|
||||
isPlaying?: () => boolean
|
||||
play?: () => void
|
||||
}
|
||||
| undefined
|
||||
if (!autoPlay) return
|
||||
|
||||
const handleInteraction = () => {
|
||||
const isPlaying = autoPlay.isPlaying && autoPlay.isPlaying()
|
||||
if (!isPlaying) {
|
||||
setTimeout(() => {
|
||||
if (autoPlay.play) {
|
||||
autoPlay.play()
|
||||
}
|
||||
}, 2000)
|
||||
}
|
||||
}
|
||||
|
||||
emblaApi.on("pointerUp", handleInteraction)
|
||||
|
||||
return () => {
|
||||
emblaApi.off("pointerUp", handleInteraction)
|
||||
}
|
||||
}, [emblaApi])
|
||||
|
||||
const containerVariants = {
|
||||
hidden: { opacity: 0 },
|
||||
visible: {
|
||||
opacity: 1,
|
||||
transition: {
|
||||
staggerChildren: 0.15,
|
||||
delayChildren: 0.3,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const itemVariants = {
|
||||
hidden: {
|
||||
opacity: 0,
|
||||
y: 20,
|
||||
},
|
||||
visible: {
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
transition: {
|
||||
duration: 0.6,
|
||||
ease: [0.21, 0.45, 0.27, 0.9],
|
||||
|
|
@ -74,123 +115,78 @@ export function Testimonials() {
|
|||
},
|
||||
}
|
||||
|
||||
const backgroundVariants = {
|
||||
hidden: {
|
||||
opacity: 0,
|
||||
},
|
||||
visible: {
|
||||
opacity: 1,
|
||||
transition: {
|
||||
duration: 1.2,
|
||||
ease: "easeOut",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return (
|
||||
<section ref={containerRef} className="relative overflow-hidden border-t border-border py-32">
|
||||
<motion.div
|
||||
className="absolute inset-0"
|
||||
initial="hidden"
|
||||
whileInView="visible"
|
||||
viewport={{ once: true }}
|
||||
variants={backgroundVariants}>
|
||||
<div className="absolute inset-y-0 left-1/2 h-full w-full max-w-[1200px] -translate-x-1/2">
|
||||
<div className="absolute left-1/2 top-1/2 h-[800px] w-full -translate-x-1/2 -translate-y-1/2 rounded-[100%] bg-blue-500/10 blur-[120px]" />
|
||||
</div>
|
||||
</motion.div>
|
||||
<div className="absolute inset-y-0 left-1/2 h-full w-full max-w-[1200px] -translate-x-1/2">
|
||||
<div className="absolute left-1/2 top-1/2 h-[400px] w-full -translate-x-1/2 -translate-y-1/2 rounded-[100%] bg-violet-500/10 dark:bg-violet-700/30 blur-[120px]" />
|
||||
</div>
|
||||
|
||||
<div className="container relative z-10 mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="mx-auto mb-24 max-w-3xl text-center">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{
|
||||
duration: 0.6,
|
||||
ease: [0.21, 0.45, 0.27, 0.9],
|
||||
}}>
|
||||
<h2 className="text-4xl font-bold tracking-tight sm:text-5xl">
|
||||
Empowering developers worldwide.
|
||||
</h2>
|
||||
<p className="mt-6 text-lg text-muted-foreground">
|
||||
Join thousands of developers who are revolutionizing their workflow with AI-powered
|
||||
assistance.
|
||||
</p>
|
||||
</motion.div>
|
||||
<div className="mx-auto mb-8 max-w-5xl text-center">
|
||||
<h2 className="text-4xl font-bold tracking-tight sm:text-5xl">
|
||||
AI-forward developers are using Roo Code
|
||||
</h2>
|
||||
<p className="mt-6 text-lg text-muted-foreground">
|
||||
Join more than 800k people revolutionizing their workflow worldwide
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Mobile Carousel */}
|
||||
<TestimonialsMobile />
|
||||
|
||||
{/* Desktop Grid */}
|
||||
<motion.div
|
||||
className="relative mx-auto hidden max-w-[1200px] md:block"
|
||||
className="relative mx-auto max-w-[1400px]"
|
||||
variants={containerVariants}
|
||||
initial="hidden"
|
||||
whileInView="visible"
|
||||
viewport={{ once: true }}>
|
||||
<div className="relative grid grid-cols-1 gap-12 md:grid-cols-2">
|
||||
{testimonials.map((testimonial, index) => (
|
||||
<motion.div
|
||||
key={testimonial.id}
|
||||
variants={itemVariants}
|
||||
className={`group relative ${index % 2 === 0 ? "md:translate-y-4" : "md:translate-y-12"}`}>
|
||||
<div className="absolute -inset-px rounded-2xl bg-gradient-to-r from-blue-500/30 via-cyan-500/30 to-purple-500/30 opacity-0 blur-sm transition-all duration-500 ease-out group-hover:opacity-100 dark:from-blue-400/40 dark:via-cyan-400/40 dark:to-purple-400/40" />
|
||||
<div className="relative flex h-full flex-col rounded-2xl border border-border/50 bg-background/30 backdrop-blur-xl transition-all duration-500 ease-out group-hover:scale-[1.02] group-hover:border-border group-hover:bg-background/40 group-hover:shadow-2xl dark:border-border/70 dark:bg-background/40 dark:group-hover:border-border dark:group-hover:bg-background/60 dark:group-hover:shadow-[0_20px_50px_rgba(59,130,246,0.15)]">
|
||||
{testimonial.image && (
|
||||
<div className="absolute -right-3 -top-3 h-16 w-16 overflow-hidden rounded-xl border border-border/50 bg-background/50 p-1.5 backdrop-blur-xl transition-all duration-500 ease-out group-hover:scale-110 dark:border-border/70 dark:bg-background/60">
|
||||
<div className="relative h-full w-full overflow-hidden rounded-lg">
|
||||
<Image
|
||||
src={testimonial.image || "/placeholder_pfp.png"}
|
||||
alt={testimonial.name}
|
||||
fill
|
||||
className="object-cover"
|
||||
/>
|
||||
{/* Previous Button */}
|
||||
<button
|
||||
onClick={scrollPrev}
|
||||
className="absolute left-0 top-1/2 z-20 -translate-y-1/2 rounded-full border border-border/50 bg-background/80 p-2 backdrop-blur-xl transition-all duration-300 hover:scale-110 hover:shadow-lg md:left-4 md:p-3 lg:left-8"
|
||||
aria-label="Previous testimonial">
|
||||
<ChevronLeft className="h-5 w-5 text-muted-foreground transition-colors hover:text-foreground md:h-6 md:w-6" />
|
||||
</button>
|
||||
|
||||
{/* Next Button */}
|
||||
<button
|
||||
onClick={scrollNext}
|
||||
className="absolute right-0 top-1/2 z-20 -translate-y-1/2 rounded-full border border-border/50 bg-background/80 p-2 backdrop-blur-xl transition-all duration-300 hover:scale-110 hover:shadow-lg md:right-4 md:p-3 lg:right-8"
|
||||
aria-label="Next testimonial">
|
||||
<ChevronRight className="h-5 w-5 text-muted-foreground transition-colors hover:text-foreground md:h-6 md:w-6" />
|
||||
</button>
|
||||
|
||||
{/* Gradient Overlays */}
|
||||
<div className="absolute inset-y-0 left-0 z-10 w-[10%] bg-gradient-to-r from-background to-transparent pointer-events-none md:w-[15%]" />
|
||||
<div className="absolute inset-y-0 right-0 z-10 w-[10%] bg-gradient-to-l from-background to-transparent pointer-events-none md:w-[15%]" />
|
||||
|
||||
{/* Embla Carousel Container */}
|
||||
<div className="overflow-hidden" ref={emblaRef}>
|
||||
<div className="flex">
|
||||
{testimonials.map((testimonial) => (
|
||||
<div
|
||||
key={testimonial.id}
|
||||
className="relative min-w-0 flex-[0_0_85%] px-2 md:flex-[0_0_70%] md:px-4 lg:flex-[0_0_60%]">
|
||||
<div className="group relative py-10 h-full">
|
||||
<div className="relative flex h-full flex-col rounded-2xl border border-border bg-background transition-all duration-500 ease-out group-hover:scale-[1.02] group-hover:border-border group-hover:bg-background/40 group-hover:shadow-xl dark:border-border/70 dark:bg-background/40 dark:group-hover:border-border dark:group-hover:bg-background/60 dark:group-hover:shadow-[0_20px_50px_rgba(59,130,246,0.15)]">
|
||||
<div className="flex flex-1 flex-col p-6 md:p-8">
|
||||
<div className="flex-1">
|
||||
<p className="relative text-sm leading-relaxed text-muted-foreground transition-colors duration-300 group-hover:text-foreground/80 dark:text-foreground/70 dark:group-hover:text-foreground/90 md:text-lg">
|
||||
{testimonial.quote}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="relative mt-4 md:mt-6">
|
||||
<h3 className="font-medium text-foreground/90 transition-colors duration-300 dark:text-foreground">
|
||||
{testimonial.name}
|
||||
</h3>
|
||||
<p className="text-sm text-muted-foreground transition-colors duration-300 dark:text-muted-foreground/80">
|
||||
{testimonial.role} at {testimonial.company}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex flex-1 flex-col p-8">
|
||||
<div className="flex-1">
|
||||
<div className="mb-6">
|
||||
<svg
|
||||
className="h-8 w-8 text-blue-500/20 transition-all duration-500 group-hover:text-blue-500/30 dark:text-blue-400/40 dark:group-hover:text-blue-400/60"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 32 32">
|
||||
<defs>
|
||||
<filter id="glow">
|
||||
<feGaussianBlur stdDeviation="3" result="coloredBlur" />
|
||||
<feMerge>
|
||||
<feMergeNode in="coloredBlur" />
|
||||
<feMergeNode in="SourceGraphic" />
|
||||
</feMerge>
|
||||
</filter>
|
||||
</defs>
|
||||
<path
|
||||
d="M9.352 4C4.456 7.456 1 13.12 1 19.36c0 5.088 3.072 8.064 6.624 8.064 3.36 0 5.856-2.688 5.856-5.856 0-3.168-2.208-5.472-5.088-5.472-.576 0-1.344.096-1.536.192.48-3.264 3.552-7.104 6.624-9.024L9.352 4zm16.512 0c-4.8 3.456-8.256 9.12-8.256 15.36 0 5.088 3.072 8.064 6.624 8.064 3.264 0 5.856-2.688 5.856-5.856 0-3.168-2.304-5.472-5.184-5.472-.576 0-1.248.096-1.44.192.48-3.264 3.456-7.104 6.528-9.024L25.864 4z"
|
||||
className="dark:filter dark:drop-shadow-[0_0_8px_rgba(96,165,250,0.4)]"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<p className="relative text-lg leading-relaxed text-muted-foreground transition-colors duration-300 group-hover:text-foreground/80 dark:text-foreground/70 dark:group-hover:text-foreground/90">
|
||||
{testimonial.quote}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="relative mt-6">
|
||||
<div className="mb-4 h-px w-12 bg-gradient-to-r from-blue-500/50 to-transparent transition-all duration-500 group-hover:w-16 group-hover:from-blue-500/70 dark:from-blue-400/70 dark:group-hover:from-blue-400/90" />
|
||||
<h3 className="font-medium text-foreground/90 transition-colors duration-300 dark:text-foreground">
|
||||
{testimonial.name}
|
||||
</h3>
|
||||
<p className="text-sm text-muted-foreground transition-colors duration-300 dark:text-muted-foreground/80">
|
||||
{testimonial.role} at {testimonial.company}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -24,7 +24,8 @@ export const EXTERNAL_LINKS = {
|
|||
OFFICE_HOURS_PODCAST: "https://www.youtube.com/@RooCodeYT/podcasts",
|
||||
FAQ: "https://roocode.com/#faq",
|
||||
TESTIMONIALS: "https://roocode.com/#testimonials",
|
||||
CLOUD_APP: "https://app.roocode.com",
|
||||
CLOUD_APP_LOGIN: "https://app.roocode.com/sign-in",
|
||||
CLOUD_APP_SIGNUP: "https://app.roocode.com/sign-up",
|
||||
}
|
||||
|
||||
export const INTERNAL_LINKS = {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue