Bladeren bron

web: Landing page for the reviewerFirst pass (#8740)

* First pass

* SEO

* Update apps/web-roo-code/src/app/reviewer/page.tsx

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

---------

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
Bruno Bergher 2 maanden geleden
bovenliggende
commit
f3a505fd36

BIN
apps/web-roo-code/public/heroes/agent-reviewer.png


+ 266 - 0
apps/web-roo-code/src/app/reviewer/page.tsx

@@ -0,0 +1,266 @@
+import { ArrowRight, Blocks, BookMarked, ListChecks, LucideIcon } from "lucide-react"
+import type { Metadata } from "next"
+
+import { Button } from "@/components/ui"
+import { AnimatedBackground } from "@/components/homepage"
+import { AgentCarousel } from "@/components/reviewer/agent-carousel"
+import { SEO } from "@/lib/seo"
+import { EXTERNAL_LINKS } from "@/lib/constants"
+import Image from "next/image"
+
+const TITLE = "PR Reviewer · Roo Code Cloud"
+const DESCRIPTION =
+	"Get comprehensive AI-powered PR reviews that save you time, not tokens. Bring your own API key and leverage advanced reasoning, repository-aware analysis, and actionable feedback to keep your PR queue moving."
+const PATH = "/reviewer"
+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,
+		"PR reviewer",
+		"code review",
+		"pull request review",
+		"AI code review",
+		"GitHub PR review",
+		"automated code review",
+		"repository-aware review",
+		"bring your own key",
+		"BYOK AI",
+		"code quality",
+		"development workflow",
+		"cloud agents",
+		"AI development team",
+	],
+}
+
+interface Feature {
+	icon: LucideIcon
+	title: string
+	description: string | React.ReactNode
+	logos?: string[]
+}
+
+const howItWorks: Feature[] = [
+	{
+		icon: Blocks,
+		title: "Our agents, your provider keys",
+		description: (
+			<>
+				<p>
+					We orchestrate the review, optimize the hell out of the prompts, integrate with GitHub, keep you
+					properly posted.
+				</p>
+				<p>We&apos;re thoughtful about token usage, but not incentivized to skimp to grow our margins.</p>
+			</>
+		),
+	},
+	{
+		icon: ListChecks,
+		title: "Advanced reasoning and workflows",
+		description:
+			"We optimize for state-of-the-art reasoning models and leverage powerful workflows (Diff analysis → Context Gathering → Impact Mapping → Contract checks) to produce crisp, actionable comments at the right level.",
+	},
+	{
+		icon: BookMarked,
+		title: "Fully repository-aware",
+		description:
+			"Reviews traverse code ownership, dependency graphs, and historical patterns to surface risk and deviations, not noise.",
+	},
+]
+
+// Workaround for next/image choking on these for some reason
+import hero from "/public/heroes/agent-reviewer.png"
+
+export default function AgentReviewerPage() {
+	return (
+		<>
+			<section className="relative flex md:h-[calc(70vh-theme(spacing.12))] items-center overflow-hidden">
+				<AnimatedBackground />
+				<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-4 md:gap-20 lg:grid-cols-2">
+						<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 mt-8  md:text-left md:text-4xl lg:text-5xl lg:mt-0">
+									Get comprehensive reviews that save you time, not&nbsp;tokens.
+								</h1>
+								<div className="mt-4 max-w-lg space-y-4 text-base text-muted-foreground md:text-left sm:mt-6">
+									<p>
+										Regular AI code review tools cap model usage to protect their margins from fixed
+										monthly prices. That leads to shallow prompts, limited context, and missed
+										issues.
+									</p>
+									<p>
+										Roo Code&apos;s PR Reviewer flips the model: you bring your own key and leverage
+										it to the max – to find real issues, increase code quality and keep your PR
+										queue moving.
+									</p>
+								</div>
+							</div>
+							<div className="flex flex-col space-y-3 sm:flex-row sm:space-x-4 sm:space-y-0 md:items-center">
+								<Button
+									size="lg"
+									className="w-full sm:w-auto backdrop-blur-sm border hover:shadow-[0_0_20px_rgba(59,130,246,0.5)] transition-all duration-300">
+									<a
+										href={EXTERNAL_LINKS.CLOUD_APP_SIGNUP_PRO}
+										target="_blank"
+										rel="noopener noreferrer"
+										className="flex w-full items-center justify-center">
+										Start 14-day Free Trial
+										<ArrowRight className="ml-2" />
+									</a>
+								</Button>
+								<span className="text-sm text-center md:text-left text-muted-foreground md:ml-2">
+									(cancel anytime)
+								</span>
+							</div>
+						</div>
+						<div className="flex items-center justify-end mx-auto h-full mt-8 lg:mt-0">
+							<div className="md:w-[800px] md:h-[474px] relative overflow-clip">
+								<div className="block">
+									<Image
+										src={hero}
+										alt="Example of a code review generated by Roo Code PR Reviewer"
+										className="max-w-full h-auto"
+										width={800}
+										height={474}
+									/>
+								</div>
+							</div>
+						</div>
+					</div>
+				</div>
+			</section>
+
+			<section className="relative overflow-hidden border-t border-border py-32">
+				<div className="container relative z-10 mx-auto px-4 sm:px-6 lg:px-8">
+					<div className="mx-auto mb-12 md:mb-24 max-w-5xl text-center">
+						<div>
+							<h2 className="text-4xl font-bold tracking-tight sm:text-5xl">
+								Why Roo&apos;s PR Reviewer is so much better
+							</h2>
+						</div>
+					</div>
+
+					<div className="relative mx-auto md:max-w-[1200px]">
+						<ul className="grid grid-cols-1 place-items-center gap-6 md:grid-cols-2 lg:grid-cols-3 lg:gap-8">
+							{howItWorks.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>
+										<div className="leading-relaxed font-light text-muted-foreground space-y-2">
+											{feature.description}
+										</div>
+										{feature.logos && (
+											<div className="mt-4 flex flex-wrap items-center gap-4">
+												{feature.logos.map((logo) => (
+													<Image
+														key={logo}
+														width={20}
+														height={20}
+														className="w-5 h-5 overflow-clip opacity-50 dark:invert"
+														src={`/logos/${logo.toLowerCase()}.svg`}
+														alt={`${logo} Logo`}
+													/>
+												))}
+											</div>
+										)}
+									</li>
+								)
+							})}
+						</ul>
+					</div>
+				</div>
+			</section>
+
+			<section className="relative overflow-hidden border-t border-border py-32">
+				<div className="container relative z-10 mx-auto px-4 sm:px-6 lg:px-8">
+					<div className="mx-auto mb-12 max-w-4xl text-center">
+						<div>
+							<h2 className="text-4xl font-bold tracking-tight sm:text-5xl">
+								The first member of a whole new team
+							</h2>
+
+							<p className="mt-6 text-lg text-muted-foreground">
+								Architecture, coding, reviewing, testing, debugging, documenting, designing –{" "}
+								<em>almost everything</em> we do today is mostly through our agents. Now we&apos;re
+								bringing them to you.
+							</p>
+							<p className="mt-2 text-lg text-muted-foreground">
+								Roo&apos;s PR Reviewer isn&apos;t yet another single-purpose tool to add to your already
+								complicated stack.
+								<br />
+								It&apos;s the first member of your AI-powered development team. More agents are shipping
+								soon.
+							</p>
+						</div>
+					</div>
+
+					<div className="relative mx-auto md:max-w-[1200px]">
+						<AgentCarousel />
+					</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">Stop wasting time.</h2>
+						<p className="mx-auto mb-8 max-w-2xl text-lg text-muted-foreground">
+							Give Roo Code&apos;s PR Reviewer your model key and turn painful reviews into a tangible
+							quality advantage.
+						</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_PRO}
+									target="_blank"
+									rel="noopener noreferrer"
+									className="flex items-center justify-center">
+									Start 14-day Free Trial
+									<ArrowRight className="ml-2 h-4 w-4" />
+								</a>
+							</Button>
+						</div>
+					</div>
+				</div>
+			</section>
+		</>
+	)
+}

+ 125 - 0
apps/web-roo-code/src/components/reviewer/agent-carousel.tsx

@@ -0,0 +1,125 @@
+"use client"
+
+import { useEffect } from "react"
+import { motion } from "framer-motion"
+import useEmblaCarousel from "embla-carousel-react"
+import AutoPlay from "embla-carousel-autoplay"
+import { Bug, FileText, Gauge, Languages, Microscope, PocketKnife, TestTube, type LucideIcon } from "lucide-react"
+
+// AI Agent types for the carousel
+interface AIAgent {
+	icon: LucideIcon
+	name: string
+}
+
+const aiAgents: AIAgent[] = [
+	{ icon: PocketKnife, name: "Generalist" },
+	{ icon: Bug, name: "Bug Fixer" },
+	{ icon: TestTube, name: "Test Engineer" },
+	{ icon: Microscope, name: "Security Auditor" },
+	{ icon: Gauge, name: "Performance Optimizer" },
+	{ icon: FileText, name: "Documentation Writer" },
+	{ icon: Languages, name: "String Translator" },
+]
+
+export function AgentCarousel() {
+	const [emblaRef, emblaApi] = useEmblaCarousel(
+		{
+			loop: true,
+			align: "start",
+			watchDrag: true,
+			dragFree: false,
+			containScroll: false,
+			duration: 10000,
+		},
+		[
+			AutoPlay({
+				playOnInit: true,
+				delay: 0,
+				stopOnInteraction: false,
+				stopOnMouseEnter: false,
+				stopOnFocusIn: false,
+			}),
+		],
+	)
+
+	// Continuous scrolling effect
+	useEffect(() => {
+		if (!emblaApi) return
+
+		const autoPlay = emblaApi?.plugins()?.autoPlay as
+			| {
+					play?: () => void
+			  }
+			| undefined
+
+		if (autoPlay?.play) {
+			autoPlay.play()
+		}
+
+		// Set up continuous scrolling
+		const interval = setInterval(() => {
+			if (emblaApi) {
+				emblaApi.scrollNext()
+			}
+		}, 30) // Smooth continuous scroll
+
+		return () => clearInterval(interval)
+	}, [emblaApi])
+
+	const containerVariants = {
+		hidden: { opacity: 0 },
+		visible: {
+			opacity: 1,
+			transition: {
+				duration: 0.6,
+				ease: [0.21, 0.45, 0.27, 0.9],
+			},
+		},
+	}
+
+	// Duplicate the agents array for seamless infinite scroll
+	const displayAgents = [...aiAgents, ...aiAgents]
+
+	return (
+		<motion.div
+			className="relative -mx-4 md:mx-auto max-w-[1400px]"
+			variants={containerVariants}
+			initial="hidden"
+			whileInView="visible"
+			viewport={{ once: true }}>
+			{/* 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 pb-4">
+					{displayAgents.map((agent, index) => {
+						const Icon = agent.icon
+						return (
+							<div
+								key={`${agent.name}-${index}`}
+								className="relative min-w-0 flex-[0_0_45%] px-2 md:flex-[0_0_30%] md:px-4 lg:flex-[0_0_15%]">
+								<div className="group relative py-6 cursor-default">
+									<div
+										className="relative flex flex-col items-center justify-center rounded-full w-[150px] h-[150px] border border-border bg-background p-6 transition-all duration-500 ease-out shadow-xl
+                                    hover:scale-110 hover:-translate-y-2
+                                    hover:shadow-[0_20px_50px_rgba(39,110,226,0.25)] dark:hover:shadow-[0_20px_50px_rgba(59,130,246,0.25)]">
+										<Icon
+											strokeWidth={1}
+											className="size-9 mb-2 text-foreground transition-colors duration-300"
+										/>
+										<h3 className="text-center leading-tight tracking-tight font-medium text-foreground/90 transition-colors duration-300 dark:text-foreground">
+											{agent.name}
+										</h3>
+									</div>
+								</div>
+							</div>
+						)
+					})}
+				</div>
+			</div>
+		</motion.div>
+	)
+}

+ 1 - 0
apps/web-roo-code/src/lib/constants.ts

@@ -26,6 +26,7 @@ export const EXTERNAL_LINKS = {
 	TESTIMONIALS: "https://roocode.com/#testimonials",
 	CLOUD_APP_LOGIN: "https://app.roocode.com/sign-in",
 	CLOUD_APP_SIGNUP: "https://app.roocode.com/sign-up",
+	CLOUD_APP_SIGNUP_PRO: "https://app.roocode.com/sign-up?redirect=/checkout/pro",
 }
 
 export const INTERNAL_LINKS = {