Revert "Add roocode.com website to monorepo" (#4145)

Revert "Add roocode.com website to monorepo (#4128)"

This reverts commit 0f22eabb1b.
This commit is contained in:
Matt Rubens 2025-05-30 07:37:47 -04:00 committed by GitHub
parent 7ee90f916f
commit 78769f187c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
83 changed files with 92 additions and 13805 deletions

View file

@ -6,7 +6,6 @@
"esbenp.prettier-vscode",
"csstools.postcss",
"bradlc.vscode-tailwindcss",
"connor4312.esbuild-problem-matchers",
"yoavbls.pretty-ts-errors"
"connor4312.esbuild-problem-matchers"
]
}

View file

@ -1,11 +0,0 @@
# PostHog Analytics Configuration
# Replace these values with your actual PostHog API key and host
NEXT_PUBLIC_POSTHOG_KEY=your_posthog_api_key_here
NEXT_PUBLIC_POSTHOG_HOST=https://us.i.posthog.com
# Basin Form Endpoint for Static Form Submissions
# Replace this with your actual Basin form endpoint (e.g., https://usebasin.com/f/your-form-id)
NEXT_PUBLIC_BASIN_ENDPOINT=https://usebasin.com/f/your-form-id-here
TURSO_CONNECTION_URL=libsql://development-roo-code.aws-us-east-1.turso.io
TURSO_AUTH_TOKEN=your-auth-token-here

View file

@ -1,42 +0,0 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# env files (can opt-in for committing if needed)
.env*
!.env.example
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts

View file

@ -1,21 +0,0 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "tailwind.config.js",
"css": "src/app/globals.css",
"baseColor": "slate",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"iconLibrary": "lucide"
}

View file

@ -1,14 +0,0 @@
import { defineConfig } from "drizzle-kit"
const dialect = process.env.BENCHMARKS_DB_PATH ? "sqlite" : "turso"
const dbCredentials = process.env.BENCHMARKS_DB_PATH
? { url: process.env.BENCHMARKS_DB_PATH }
: { url: process.env.TURSO_CONNECTION_URL!, authToken: process.env.TURSO_AUTH_TOKEN! }
export default defineConfig({
out: "./drizzle",
schema: "./src/db/schema.ts",
dialect,
dbCredentials,
})

View file

@ -1,4 +0,0 @@
import { nextJsConfig } from "@roo-code/config-eslint/next-js"
/** @type {import("eslint").Linter.Config} */
export default [...nextJsConfig]

View file

@ -1,28 +0,0 @@
import type { NextConfig } from "next"
const nextConfig: NextConfig = {
webpack: (config) => {
config.resolve.extensionAlias = { ".js": [".ts", ".tsx", ".js", ".jsx"] }
return config
},
async redirects() {
return [
// Redirect www to non-www
{
source: "/:path*",
has: [{ type: "host", value: "www.roocode.com" }],
destination: "https://roocode.com/:path*",
permanent: true,
},
// Redirect HTTP to HTTPS
{
source: "/:path*",
has: [{ type: "header", key: "x-forwarded-proto", value: "http" }],
destination: "https://roocode.com/:path*",
permanent: true,
},
]
},
}
export default nextConfig

View file

@ -1,58 +0,0 @@
{
"name": "@roo-code/website",
"version": "0.0.0",
"type": "module",
"scripts": {
"lint": "next lint",
"check-types": "tsc --noEmit",
"dev": "next dev",
"build": "next build",
"start": "next start",
"drizzle-kit": "dotenvx run -f .env -- tsx node_modules/drizzle-kit/bin.cjs",
"db:generate": "pnpm drizzle-kit generate",
"db:migrate": "pnpm drizzle-kit migrate",
"db:push": "pnpm drizzle-kit push",
"db:pull": "pnpm drizzle-kit pull",
"db:check": "pnpm drizzle-kit check",
"db:up": "pnpm drizzle-kit up",
"db:studio": "pnpm drizzle-kit studio"
},
"dependencies": {
"@libsql/client": "^0.15.7",
"@radix-ui/react-dialog": "^1.1.14",
"@radix-ui/react-slot": "^1.2.3",
"@roo-code/types": "workspace:^",
"@tanstack/react-query": "^5.79.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"drizzle-orm": "^0.41.0",
"drizzle-zod": "^0.7.1",
"embla-carousel-auto-scroll": "^8.6.0",
"embla-carousel-autoplay": "^8.6.0",
"embla-carousel-react": "^8.6.0",
"framer-motion": "^12.15.0",
"lucide-react": "^0.479.0",
"next": "15.2.4",
"next-themes": "^0.4.6",
"posthog-js": "^1.248.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-icons": "^5.5.0",
"recharts": "^2.15.3",
"tailwind-merge": "^3.3.0",
"tailwindcss-animate": "^1.0.7",
"zod": "^3.25.41"
},
"devDependencies": {
"@roo-code/config-eslint": "workspace:^",
"@roo-code/config-typescript": "workspace:^",
"@tailwindcss/typography": "^0.5.16",
"@types/node": "^20.17.54",
"@types/react": "^18.3.23",
"@types/react-dom": "^18.3.7",
"autoprefixer": "^10.4.21",
"drizzle-kit": "^0.30.6",
"postcss": "^8.5.4",
"tailwindcss": "^3.4.17"
}
}

View file

@ -1,6 +0,0 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

File diff suppressed because it is too large Load diff

Before

Width:  |  Height:  |  Size: 226 KiB

File diff suppressed because it is too large Load diff

Before

Width:  |  Height:  |  Size: 226 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 322 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 600 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="1200" fill="none"><rect width="1200" height="1200" fill="#EAEAEA" rx="3"/><g opacity=".5"><g opacity=".5"><path fill="#FAFAFA" d="M600.709 736.5c-75.454 0-136.621-61.167-136.621-136.62 0-75.454 61.167-136.621 136.621-136.621 75.453 0 136.62 61.167 136.62 136.621 0 75.453-61.167 136.62-136.62 136.62Z"/><path stroke="#C9C9C9" stroke-width="2.418" d="M600.709 736.5c-75.454 0-136.621-61.167-136.621-136.62 0-75.454 61.167-136.621 136.621-136.621 75.453 0 136.62 61.167 136.62 136.621 0 75.453-61.167 136.62-136.62 136.62Z"/></g><path stroke="url(#a)" stroke-width="2.418" d="M0-1.209h553.581" transform="scale(1 -1) rotate(45 1163.11 91.165)"/><path stroke="url(#b)" stroke-width="2.418" d="M404.846 598.671h391.726"/><path stroke="url(#c)" stroke-width="2.418" d="M599.5 795.742V404.017"/><path stroke="url(#d)" stroke-width="2.418" d="m795.717 796.597-391.441-391.44"/><path fill="#fff" d="M600.709 656.704c-31.384 0-56.825-25.441-56.825-56.824 0-31.384 25.441-56.825 56.825-56.825 31.383 0 56.824 25.441 56.824 56.825 0 31.383-25.441 56.824-56.824 56.824Z"/><g clip-path="url(#e)"><path fill="#666" fill-rule="evenodd" d="M616.426 586.58h-31.434v16.176l3.553-3.554.531-.531h9.068l.074-.074 8.463-8.463h2.565l7.18 7.181V586.58Zm-15.715 14.654 3.698 3.699 1.283 1.282-2.565 2.565-1.282-1.283-5.2-5.199h-6.066l-5.514 5.514-.073.073v2.876a2.418 2.418 0 0 0 2.418 2.418h26.598a2.418 2.418 0 0 0 2.418-2.418v-8.317l-8.463-8.463-7.181 7.181-.071.072Zm-19.347 5.442v4.085a6.045 6.045 0 0 0 6.046 6.045h26.598a6.044 6.044 0 0 0 6.045-6.045v-7.108l1.356-1.355-1.282-1.283-.074-.073v-17.989h-38.689v23.43l-.146.146.146.147Z" clip-rule="evenodd"/></g><path stroke="#C9C9C9" stroke-width="2.418" d="M600.709 656.704c-31.384 0-56.825-25.441-56.825-56.824 0-31.384 25.441-56.825 56.825-56.825 31.383 0 56.824 25.441 56.824 56.825 0 31.383-25.441 56.824-56.824 56.824Z"/></g><defs><linearGradient id="a" x1="554.061" x2="-.48" y1=".083" y2=".087" gradientUnits="userSpaceOnUse"><stop stop-color="#C9C9C9" stop-opacity="0"/><stop offset=".208" stop-color="#C9C9C9"/><stop offset=".792" stop-color="#C9C9C9"/><stop offset="1" stop-color="#C9C9C9" stop-opacity="0"/></linearGradient><linearGradient id="b" x1="796.912" x2="404.507" y1="599.963" y2="599.965" gradientUnits="userSpaceOnUse"><stop stop-color="#C9C9C9" stop-opacity="0"/><stop offset=".208" stop-color="#C9C9C9"/><stop offset=".792" stop-color="#C9C9C9"/><stop offset="1" stop-color="#C9C9C9" stop-opacity="0"/></linearGradient><linearGradient id="c" x1="600.792" x2="600.794" y1="403.677" y2="796.082" gradientUnits="userSpaceOnUse"><stop stop-color="#C9C9C9" stop-opacity="0"/><stop offset=".208" stop-color="#C9C9C9"/><stop offset=".792" stop-color="#C9C9C9"/><stop offset="1" stop-color="#C9C9C9" stop-opacity="0"/></linearGradient><linearGradient id="d" x1="404.85" x2="796.972" y1="403.903" y2="796.02" gradientUnits="userSpaceOnUse"><stop stop-color="#C9C9C9" stop-opacity="0"/><stop offset=".208" stop-color="#C9C9C9"/><stop offset=".792" stop-color="#C9C9C9"/><stop offset="1" stop-color="#C9C9C9" stop-opacity="0"/></linearGradient><clipPath id="e"><path fill="#fff" d="M581.364 580.535h38.689v38.689h-38.689z"/></clipPath></defs></svg>

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

View file

@ -1,427 +0,0 @@
import { Code, CheckCircle, Shield, Users, Zap, Workflow } from "lucide-react"
import { Button } from "@/components/ui"
import { AnimatedText } from "@/components/animated-text"
import { AnimatedBackground } from "@/components/homepage"
import { ContactForm } from "@/components/enterprise/contact-form"
export default async function Enterprise() {
return (
<>
{/* Hero Section */}
<section className="relative flex h-[calc(100vh-theme(spacing.16))] items-center overflow-hidden">
<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>
<h1 className="text-3xl font-bold tracking-tight sm:text-4xl md:text-5xl lg:text-6xl">
<span className="block">Roo Code for</span>
<AnimatedText className="bg-gradient-to-r from-blue-400 to-cyan-400 bg-clip-text text-transparent">
Enterprise
</AnimatedText>
</h1>
<p className="mt-4 max-w-md text-base text-muted-foreground sm:mt-6 sm:text-lg">
A next-generation, AI-powered{" "}
<AnimatedText className="bg-gradient-to-r from-blue-400 to-cyan-400 bg-clip-text text-transparent">
coding partner
</AnimatedText>{" "}
for enterprise development teams.
</p>
</div>
<div className="flex flex-col space-y-3 sm:flex-row sm:space-x-4 sm:space-y-0">
<Button
size="lg"
className="w-full hover:bg-gray-200 dark:bg-white dark:text-black sm:w-auto"
asChild>
<a href="#contact" className="flex w-full items-center justify-center">
Request a Demo
<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>
</a>
</Button>
<Button variant="outline" size="lg" className="w-full sm:w-auto">
<a href="#benefits" className="flex w-full items-center justify-center">
Learn More
</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>
<div className="relative z-10 rounded-lg border border-border bg-card p-6 shadow-lg">
<div className="mb-4 flex items-center space-x-2">
<Code className="h-6 w-6 text-blue-400" />
<h3 className="text-lg font-semibold">Roo Code Enterprise</h3>
</div>
<p className="mb-4 text-sm text-muted-foreground">
An AI extension of your team that handles coding tasks, from new code generation to
refactoring, bug fixing, and documentation.
</p>
<div className="space-y-2">
<div className="flex items-center space-x-2">
<CheckCircle className="h-4 w-4 text-green-400" />
<span className="text-sm">Accelerate development cycles</span>
</div>
<div className="flex items-center space-x-2">
<CheckCircle className="h-4 w-4 text-green-400" />
<span className="text-sm">Enterprise-grade security</span>
</div>
<div className="flex items-center space-x-2">
<CheckCircle className="h-4 w-4 text-green-400" />
<span className="text-sm">Custom-tailored to your workflow</span>
</div>
<div className="flex items-center space-x-2">
<CheckCircle className="h-4 w-4 text-green-400" />
<span className="text-sm">Improve collaboration and onboarding</span>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
{/* Key Messaging Sections */}
<section id="benefits" className="bg-secondary/50 py-16">
<div className="container mx-auto px-4 sm:px-6 lg:px-8">
<div className="mb-12 text-center">
<h2 className="text-3xl font-bold tracking-tight sm:text-4xl">Empower Your Development Team</h2>
<p className="mx-auto mt-4 max-w-2xl text-lg text-muted-foreground">
Roo Code functions like an entire AI dev team embedded in your developers&apos; IDE, ready
to accelerate software delivery and improve code quality.
</p>
</div>
<div className="grid gap-8 md:grid-cols-2 lg:grid-cols-3">
{/* Card 1 */}
<div className="rounded-lg border border-border bg-card p-6 shadow-sm transition-all hover:shadow-md">
<div className="mb-4 inline-flex h-12 w-12 items-center justify-center rounded-full bg-blue-100 dark:bg-blue-900/20">
<Zap className="h-6 w-6 text-blue-500" />
</div>
<h3 className="mb-2 text-xl font-bold">Accelerate Development Cycles</h3>
<p className="text-muted-foreground">
Supercharge development with AI assistance that helps developers code faster while
maintaining quality.
</p>
<ul className="mt-4 space-y-2">
<li className="flex items-start">
<CheckCircle className="mr-2 mt-0.5 h-5 w-5 shrink-0 text-green-500" />
<span>Faster time-to-market</span>
</li>
<li className="flex items-start">
<CheckCircle className="mr-2 mt-0.5 h-5 w-5 shrink-0 text-green-500" />
<span>AI pair-programming</span>
</li>
<li className="flex items-start">
<CheckCircle className="mr-2 mt-0.5 h-5 w-5 shrink-0 text-green-500" />
<span>Improved code quality</span>
</li>
</ul>
</div>
{/* Card 2 */}
<div className="rounded-lg border border-border bg-card p-6 shadow-sm transition-all hover:shadow-md">
<div className="mb-4 inline-flex h-12 w-12 items-center justify-center rounded-full bg-blue-100 dark:bg-blue-900/20">
<Users className="h-6 w-6 text-blue-500" />
</div>
<h3 className="mb-2 text-xl font-bold">Augment Your Team with AI Agents</h3>
<p className="text-muted-foreground">
Roo Code functions like an AI extension of your team, handling various coding tasks.
</p>
<ul className="mt-4 space-y-2">
<li className="flex items-start">
<CheckCircle className="mr-2 mt-0.5 h-5 w-5 shrink-0 text-green-500" />
<span>New code generation</span>
</li>
<li className="flex items-start">
<CheckCircle className="mr-2 mt-0.5 h-5 w-5 shrink-0 text-green-500" />
<span>Refactoring and bug fixing</span>
</li>
<li className="flex items-start">
<CheckCircle className="mr-2 mt-0.5 h-5 w-5 shrink-0 text-green-500" />
<span>Automate complex migrations</span>
</li>
</ul>
</div>
{/* Card 3 */}
<div className="rounded-lg border border-border bg-card p-6 shadow-sm transition-all hover:shadow-md">
<div className="mb-4 inline-flex h-12 w-12 items-center justify-center rounded-full bg-blue-100 dark:bg-blue-900/20">
<Shield className="h-6 w-6 text-blue-500" />
</div>
<h3 className="mb-2 text-xl font-bold">Enterprise-Grade Security</h3>
<p className="text-muted-foreground">
Keep your data private with on-premises models, keeping proprietary code in-house.
</p>
<ul className="mt-4 space-y-2">
<li className="flex items-start">
<CheckCircle className="mr-2 mt-0.5 h-5 w-5 shrink-0 text-green-500" />
<span>Security and compliance</span>
</li>
<li className="flex items-start">
<CheckCircle className="mr-2 mt-0.5 h-5 w-5 shrink-0 text-green-500" />
<span>No external cloud dependencies</span>
</li>
<li className="flex items-start">
<CheckCircle className="mr-2 mt-0.5 h-5 w-5 shrink-0 text-green-500" />
<span>Open-source and extensible</span>
</li>
</ul>
</div>
{/* Card 4 */}
<div className="rounded-lg border border-border bg-card p-6 shadow-sm transition-all hover:shadow-md">
<div className="mb-4 inline-flex h-12 w-12 items-center justify-center rounded-full bg-blue-100 dark:bg-blue-900/20">
<Workflow className="h-6 w-6 text-blue-500" />
</div>
<h3 className="mb-2 text-xl font-bold">Custom-Tailored to Your Workflow</h3>
<p className="text-muted-foreground">
Developers can create Custom Modes for specialized tasks like security auditing or
performance tuning.
</p>
<ul className="mt-4 space-y-2">
<li className="flex items-start">
<CheckCircle className="mr-2 mt-0.5 h-5 w-5 shrink-0 text-green-500" />
<span>Integrate with internal tools</span>
</li>
<li className="flex items-start">
<CheckCircle className="mr-2 mt-0.5 h-5 w-5 shrink-0 text-green-500" />
<span>Adapt to existing workflows</span>
</li>
<li className="flex items-start">
<CheckCircle className="mr-2 mt-0.5 h-5 w-5 shrink-0 text-green-500" />
<span>Custom AI behaviors</span>
</li>
</ul>
</div>
{/* Card 5 */}
<div className="rounded-lg border border-border bg-card p-6 shadow-sm transition-all hover:shadow-md">
<div className="mb-4 inline-flex h-12 w-12 items-center justify-center rounded-full bg-blue-100 dark:bg-blue-900/20">
<Users className="h-6 w-6 text-blue-500" />
</div>
<h3 className="mb-2 text-xl font-bold">Collaboration and Onboarding</h3>
<p className="text-muted-foreground">
Ask Mode enables developers to query their codebase in plain language and receive
instant answers.
</p>
<ul className="mt-4 space-y-2">
<li className="flex items-start">
<CheckCircle className="mr-2 mt-0.5 h-5 w-5 shrink-0 text-green-500" />
<span>Accelerates onboarding</span>
</li>
<li className="flex items-start">
<CheckCircle className="mr-2 mt-0.5 h-5 w-5 shrink-0 text-green-500" />
<span>Improves cross-team collaboration</span>
</li>
<li className="flex items-start">
<CheckCircle className="mr-2 mt-0.5 h-5 w-5 shrink-0 text-green-500" />
<span>Makes code more accessible</span>
</li>
</ul>
</div>
{/* Card 6 */}
<div className="rounded-lg border border-border bg-card p-6 shadow-sm transition-all hover:shadow-md">
<div className="mb-4 inline-flex h-12 w-12 items-center justify-center rounded-full bg-blue-100 dark:bg-blue-900/20">
<Zap className="h-6 w-6 text-blue-500" />
</div>
<h3 className="mb-2 text-xl font-bold">Faster Delivery, Lower Costs</h3>
<p className="text-muted-foreground">
Automate routine tasks to accelerate software releases and reduce costs.
</p>
<ul className="mt-4 space-y-2">
<li className="flex items-start">
<CheckCircle className="mr-2 mt-0.5 h-5 w-5 shrink-0 text-green-500" />
<span>Improved code quality & consistency</span>
</li>
<li className="flex items-start">
<CheckCircle className="mr-2 mt-0.5 h-5 w-5 shrink-0 text-green-500" />
<span>Empowered developers, happier teams</span>
</li>
<li className="flex items-start">
<CheckCircle className="mr-2 mt-0.5 h-5 w-5 shrink-0 text-green-500" />
<span>Rapid knowledge sharing</span>
</li>
</ul>
</div>
</div>
</div>
</section>
{/* Differentiator Section */}
<section className="py-16">
<div className="container mx-auto px-4 sm:px-6 lg:px-8">
<div className="mb-12 text-center">
<h2 className="text-3xl font-bold tracking-tight sm:text-4xl">What Makes Roo Code Unique</h2>
<p className="mx-auto mt-4 max-w-2xl text-lg text-muted-foreground">
Unlike traditional code editors or basic autocomplete tools, Roo Code is an autonomous
coding agent with powerful capabilities.
</p>
</div>
<div className="grid gap-8 md:grid-cols-2">
<div className="rounded-lg border border-border bg-card p-8 shadow-sm">
<h3 className="mb-4 text-2xl font-bold">Traditional AI Coding Assistants</h3>
<ul className="space-y-3">
<li className="flex items-start">
<svg
className="mr-2 mt-0.5 h-5 w-5 text-red-500"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor">
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M6 18L18 6M6 6l12 12"
/>
</svg>
<span>Limited to autocomplete and simple suggestions</span>
</li>
<li className="flex items-start">
<svg
className="mr-2 mt-0.5 h-5 w-5 text-red-500"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor">
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M6 18L18 6M6 6l12 12"
/>
</svg>
<span>Lack project-wide context understanding</span>
</li>
<li className="flex items-start">
<svg
className="mr-2 mt-0.5 h-5 w-5 text-red-500"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor">
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M6 18L18 6M6 6l12 12"
/>
</svg>
<span>Can&apos;t execute commands or perform web actions</span>
</li>
<li className="flex items-start">
<svg
className="mr-2 mt-0.5 h-5 w-5 text-red-500"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor">
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M6 18L18 6M6 6l12 12"
/>
</svg>
<span>No customization for enterprise workflows</span>
</li>
<li className="flex items-start">
<svg
className="mr-2 mt-0.5 h-5 w-5 text-red-500"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor">
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M6 18L18 6M6 6l12 12"
/>
</svg>
<span>Often require sending code to external cloud services</span>
</li>
</ul>
</div>
<div className="rounded-lg border border-border bg-card p-8 shadow-sm">
<h3 className="mb-4 text-2xl font-bold text-blue-400">Roo Code Enterprise</h3>
<ul className="space-y-3">
<li className="flex items-start">
<CheckCircle className="mr-2 mt-0.5 h-5 w-5 text-green-500" />
<span>Full-featured AI dev team with natural language communication</span>
</li>
<li className="flex items-start">
<CheckCircle className="mr-2 mt-0.5 h-5 w-5 text-green-500" />
<span>Deep understanding of your entire codebase</span>
</li>
<li className="flex items-start">
<CheckCircle className="mr-2 mt-0.5 h-5 w-5 text-green-500" />
<span>Can run tests, execute commands, and perform web actions</span>
</li>
<li className="flex items-start">
<CheckCircle className="mr-2 mt-0.5 h-5 w-5 text-green-500" />
<span>Custom modes for specialized enterprise tasks</span>
</li>
<li className="flex items-start">
<CheckCircle className="mr-2 mt-0.5 h-5 w-5 text-green-500" />
<span>On-premises deployment option for data privacy</span>
</li>
</ul>
</div>
</div>
</div>
</section>
{/* CTA Section */}
<section id="contact" className="bg-secondary/50 py-16">
<div className="container mx-auto px-4 sm:px-6 lg:px-8">
<div className="mx-auto max-w-3xl text-center">
<h2 className="mb-4 text-3xl font-bold tracking-tight sm:text-4xl">
Ready to Transform Your Development Process?
</h2>
<p className="mb-8 text-lg text-muted-foreground">
Join our early access program and be among the first to experience the power of Roo Code for
Enterprise.
</p>
<div className="grid gap-4 sm:grid-cols-2 sm:gap-6">
<div className="rounded-lg border border-border bg-card p-6 text-center shadow-sm">
<h3 className="mb-2 text-xl font-bold">Become an Early Access Partner</h3>
<p className="mb-4 text-muted-foreground">
Collaborate in shaping Roo Code&apos;s enterprise solution.
</p>
<ContactForm formType="early-access" buttonText="Apply Now" buttonClassName="w-full" />
</div>
<div className="rounded-lg border border-border bg-card p-6 text-center shadow-sm">
<h3 className="mb-2 text-xl font-bold">Request a Demo</h3>
<p className="mb-4 text-muted-foreground">
See Roo Code&apos;s enterprise capabilities in action.
</p>
<ContactForm formType="demo" buttonText="Contact Us" buttonClassName="w-full" />
</div>
</div>
<div className="mt-8">
<Button variant="outline" size="lg">
<a
href="mailto:enterprise@roocode.com?subject=Enterprise Guide Request"
className="flex items-center justify-center">
Download the Enterprise Guide
</a>
</Button>
</div>
</div>
</div>
</section>
</>
)
}

View file

@ -1,224 +0,0 @@
"use client"
import { useMemo } from "react"
import { ScatterChart, Scatter, XAxis, YAxis, Label, Customized, Cross } from "recharts"
import { TaskMetrics, type Run } from "@/db"
import { ChartConfig, ChartLegend, ChartLegendContent } from "@/components/ui/chart"
import { formatTokens, formatCurrency, formatDuration, formatScore } from "@/lib"
import {
ChartContainer,
ChartTooltip,
ChartTooltipContent,
Table,
TableBody,
TableCaption,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui"
import { useOpenRouterModels } from "@/lib/hooks/use-open-router-models"
export function Evals({
runs,
}: {
runs: (Run & {
label: string
score: number
languageScores?: Record<"go" | "java" | "javascript" | "python" | "rust", number>
taskMetrics: TaskMetrics
modelId?: string
})[]
}) {
const { data: openRouterModels } = useOpenRouterModels()
const tableData = useMemo(
() =>
runs.map((run) => ({
...run,
label: run.description || run.model,
score: run.score,
cost: run.taskMetrics.cost,
model: openRouterModels?.[run.modelId ?? ""],
modelInfo: openRouterModels?.[run.modelId ?? ""]?.modelInfo,
})),
[runs, openRouterModels],
)
const chartData = useMemo(() => tableData.filter(({ cost }) => cost < 100), [tableData])
const chartConfig = useMemo(
() => chartData.reduce((acc, run) => ({ ...acc, [run.label]: run }), {} as ChartConfig),
[chartData],
)
return (
<div className="mx-auto flex max-w-screen-lg flex-col gap-8 p-8">
<div className="flex flex-col gap-4">
<div>
Roo Code tests each frontier model against{" "}
<a href="https://github.com/cte/evals/" className="underline">
a suite of hundreds of exercises
</a>{" "}
across 5 programming languages with varying difficulty. These results can help you find the right
price-to-intelligence ratio for your use case.
</div>
<div>
Want to see the results for a model we haven&apos;t tested yet? Ping us in{" "}
<a href="https://discord.gg/roocode" className="underline">
Discord
</a>
.
</div>
</div>
<Table className="border">
<TableHeader>
<TableRow>
<TableHead colSpan={2} className="border-r text-center">
Model
</TableHead>
<TableHead colSpan={3} className="border-r text-center">
Metrics
</TableHead>
<TableHead colSpan={6} className="text-center">
Scores
</TableHead>
</TableRow>
<TableRow>
<TableHead>
Name
<div className="text-xs opacity-50">Context Window</div>
</TableHead>
<TableHead className="border-r">
Price
<div className="text-xs opacity-50">In / Out</div>
</TableHead>
<TableHead>Duration</TableHead>
<TableHead>
Tokens
<div className="text-xs opacity-50">In / Out</div>
</TableHead>
<TableHead className="border-r">
Cost
<div className="text-xs opacity-50">USD</div>
</TableHead>
<TableHead>
<i className="devicon-go-plain text-lg" title="Go" />
</TableHead>
<TableHead>
<i className="devicon-java-plain text-lg" title="Java" />
</TableHead>
<TableHead>
<i className="devicon-javascript-plain text-lg" title="JavaScript" />
</TableHead>
<TableHead>
<i className="devicon-python-plain text-lg" title="Python" />
</TableHead>
<TableHead>
<i className="devicon-rust-original text-lg" title="Rust" />
</TableHead>
<TableHead>Total</TableHead>
</TableRow>
</TableHeader>
<TableBody className="font-mono">
{tableData.map((run) => (
<TableRow key={run.id}>
<TableCell title={run.model?.description}>
<div className="font-sans">{run.model?.name || run.label}</div>
<div className="text-xs opacity-50">
{formatTokens(run.modelInfo?.contextWindow ?? 0)}
</div>
</TableCell>
<TableCell className="border-r">
<div className="flex flex-row gap-2">
<div>{formatCurrency(run.modelInfo?.inputPrice ?? 0)}</div>
<div className="opacity-25">/</div>
<div>{formatCurrency(run.modelInfo?.outputPrice ?? 0)}</div>
</div>
</TableCell>
<TableCell className="font-mono">{formatDuration(run.taskMetrics.duration)}</TableCell>
<TableCell>
<div className="flex flex-row gap-2">
<div>{formatTokens(run.taskMetrics.tokensIn)}</div>
<div className="opacity-25">/</div>
<div>{formatTokens(run.taskMetrics.tokensOut)}</div>
</div>
</TableCell>
<TableCell className="border-r">{formatCurrency(run.taskMetrics.cost)}</TableCell>
<TableCell className="text-muted-foreground">
{formatScore(run.languageScores?.go ?? 0)}%
</TableCell>
<TableCell className="text-muted-foreground">
{formatScore(run.languageScores?.java ?? 0)}%
</TableCell>
<TableCell className="text-muted-foreground">
{formatScore(run.languageScores?.javascript ?? 0)}%
</TableCell>
<TableCell className="text-muted-foreground">
{formatScore(run.languageScores?.python ?? 0)}%
</TableCell>
<TableCell className="text-muted-foreground">
{formatScore(run.languageScores?.rust ?? 0)}%
</TableCell>
<TableCell className="font-bold">{run.score}%</TableCell>
</TableRow>
))}
</TableBody>
<TableCaption>
<div className="pb-4 font-medium">Cost Versus Score</div>
<ChartContainer config={chartConfig} className="h-[500px] w-full">
<ScatterChart margin={{ top: 0, right: 0, bottom: 0, left: 20 }}>
<XAxis
type="number"
dataKey="cost"
name="Cost"
domain={[
(dataMin: number) => Math.round((dataMin - 5) / 5) * 5,
(dataMax: number) => Math.round((dataMax + 5) / 5) * 5,
]}
tickFormatter={(value) => formatCurrency(value)}>
<Label value="Cost" position="bottom" offset={0} />
</XAxis>
<YAxis
type="number"
dataKey="score"
name="Score"
domain={[
(dataMin: number) => Math.max(0, Math.round((dataMin - 5) / 5) * 5),
(dataMax: number) => Math.min(100, Math.round((dataMax + 5) / 5) * 5),
]}
tickFormatter={(value) => `${value}%`}>
<Label value="Score" angle={-90} position="left" dy={-15} />
</YAxis>
<ChartTooltip content={<ChartTooltipContent labelKey="label" hideIndicator />} />
<Customized component={renderQuadrant} />
{chartData.map((d, i) => (
<Scatter key={d.label} name={d.label} data={[d]} fill={`hsl(var(--chart-${i + 1}))`} />
))}
<ChartLegend content={<ChartLegendContent />} />
</ScatterChart>
</ChartContainer>
<div className="py-4 text-xs opacity-50">
(Note: Very expensive models are exluded from the scatter plot.)
</div>
</TableCaption>
</Table>
</div>
)
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const renderQuadrant = (props: any) => (
<Cross
width={props.width}
height={props.height}
x={props.width / 2 + 35}
y={props.height / 2 - 15}
top={0}
left={0}
stroke="currentColor"
opacity={0.1}
/>
)

View file

@ -1,49 +0,0 @@
import type { Metadata } from "next"
import { rooCodeSettingsSchema, getModelId } from "@roo-code/types"
import { getRuns } from "@/db"
import { getLanguageScores } from "@/lib/server"
import { formatScore } from "@/lib"
import { Evals } from "./evals"
export const revalidate = 300
export const metadata: Metadata = {
title: "Roo Code Evals",
openGraph: {
title: "Roo Code Evals",
description: "Quantitative evals of LLM coding skills.",
url: "https://roocode.com/evals",
siteName: "Roo Code",
images: {
url: "https://i.imgur.com/ijP7aZm.png",
width: 1954,
height: 1088,
},
},
}
export default async function Page() {
const languageScores = await getLanguageScores()
const runs = (await getRuns())
.filter((run) => !!run.taskMetrics)
.filter(({ settings }) => rooCodeSettingsSchema.safeParse(settings).success)
.sort((a, b) => b.passed - a.passed)
.map((run) => {
const settings = rooCodeSettingsSchema.parse(run.settings)
return {
...run,
label: run.description || run.model,
score: formatScore(run.passed / (run.passed + run.failed)),
languageScores: languageScores[run.id],
taskMetrics: run.taskMetrics!,
modelId: getModelId(settings),
}
})
return <Evals runs={runs} />
}

View file

@ -1,72 +0,0 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
:root {
--background: 0 0% 100%;
--foreground: 0 0% 3.9%;
--card: 0 0% 100%;
--card-foreground: 0 0% 3.9%;
--popover: 0 0% 100%;
--popover-foreground: 0 0% 3.9%;
--primary: 0 0% 9%;
--primary-foreground: 0 0% 98%;
--secondary: 0 0% 96.1%;
--secondary-foreground: 0 0% 9%;
--muted: 0 0% 96.1%;
--muted-foreground: 0 0% 45.1%;
--accent: 0 0% 96.1%;
--accent-foreground: 0 0% 9%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 0 0% 98%;
--border: 0 0% 89.8%;
--input: 0 0% 89.8%;
--ring: 0 0% 3.9%;
--radius: 0.5rem;
--chart-1: 0 100% 50%;
--chart-2: 29 100% 50%;
--chart-3: 51 100% 50%;
--chart-4: 83 100% 50%;
--chart-5: 115 100% 50%;
--chart-6: 147 100% 50%;
--chart-7: 168 100% 50%;
--chart-8: 196 100% 50%;
--chart-9: 224 100% 50%;
--chart-10: 261 100% 50%;
--chart-11: 279 100% 50%;
--chart-12: 298 100% 50%;
--chart-13: 324 100% 50%;
--chart-14: 358 100% 50%;
}
.dark {
--background: 0 0% 0%;
--foreground: 0 0% 98%;
--card: 0 0% 3.9%;
--card-foreground: 0 0% 98%;
--popover: 0 0% 3.9%;
--popover-foreground: 0 0% 98%;
--primary: 0 0% 98%;
--primary-foreground: 0 0% 9%;
--secondary: 0 0% 14.9%;
--secondary-foreground: 0 0% 98%;
--muted: 0 0% 14.9%;
--muted-foreground: 0 0% 63.9%;
--accent: 0 0% 14.9%;
--accent-foreground: 0 0% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 0 0% 98%;
--border: 0 0% 14.9%;
--input: 0 0% 14.9%;
--ring: 0 0% 83.1%;
}
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
}
}

View file

@ -1,65 +0,0 @@
import React from "react"
import type { Metadata } from "next"
import { Inter } from "next/font/google"
import { Providers } from "@/components/providers"
import Shell from "./shell"
import "./globals.css"
const inter = Inter({ subsets: ["latin"] })
export const metadata: Metadata = {
title: "Roo Code Your AI-Powered Dev Team in VS Code",
description:
"Roo Code puts an entire AI dev team right in your editor, outpacing closed tools with deep project-wide context, multi-step agentic coding, and unmatched developer-centric flexibility.",
alternates: {
canonical: "https://roocode.com",
},
icons: {
icon: [
{ url: "/favicon.ico" },
{ url: "/favicon-16x16.png", sizes: "16x16", type: "image/png" },
{ url: "/favicon-32x32.png", sizes: "32x32", type: "image/png" },
],
apple: [{ url: "/apple-touch-icon.png" }],
other: [
{
rel: "android-chrome-192x192",
url: "/android-chrome-192x192.png",
sizes: "192x192",
type: "image/png",
},
{
rel: "android-chrome-512x512",
url: "/android-chrome-512x512.png",
sizes: "512x512",
type: "image/png",
},
],
},
}
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" suppressHydrationWarning>
<head>
<link
rel="stylesheet"
type="text/css"
href="https://cdn.jsdelivr.net/gh/devicons/devicon@latest/devicon.min.css"
/>
</head>
<body className={inter.className}>
<div itemScope itemType="https://schema.org/WebSite">
<link itemProp="url" href="https://roocode.com" />
<meta itemProp="name" content="Roo Code" />
</div>
<Providers>
<Shell>{children}</Shell>
</Providers>
</body>
</html>
)
}

View file

@ -1,98 +0,0 @@
/* eslint-disable react/jsx-no-target-blank */
import { getVSCodeDownloads } from "@/lib/stats"
import { Button } from "@/components/ui"
import { AnimatedText } from "@/components/animated-text"
import {
AnimatedBackground,
InstallSection,
Features,
Testimonials,
FAQSection,
CodeExample,
} from "@/components/homepage"
// Invalidate cache when a request comes in, at most once every hour.
export const revalidate = 3600
export default async function Home() {
const downloads = await getVSCodeDownloads()
return (
<>
<section className="relative flex h-[calc(125vh-theme(spacing.16))] items-center overflow-hidden md:h-[calc(100svh-theme(spacing.16))] lg:h-[calc(100vh-theme(spacing.16))]">
<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>
<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, Right in Your Editor.</span>
</h1>
<p className="mt-4 max-w-md text-base text-muted-foreground sm:mt-6 sm:text-lg">
Supercharge your editor with AI that{" "}
<AnimatedText className="bg-gradient-to-r from-blue-400 to-cyan-400 bg-clip-text text-transparent">
understands your codebase
</AnimatedText>
, streamlines development, and helps you write, refactor, and debug with ease.
</p>
</div>
<div className="flex flex-col space-y-3 sm:flex-row sm:space-x-4 sm:space-y-0">
<Button
size="lg"
className="w-full hover:bg-gray-200 dark:bg-white dark:text-black sm:w-auto">
<a
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>
</a>
</Button>
<Button variant="outline" size="lg" className="w-full sm:w-auto">
<a
href="https://docs.roocode.com"
target="_blank"
className="flex w-full items-center justify-center">
View Documentation
</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>
<CodeExample />
</div>
</div>
</div>
</section>
<div id="features">
<Features />
</div>
<div id="testimonials">
<Testimonials />
</div>
<div id="faq">
<FAQSection />
</div>
<InstallSection downloads={downloads} />
</>
)
}

View file

@ -1,177 +0,0 @@
import { Metadata } from "next"
export const metadata: Metadata = {
title: "Privacy Policy - Roo Code Marketing Website",
description:
"Privacy policy for the Roo Code marketing website. Learn how we handle your data and protect your privacy.",
}
export default function Privacy() {
return (
<>
<div className="container mx-auto px-4 py-12 sm:px-6 lg:px-8">
<div className="prose prose-lg mx-auto max-w-3xl dark:prose-invert">
<h1 className="text-3xl font-bold tracking-tight sm:text-4xl md:text-5xl">
Roo Code Marketing Landing Page Privacy Policy
</h1>
<p className="text-muted-foreground">Last Updated: March 7th, 2025</p>
<p className="lead">
Roo Code respects your privacy and is committed to being transparent about how data is collected
and used on our marketing landing page. This policy focuses on data handling for the Roo Code
marketing website. For details on how your data is handled within the Roo Code extension itself,
please refer to our separate{" "}
<a
href="https://github.com/RooVetGit/Roo-Code/blob/main/PRIVACY.md"
target="_blank"
rel="noopener noreferrer"
className="text-primary hover:underline">
Roo Code Extension Privacy Policy
</a>
.
</p>
<h2 className="mt-8 text-2xl font-bold">Where Your Data Goes (And Where It Doesn&apos;t)</h2>
<h3 className="mt-6 text-xl font-bold">Website Analytics & Tracking</h3>
<ul>
<li>
We use PostHog (and its standard features) on our marketing landing page to analyze site
traffic and usage trends. This collection includes information such as your IP address,
browser type, device information, and pages visited.
</li>
<li>
These analytics help us understand how users engage with the website, so we can improve
content and design.
</li>
<li>We do not collect code, project data, or any AI-related prompts on this page.</li>
</ul>
<h3 className="mt-6 text-xl font-bold">Cookies and Similar Technologies</h3>
<ul>
<li>
Our marketing website may use cookies or similar tracking technologies to remember user
preferences and provide aggregated analytics.
</li>
<li>
Cookies help with things like user session management, remembering certain selections or
preferences, and compiling anonymous statistics.
</li>
</ul>
<h3 className="mt-6 text-xl font-bold">Forms & Voluntary Submissions</h3>
<ul>
<li>
If you submit your email or other personal data on our landing page (for example, to receive
updates or join a waiting list), we collect that information voluntarily provided by you.
</li>
<li>
We do not share or sell this data to third parties for their own marketing purposes. It is
used only to communicate with you about Roo Code, respond to inquiries, or send updates
you&apos;ve requested.
</li>
</ul>
<h3 className="mt-6 text-xl font-bold">Third-Party Integrations</h3>
<ul>
<li>
Our website may embed content or links to external platforms (e.g., for processing payments
or handling support). Any data you provide through these external sites is governed by the
privacy policies of those platforms.
</li>
</ul>
<h2 className="mt-8 text-2xl font-bold">How We Use Your Data</h2>
<h3 className="mt-6 text-xl font-bold">Site Improvements & Marketing</h3>
<ul>
<li>
We analyze aggregated user behavior to measure the effectiveness of our site, troubleshoot
any issues, and guide future improvements.
</li>
<li>
If you sign up for newsletters or updates, we use your email or other contact information
only to send you relevant Roo Code communications.
</li>
</ul>
<h3 className="mt-6 text-xl font-bold">No Selling or Sharing of Data</h3>
<ul>
<li>
We do not sell or share your personally identifiable information with third parties for
their marketing.
</li>
<li>We do not train any models on your marketing site data.</li>
</ul>
<h2 className="mt-8 text-2xl font-bold">Your Choices & Control</h2>
<h3 className="mt-6 text-xl font-bold">Manage Cookies</h3>
<ul>
<li>
Most browsers allow you to manage or block cookies. If you disable cookies, some features of
the site may not function properly.
</li>
</ul>
<h3 className="mt-6 text-xl font-bold">Opt-Out of Communications</h3>
<ul>
<li>
If you have signed up to receive updates, you can unsubscribe anytime by following the
instructions in our emails or contacting us directly.
</li>
</ul>
<h3 className="mt-6 text-xl font-bold">Request Deletion</h3>
<ul>
<li>
You may request the deletion of any personal data you&apos;ve provided through our marketing
forms by reaching out to us at{" "}
<a href="mailto:support@roocode.com" className="text-primary hover:underline">
support@roocode.com
</a>
.
</li>
</ul>
<h2 className="mt-8 text-2xl font-bold">Security & Updates</h2>
<ul>
<li>
We take reasonable measures to protect your data from unauthorized access or disclosure, but
no website can be 100% secure.
</li>
<li>
If our privacy practices for the marketing site change, we will update this policy and note
the effective date at the top.
</li>
</ul>
<h2 className="mt-8 text-2xl font-bold">Contact Us</h2>
<p>
If you have questions or concerns about this Privacy Policy or wish to make a request regarding
your data, please reach out to us at{" "}
<a href="mailto:support@roocode.com" className="text-primary hover:underline">
support@roocode.com
</a>
.
</p>
<div className="mt-8 border-t border-border pt-6">
<p className="text-muted-foreground">
By using the Roo Code marketing landing page, you agree to this Privacy Policy. If you use
the Roo Code extension, please see our separate{" "}
<a
href="https://github.com/RooVetGit/Roo-Code/blob/main/PRIVACY.md"
target="_blank"
rel="noopener noreferrer"
className="text-primary hover:underline">
Roo Code Extension Privacy Policy
</a>{" "}
for details on data handling in the extension.
</p>
</div>
</div>
</div>
</>
)
}

View file

@ -1,18 +0,0 @@
import { getGitHubStars, getVSCodeDownloads } from "@/lib/stats"
import { NavBar, Footer } from "@/components/chromes"
// Invalidate cache when a request comes in, at most once every hour.
export const revalidate = 3600
export default async function Shell({ children }: { children: React.ReactNode }) {
const [stars, downloads] = await Promise.all([getGitHubStars(), getVSCodeDownloads()])
return (
<div className="flex min-h-screen flex-col bg-background text-foreground">
<NavBar stars={stars} downloads={downloads} />
<main className="flex-1">{children}</main>
<Footer />
</div>
)
}

View file

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://roocode.com/</loc>
<lastmod>2025-03-13T22:26:09Z</lastmod>
<changefreq>yearly</changefreq>
<priority>1.0</priority>
</url>
</urlset>

View file

@ -1,24 +0,0 @@
"use client"
import type React from "react"
import { motion } from "framer-motion"
interface AnimatedTextProps {
children: React.ReactNode
className?: string
}
export function AnimatedText({ children, className }: AnimatedTextProps) {
return (
<motion.span
className={className}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{
duration: 0.8,
ease: [0.2, 0.65, 0.3, 0.9],
}}>
{children}
</motion.span>
)
}

View file

@ -1,269 +0,0 @@
"use client"
import { useState, useRef, useEffect } from "react"
import Link from "next/link"
import Image from "next/image"
import { ChevronDown } from "lucide-react"
import { RxGithubLogo, RxDiscordLogo } from "react-icons/rx"
import { FaReddit } from "react-icons/fa6"
import { EXTERNAL_LINKS, INTERNAL_LINKS } from "@/lib/constants"
import { useLogoSrc } from "@/lib/hooks/use-logo-src"
import { ScrollButton } from "@/components/ui"
export function Footer() {
const [privacyDropdownOpen, setPrivacyDropdownOpen] = useState(false)
const dropdownRef = useRef<HTMLDivElement>(null)
const logoSrc = useLogoSrc()
// Close dropdown when clicking outside
useEffect(() => {
function handleClickOutside(event: MouseEvent) {
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
setPrivacyDropdownOpen(false)
}
}
document.addEventListener("mousedown", handleClickOutside)
return () => {
document.removeEventListener("mousedown", handleClickOutside)
}
}, [])
return (
<footer className="border-t border-border bg-background">
<div className="mx-auto max-w-7xl px-6 pb-6 pt-12 md:pb-8 md:pt-16 lg:px-8">
<div className="xl:grid xl:grid-cols-3 xl:gap-8">
<div className="space-y-8">
<div className="flex items-center">
<Image src={logoSrc} alt="Roo Code Logo" width={120} height={40} className="h-6 w-auto" />
</div>
<p className="max-w-md text-sm leading-6 text-muted-foreground md:pr-16 lg:pr-32">
Empowering developers to build better software faster with AI-powered tools and insights.
</p>
<div className="flex space-x-5">
<a
href={EXTERNAL_LINKS.GITHUB}
target="_blank"
rel="noopener noreferrer"
className="text-muted-foreground transition-colors hover:text-foreground">
<RxGithubLogo className="h-6 w-6" />
<span className="sr-only">GitHub</span>
</a>
<a
href={EXTERNAL_LINKS.DISCORD}
target="_blank"
rel="noopener noreferrer"
className="text-muted-foreground transition-colors hover:text-foreground">
<RxDiscordLogo className="h-6 w-6" />
<span className="sr-only">Discord</span>
</a>
<a
href={EXTERNAL_LINKS.REDDIT}
target="_blank"
rel="noopener noreferrer"
className="text-muted-foreground transition-colors hover:text-foreground">
<FaReddit className="h-6 w-6" />
<span className="sr-only">Reddit</span>
</a>
</div>
</div>
<div className="mt-16 grid grid-cols-2 gap-8 xl:col-span-2 xl:mt-0">
<div className="md:grid md:grid-cols-2 md:gap-8">
<div>
<h3 className="text-sm font-semibold uppercase leading-6 text-foreground">Product</h3>
<ul className="mt-6 space-y-4">
<li>
<ScrollButton
targetId="features"
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>
<ScrollButton
targetId="testimonials"
className="text-sm leading-6 text-muted-foreground transition-colors hover:text-foreground">
Testimonials
</ScrollButton>
</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
</a>
</li>
<li>
<a
href={EXTERNAL_LINKS.CHANGELOG}
target="_blank"
rel="noopener noreferrer"
className="text-sm leading-6 text-muted-foreground transition-colors hover:text-foreground">
Changelog
</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.DOCUMENTATION}
target="_blank"
rel="noopener noreferrer"
className="text-sm leading-6 text-muted-foreground transition-colors hover:text-foreground">
Documentation
</a>
</li>
<li>
<a
href={EXTERNAL_LINKS.TUTORIALS}
target="_blank"
rel="noopener noreferrer"
className="text-sm leading-6 text-muted-foreground transition-colors hover:text-foreground">
Tutorials
</a>
</li>
<li>
<a
href={EXTERNAL_LINKS.COMMUNITY}
target="_blank"
rel="noopener noreferrer"
className="text-sm leading-6 text-muted-foreground transition-colors hover:text-foreground">
Community
</a>
</li>
<li>
<a
href={EXTERNAL_LINKS.DISCORD}
target="_blank"
rel="noopener noreferrer"
className="text-sm leading-6 text-muted-foreground transition-colors hover:text-foreground">
Discord
</a>
</li>
<li>
<a
href={EXTERNAL_LINKS.REDDIT}
target="_blank"
rel="noopener noreferrer"
className="text-sm leading-6 text-muted-foreground transition-colors hover:text-foreground">
Reddit
</a>
</li>
</ul>
</div>
</div>
<div className="md:grid md:grid-cols-2 md:gap-8">
<div>
<h3 className="text-sm font-semibold uppercase leading-6 text-foreground">Support</h3>
<ul className="mt-6 space-y-4">
<li>
<a
href={EXTERNAL_LINKS.ISSUES}
target="_blank"
rel="noopener noreferrer"
className="text-sm leading-6 text-muted-foreground transition-colors hover:text-foreground">
Issues
</a>
</li>
<li>
<a
href={EXTERNAL_LINKS.FEATURE_REQUESTS}
target="_blank"
rel="noopener noreferrer"
className="text-sm leading-6 text-muted-foreground transition-colors hover:text-foreground">
Feature Requests
</a>
</li>
<li>
<ScrollButton
targetId="faq"
className="text-sm leading-6 text-muted-foreground transition-colors hover:text-foreground">
FAQ
</ScrollButton>
</li>
</ul>
</div>
<div className="mt-10 md:mt-0">
<h3 className="text-sm font-semibold uppercase leading-6 text-foreground">Company</h3>
<ul className="mt-6 space-y-4">
<li>
<a
href="mailto:support@roocode.com"
className="text-sm leading-6 text-muted-foreground transition-colors hover:text-foreground">
Contact
</a>
</li>
<li>
<a
href={EXTERNAL_LINKS.CAREERS}
target="_blank"
rel="noopener noreferrer"
className="text-sm leading-6 text-muted-foreground transition-colors hover:text-foreground">
Careers
</a>
</li>
<li>
<div className="relative z-10" ref={dropdownRef}>
<button
onClick={() => setPrivacyDropdownOpen(!privacyDropdownOpen)}
className="flex items-center text-sm leading-6 text-muted-foreground transition-colors hover:text-foreground"
aria-expanded={privacyDropdownOpen}
aria-haspopup="true">
<span>
Privacy <span className="max-[320px]:hidden">Policy</span>
</span>
<ChevronDown
className={`ml-1 h-4 w-4 transition-transform ${privacyDropdownOpen ? "rotate-180" : ""}`}
/>
</button>
{privacyDropdownOpen && (
<div className="absolute z-50 mt-2 w-44 origin-top-left scale-95 rounded-md border border-border bg-background shadow-lg ring-1 ring-black ring-opacity-5 transition-all duration-100 ease-out data-[state=open]:scale-100 max-xs:right-0 max-xs:origin-top-right xs:left-0">
<div className="flex flex-col gap-1 p-2 text-sm text-muted-foreground">
<a
href={EXTERNAL_LINKS.PRIVACY_POLICY_EXTENSION}
target="_blank"
rel="noopener noreferrer"
onClick={() => setPrivacyDropdownOpen(false)}
className="rounded-md px-3 py-2 transition-colors hover:bg-accent/50 hover:text-foreground">
Extension
</a>
<Link
href={INTERNAL_LINKS.PRIVACY_POLICY_WEBSITE}
onClick={() => setPrivacyDropdownOpen(false)}
className="rounded-md px-3 py-2 transition-colors hover:bg-accent/50 hover:text-foreground">
Marketing Website
</Link>
</div>
</div>
)}
</div>
</li>
</ul>
</div>
</div>
</div>
</div>
<div className="mt-16 flex border-t border-border pt-8 sm:mt-20 lg:mt-24">
<p className="mx-auto text-sm leading-5 text-muted-foreground">
&copy; {new Date().getFullYear()} Roo Code. All rights reserved.
</p>
</div>
</div>
</footer>
)
}

View file

@ -1,4 +0,0 @@
export * from "./footer"
export * from "./nav-bar"
export * from "./stats-display"
export * from "./theme-toggle"

View file

@ -1,188 +0,0 @@
/* eslint-disable react/jsx-no-target-blank */
"use client"
import Link from "next/link"
import Image from "next/image"
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"
interface NavBarProps {
stars: string | null
downloads: string | null
}
export function NavBar({ stars, downloads }: NavBarProps) {
const [isMenuOpen, setIsMenuOpen] = useState(false)
const logoSrc = useLogoSrc()
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="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" />
</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 */}
<ScrollButton
targetId="features"
className="text-muted-foreground transition-transform duration-200 hover:scale-105 hover:text-foreground max-lg:hidden">
Features
</ScrollButton>
<ScrollButton
targetId="testimonials"
className="text-muted-foreground transition-transform duration-200 hover:scale-105 hover:text-foreground max-lg:hidden">
Testimonials
</ScrollButton>
<ScrollButton
targetId="faq"
className="text-muted-foreground transition-transform duration-200 hover:scale-105 hover:text-foreground">
FAQ
</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">
Documentation
</a>
<a
href={EXTERNAL_LINKS.CAREERS}
target="_blank"
className="text-muted-foreground transition-transform duration-200 hover:scale-105 hover:text-foreground">
Careers
</a>
</nav>
<div className="hidden md:flex md:items-center md:space-x-4">
<div className="flex flex-row space-x-2">
<ThemeToggle />
<Link
href={EXTERNAL_LINKS.GITHUB}
target="_blank"
className="hidden items-center gap-1.5 text-sm font-medium text-muted-foreground hover:text-foreground md:flex">
<RxGithubLogo className="h-4 w-4" />
{stars !== null && <span>{stars}</span>}
</Link>
</div>
<Link
href={EXTERNAL_LINKS.MARKETPLACE}
target="_blank"
className="hidden items-center gap-1.5 rounded-full bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground transition-colors hover:bg-primary/90 md:flex">
<VscVscode className="-mr-[2px] mt-[1px] h-4 w-4" />
<span>
Install <span className="font-black max-lg:text-xs">&middot;</span>
</span>
{downloads !== null && <span>{downloads}</span>}
</Link>
</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"
aria-label="Toggle mobile menu">
{isMenuOpen ? <IoClose className="h-6 w-6" /> : <HiMenu className="h-6 w-6" />}
</button>
</div>
{/* Mobile Menu Panel */}
<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>
<ScrollButton
targetId="faq"
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)}>
FAQ
</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.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)}>
Documentation
</a>
<a
href={EXTERNAL_LINKS.CAREERS}
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)}>
Careers
</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"
onClick={() => setIsMenuOpen(false)}>
<RxGithubLogo className="h-5 w-5" />
{stars !== null && <span>{stars}</span>}
</Link>
<div className="flex items-center rounded-md p-2 transition-colors hover:bg-accent">
<ThemeToggle />
</div>
<Link
href={EXTERNAL_LINKS.MARKETPLACE}
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"
onClick={() => setIsMenuOpen(false)}>
<VscVscode className="h-5 w-5" />
{downloads !== null && <span>{downloads}</span>}
</Link>
</div>
</nav>
</div>
</header>
)
}

View file

@ -1,31 +0,0 @@
import Link from "next/link"
import { RxGithubLogo } from "react-icons/rx"
import { VscVscode } from "react-icons/vsc"
import { getGitHubStars, getVSCodeDownloads } from "@/lib/stats"
export default async function StatsDisplay() {
const stars = await getGitHubStars()
const downloads = await getVSCodeDownloads()
return (
<>
<Link
href="https://github.com/RooVetGit/Roo-Code"
target="_blank"
className="hidden md:flex items-center gap-1.5 text-sm font-medium text-gray-400 hover:text-gray-200">
<RxGithubLogo className="h-4 w-4" />
{stars !== null && <span>{stars}</span>}
</Link>
<Link
href="https://marketplace.visualstudio.com/items?itemName=RooVeterinaryInc.roo-cline"
target="_blank"
className="hidden md:flex items-center gap-1.5 rounded-full bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground transition-colors hover:bg-primary/90">
<VscVscode className="h-4 w-4" />
<span>
Install <span className="font-black">&middot;</span>
</span>
{downloads !== null && <span>{downloads}</span>}
</Link>
</>
)
}

View file

@ -1,40 +0,0 @@
"use client"
import { useEffect, useState } from "react"
import { useTheme } from "next-themes"
import { RxSun, RxMoon } from "react-icons/rx"
import { Button } from "@/components/ui"
export default function ThemeToggle() {
const { theme, setTheme } = useTheme()
const [mounted, setMounted] = useState(false)
// Avoid hydration mismatch.
useEffect(() => {
setMounted(true)
}, [])
if (!mounted) {
return (
<Button variant="ghost" size="icon" disabled className="h-9 w-9">
<RxSun className="h-4 w-4" />
</Button>
)
}
return (
<Button
variant="ghost"
size="icon"
onClick={() => setTheme(theme === "dark" ? "light" : "dark")}
className="h-9 w-9"
aria-label="Toggle theme">
{theme === "dark" ? (
<RxSun className="h-4 w-4 transition-all" />
) : (
<RxMoon className="h-4 w-4 transition-all" />
)}
</Button>
)
}

View file

@ -1,291 +0,0 @@
"use client"
import { useState, useRef } from "react"
import { z } from "zod"
import {
Button,
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui"
// Define the form schema using Zod
const contactFormSchema = z.object({
name: z.string().min(1, "Name is required"),
company: z.string().min(1, "Company is required"),
email: z.string().email("Invalid email address"),
website: z.string().url("Invalid website URL").or(z.string().length(0)),
engineerCount: z.enum(["1-10", "11-50", "51-200", "201-500", "501-1000", "1000+"]),
formType: z.enum(["early-access", "demo"]),
_honeypot: z.string().optional(),
})
interface ContactFormProps {
formType: "early-access" | "demo"
buttonText: string
buttonClassName?: string
}
export function ContactForm({ formType, buttonText, buttonClassName }: ContactFormProps) {
const [isOpen, setIsOpen] = useState(false)
const [isSubmitting, setIsSubmitting] = useState(false)
const [formErrors, setFormErrors] = useState<Record<string, string>>({})
const [submitStatus, setSubmitStatus] = useState<"idle" | "success" | "error">("idle")
const formRef = useRef<HTMLFormElement>(null)
const formTitle = formType === "early-access" ? "Become an Early Access Partner" : "Request a Demo"
const formDescription =
formType === "early-access"
? "Fill out the form below to collaborate in shaping Roo Code's enterprise solution."
: "Fill out the form below to see Roo Code's enterprise capabilities in action."
// Get Basin endpoint from environment variable
// This should be set in .env.local as NEXT_PUBLIC_BASIN_ENDPOINT="https://usebasin.com/f/your-form-id"
const BASIN_ENDPOINT = process.env.NEXT_PUBLIC_BASIN_ENDPOINT || ""
// Check if Basin endpoint is configured
if (!BASIN_ENDPOINT) {
console.warn("NEXT_PUBLIC_BASIN_ENDPOINT is not configured. Form submissions will not work.")
}
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
setIsSubmitting(true)
setFormErrors({})
setSubmitStatus("idle")
const form = e.currentTarget
const formData = new FormData(form)
// Create a data object for validation and submission
const data = {
name: formData.get("name") as string,
company: formData.get("company") as string,
email: formData.get("email") as string,
website: formData.get("website") as string,
engineerCount: formData.get("engineerCount") as string,
formType,
// Include honeypot field for spam protection
_honeypot: formData.get("_honeypot") as string,
}
// Validate form data on client side
try {
contactFormSchema.parse(data)
// Submit data to Basin
try {
const response = await fetch(BASIN_ENDPOINT, {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
mode: "cors", // Ensure proper CORS handling
body: JSON.stringify(data),
})
// Basin returns a 200 status code on success
if (response.ok) {
try {
const responseData = await response.json()
// Basin JSON API typically returns a 'status' property of 'success' when submission succeeds
if (responseData && (responseData.success === true || responseData.status === "success")) {
setSubmitStatus("success")
// Reset form safely
if (form) {
form.reset()
}
} else {
console.error("Basin error:", responseData)
setSubmitStatus("error")
}
} catch (jsonError) {
// In case response parsing fails but status was OK, assume success
console.error("Error parsing JSON response:", jsonError)
setSubmitStatus("success")
if (form) {
form.reset()
}
}
} else {
// Handle error response from Basin (4xx or 5xx)
try {
const errorData = await response.json()
console.error("Basin API error:", response.status, errorData)
} catch {
console.error("Basin returned error status:", response.status)
}
setSubmitStatus("error")
}
} catch (error) {
console.error("Error submitting form:", error)
setSubmitStatus("error")
}
} catch (error) {
if (error instanceof z.ZodError) {
const errors: Record<string, string> = {}
error.errors.forEach((err) => {
if (err.path[0]) {
errors[err.path[0] as string] = err.message
}
})
setFormErrors(errors)
} else {
setSubmitStatus("error")
}
} finally {
setIsSubmitting(false)
}
}
return (
<Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogTrigger asChild>
<Button className={buttonClassName}>{buttonText}</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>{formTitle}</DialogTitle>
<DialogDescription>{formDescription}</DialogDescription>
</DialogHeader>
{submitStatus === "success" ? (
<div className="flex flex-col items-center justify-center py-6">
<div className="mb-4 rounded-full bg-green-100 p-3 text-green-600 dark:bg-green-900/20 dark:text-green-400">
<svg
xmlns="http://www.w3.org/2000/svg"
className="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
</svg>
</div>
<h3 className="mb-2 text-xl font-bold">Thank You!</h3>
<p className="text-center text-muted-foreground">
Your information has been submitted successfully. Our team will be in touch with you
shortly.
</p>
<Button className="mt-4" onClick={() => setIsOpen(false)}>
Close
</Button>
</div>
) : (
<form ref={formRef} onSubmit={handleSubmit} className="space-y-4" data-basin-form>
{/* Basin honeypot field for spam protection - should remain empty and hidden */}
<input type="text" name="_honeypot" className="hidden" style={{ display: "none" }} />
<div className="space-y-2">
<label htmlFor="name" className="text-sm font-medium">
Name <span className="text-red-500">*</span>
</label>
<input
id="name"
name="name"
type="text"
className={`w-full rounded-md border ${
formErrors.name ? "border-red-500" : "border-input"
} bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring`}
placeholder="Your name"
/>
{formErrors.name && <p className="text-xs text-red-500">{formErrors.name}</p>}
</div>
<div className="space-y-2">
<label htmlFor="company" className="text-sm font-medium">
Company <span className="text-red-500">*</span>
</label>
<input
id="company"
name="company"
type="text"
className={`w-full rounded-md border ${
formErrors.company ? "border-red-500" : "border-input"
} bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring`}
placeholder="Your company"
/>
{formErrors.company && <p className="text-xs text-red-500">{formErrors.company}</p>}
</div>
<div className="space-y-2">
<label htmlFor="email" className="text-sm font-medium">
Email <span className="text-red-500">*</span>
</label>
<input
id="email"
name="email"
type="email"
className={`w-full rounded-md border ${
formErrors.email ? "border-red-500" : "border-input"
} bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring`}
placeholder="your.email@example.com"
/>
{formErrors.email && <p className="text-xs text-red-500">{formErrors.email}</p>}
</div>
<div className="space-y-2">
<label htmlFor="website" className="text-sm font-medium">
Website
</label>
<input
id="website"
name="website"
type="url"
className={`w-full rounded-md border ${
formErrors.website ? "border-red-500" : "border-input"
} bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring`}
placeholder="https://example.com"
/>
{formErrors.website && <p className="text-xs text-red-500">{formErrors.website}</p>}
</div>
<div className="space-y-2">
<label htmlFor="engineerCount" className="text-sm font-medium">
Number of Software Engineers <span className="text-red-500">*</span>
</label>
<select
id="engineerCount"
name="engineerCount"
className={`w-full rounded-md border ${
formErrors.engineerCount ? "border-red-500" : "border-input"
} bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring`}
defaultValue="1-10">
<option value="1-10">1-10</option>
<option value="11-50">11-50</option>
<option value="51-200">51-200</option>
<option value="201-500">201-500</option>
<option value="501-1000">501-1000</option>
<option value="1000+">1000+</option>
</select>
{formErrors.engineerCount && (
<p className="text-xs text-red-500">{formErrors.engineerCount}</p>
)}
</div>
{submitStatus === "error" && (
<div className="rounded-md bg-red-50 p-3 text-sm text-red-500 dark:bg-red-900/20">
There was an error submitting your request. Please try again later.
</div>
)}
<DialogFooter>
<Button type="button" variant="outline" onClick={() => setIsOpen(false)}>
Cancel
</Button>
<Button type="submit" disabled={isSubmitting}>
{isSubmitting ? "Submitting..." : "Submit"}
</Button>
</DialogFooter>
</form>
)}
</DialogContent>
</Dialog>
)
}

View file

@ -1,280 +0,0 @@
"use client"
import { useEffect, useRef } from "react"
export function AnimatedBackground() {
const canvasRef = useRef<HTMLCanvasElement>(null)
useEffect(() => {
const canvas = canvasRef.current
if (!canvas) return
const ctx = canvas.getContext("2d")
if (!ctx) return
// grid settings
const gridSize = 50
const gridOpacity = 0.15
// initialize gradient points for lighting effects
let gradientPoints = [
{
x: canvas.width * 0.2,
y: canvas.height * 0.3,
radius: canvas.width * 0.4,
color: "rgba(0, 100, 255, 0.15)",
},
{
x: canvas.width * 0.8,
y: canvas.height * 0.7,
radius: canvas.width * 0.5,
color: "rgba(100, 0, 255, 0.1)",
},
]
// particle system
const particles: Particle[] = []
const particleCount = Math.min(50, Math.floor(window.innerWidth / 40))
// set canvas dimensions
const resizeCanvas = () => {
canvas.width = window.innerWidth
canvas.height = window.innerHeight
// update gradient points when canvas is resized
gradientPoints = [
{
x: canvas.width * 0.2,
y: canvas.height * 0.3,
radius: canvas.width * 0.4,
color: "rgba(0, 100, 255, 0.15)",
},
{
x: canvas.width * 0.8,
y: canvas.height * 0.7,
radius: canvas.width * 0.5,
color: "rgba(100, 0, 255, 0.1)",
},
]
// redraw grid after resize
drawGrid()
}
resizeCanvas()
window.addEventListener("resize", resizeCanvas)
// draw grid with perspective effect
function drawGrid() {
if (!ctx) {
throw new Error("Context is null (not initialized?)")
}
if (!canvas) {
throw new Error("Canvas is null (not initialized?)")
}
ctx.clearRect(0, 0, canvas.width, canvas.height)
// Draw gradient lighting effects.
gradientPoints.forEach((point) => {
const gradient = ctx.createRadialGradient(point.x, point.y, 0, point.x, point.y, point.radius)
gradient.addColorStop(0, point.color)
gradient.addColorStop(1, "rgba(0, 0, 0, 0)")
ctx.fillStyle = gradient
ctx.fillRect(0, 0, canvas.width, canvas.height)
})
// Draw grid lines with perspective effect.
ctx.strokeStyle = `rgba(50, 50, 70, ${gridOpacity})`
ctx.lineWidth = 0.5
// horizontal lines with perspective.
const horizonY = canvas.height * 0.7 // Horizon point.
const vanishingPointX = canvas.width * 0.5 // Center vanishing point.
// Vertical lines.
for (let x = 0; x <= canvas.width; x += gridSize) {
const normalizedX = x / canvas.width - 0.5 // -0.5 to 0.5
ctx.beginPath()
ctx.moveTo(x, 0)
// Calculate curve based on distance from center.
const curveStrength = 50 * Math.abs(normalizedX)
const controlPointY = horizonY - curveStrength
// Create curved line toward vanishing point.
ctx.quadraticCurveTo(
x + (vanishingPointX - x) * 0.3,
controlPointY,
vanishingPointX + (x - vanishingPointX) * 0.2,
horizonY,
)
ctx.stroke()
}
// Horizontal lines.
for (let y = 0; y <= horizonY; y += gridSize) {
const normalizedY = y / horizonY // 0 to 1
const lineWidth = gridSize * (1 + normalizedY * 5) // lines get wider as they get closer
ctx.beginPath()
ctx.moveTo(vanishingPointX - lineWidth, y)
ctx.lineTo(vanishingPointX + lineWidth, y)
ctx.stroke()
}
updateParticles()
}
class Particle {
x: number
y: number
size: number
speedX: number
speedY: number
color: string
opacity: number
constructor() {
if (!canvas) {
throw new Error("Canvas is null (not initialized?)")
}
this.x = Math.random() * canvas.width
this.y = Math.random() * (canvas.height * 0.7) // Keep particles above horizon.
this.size = Math.random() * 2 + 1
this.speedX = (Math.random() - 0.5) * 0.8
this.speedY = (Math.random() - 0.5) * 0.8
this.color = "rgba(100, 150, 255, "
this.opacity = Math.random() * 0.5 + 0.2
}
update() {
if (!canvas) {
throw new Error("Canvas is null (not initialized?)")
}
this.x += this.speedX
this.y += this.speedY
// Boundary check.
if (this.x > canvas.width) this.x = 0
else if (this.x < 0) this.x = canvas.width
if (this.y > canvas.height * 0.7) this.y = 0
else if (this.y < 0) this.y = canvas.height * 0.7
// Pulsate opacity.
this.opacity += Math.sin(Date.now() * 0.001) * 0.01
this.opacity = Math.max(0.1, Math.min(0.7, this.opacity))
}
draw() {
if (!ctx) {
throw new Error("Context is null (not initialized?)")
}
ctx.fillStyle = `${this.color}${this.opacity})`
ctx.beginPath()
ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2)
ctx.fill()
}
}
// Initialize particles.
for (let i = 0; i < particleCount; i++) {
particles.push(new Particle())
}
// Connect particles with lines.
function connectParticles() {
if (!ctx) {
throw new Error("Context is null (not initialized?)")
}
const maxDistance = 150
for (let a = 0; a < particles.length; a++) {
for (let b = a; b < particles.length; b++) {
const dx = particles[a]!.x - particles[b]!.x
const dy = particles[a]!.y - particles[b]!.y
const distance = Math.sqrt(dx * dx + dy * dy)
if (distance < maxDistance) {
const opacity = (1 - distance / maxDistance) * 0.5
ctx.strokeStyle = `rgba(100, 150, 255, ${opacity})`
ctx.lineWidth = 0.5
ctx.beginPath()
ctx.moveTo(particles[a]!.x, particles[a]!.y)
ctx.lineTo(particles[b]!.x, particles[b]!.y)
ctx.stroke()
}
}
}
}
function updateParticles() {
particles.forEach((particle) => {
particle.update()
particle.draw()
})
connectParticles()
}
// Animation loop.
let animationId: number
// Target position for smooth following.
let targetX = canvas.width * 0.2
let targetY = canvas.height * 0.3
const moveSpeed = 0.05 // Adjust this value to control movement speed (0-1).
// Move gradient points with mouse.
const handleMouseMove = (e: MouseEvent) => {
targetX = e.clientX
targetY = e.clientY
}
// Update gradient point position in animation loop.
function updateGradientPosition() {
if (!canvas) throw new Error("Canvas is null (not initialized?)")
// Calculate direction vector.
const dx = targetX - gradientPoints[0]!.x
const dy = targetY - gradientPoints[0]!.y
// Smooth movement using linear interpolation.
gradientPoints[0]!.x += dx * moveSpeed
gradientPoints[0]!.y += dy * moveSpeed
// Adjust radius based on distance to target.
const distanceToTarget = Math.sqrt(dx * dx + dy * dy)
gradientPoints[0]!.radius = Math.max(
canvas.width * 0.2,
Math.min(canvas.width * 0.4, canvas.width * 0.3 + distanceToTarget * 0.1),
)
}
function animate() {
animationId = requestAnimationFrame(animate)
updateGradientPosition()
drawGrid()
}
animate()
window.addEventListener("mousemove", handleMouseMove)
return () => {
window.removeEventListener("resize", resizeCanvas)
window.removeEventListener("mousemove", handleMouseMove)
cancelAnimationFrame(animationId)
}
}, [])
return <canvas ref={canvasRef} className="absolute inset-0 h-full w-full" style={{ zIndex: 0 }} />
}

View file

@ -1,236 +0,0 @@
"use client"
import { useState, useEffect, useRef } from "react"
import { motion } from "framer-motion"
export function CodeExample() {
const [currentMode, setCurrentMode] = useState<"code" | "architect" | "debug">("code")
const [isTyping, setIsTyping] = useState(false)
const [currentText, setCurrentText] = useState("")
const [textIndex, setTextIndex] = useState(0)
const codeContainerRef = useRef<HTMLPreElement>(null)
// simulate typing effect
useEffect(() => {
if (isTyping && textIndex < codeExamples[currentMode].code.length) {
const timer = setTimeout(() => {
setCurrentText((prev) => prev + codeExamples[currentMode].code[textIndex])
setTextIndex(textIndex + 1)
// Auto-scroll to the bottom
if (codeContainerRef.current) {
codeContainerRef.current.scrollTop = codeContainerRef.current.scrollHeight
}
}, 15) // adjust speed as needed
return () => clearTimeout(timer)
} else if (textIndex >= codeExamples[currentMode].code.length) {
setIsTyping(false)
// switch to next mode after a delay
const timer = setTimeout(() => {
const nextMode = currentMode === "code" ? "architect" : currentMode === "architect" ? "debug" : "code"
switchMode(nextMode)
}, 1000) // wait a second before switching
return () => clearTimeout(timer)
}
}, [isTyping, textIndex, currentMode])
// switch modes with typing effect
const switchMode = (mode: "code" | "architect" | "debug") => {
setCurrentMode(mode)
setCurrentText("")
setTextIndex(0)
setIsTyping(true)
// Reset scroll position when switching modes
if (codeContainerRef.current) {
codeContainerRef.current.scrollTop = 0
}
}
// start typing on initial load
useEffect(() => {
setIsTyping(true)
}, [])
return (
<div className="relative z-10 w-full max-w-[90vw] rounded-lg border border-border bg-background/50 p-2 shadow-2xl backdrop-blur-sm sm:max-w-[500px]">
<div className="rounded-md bg-muted p-1.5 dark:bg-gray-900 sm:p-2">
<div className="flex items-center justify-between border-b border-border px-2 py-1.5 sm:px-3 sm:py-2">
<div className="flex items-center space-x-1.5">
<div className="h-2.5 w-2.5 rounded-full bg-red-500 sm:h-3 sm:w-3" />
<div className="h-2.5 w-2.5 rounded-full bg-yellow-500 sm:h-3 sm:w-3" />
<div className="h-2.5 w-2.5 rounded-full bg-green-500 sm:h-3 sm:w-3" />
</div>
<div className="flex space-x-1">
<button
onClick={() => switchMode("code")}
className={`rounded px-2 py-0.5 text-xs font-medium transition-colors sm:text-sm ${currentMode === "code" ? "bg-blue-500/20 text-blue-400" : "text-gray-400 hover:bg-gray-800"}`}>
Code
</button>
<button
onClick={() => switchMode("architect")}
className={`rounded px-2 py-0.5 text-xs font-medium transition-colors sm:text-sm ${currentMode === "architect" ? "bg-purple-500/20 text-purple-400" : "text-gray-400 hover:bg-gray-800"}`}>
Architect
</button>
<button
onClick={() => switchMode("debug")}
className={`rounded px-2 py-0.5 text-xs font-medium transition-colors sm:text-sm ${currentMode === "debug" ? "bg-green-500/20 text-green-400" : "text-gray-400 hover:bg-gray-800"}`}>
Debug
</button>
</div>
</div>
<div className="p-2 sm:p-4">
<pre
ref={codeContainerRef}
className="scrollbar-thin scrollbar-thumb-gray-700 scrollbar-track-transparent overflow-y-hidden text-xs text-muted-foreground max-lg:h-[25vh] sm:text-sm lg:max-h-[50vh]">
<code className="block whitespace-pre font-mono">
{currentText}
{isTyping && (
<motion.span
animate={{ opacity: [1, 0] }}
transition={{ repeat: Number.POSITIVE_INFINITY, duration: 0.8 }}
className="inline-block h-4 w-2 bg-blue-500"
/>
)}
</code>
</pre>
</div>
</div>
</div>
)
}
const codeExamples = {
code: {
code: `// Code Mode
You: Generate a React component for a user profile card
Roo: Here's a React component for a user profile card:
import React from 'react';
interface UserProfileProps {
name: string;
role: string;
avatar: string;
bio: string;
stats: {
projects: number;
followers: number;
following: number;
};
}
export const UserProfile: React.FC<UserProfileProps> = ({
name,
role,
avatar,
bio,
stats
}) => {
return (
<div className="bg-white rounded-lg shadow-md p-6 max-w-sm">
<div className="flex items-center space-x-4">
<img
src={avatar || "/placeholder.svg"}
alt={name}
className="w-16 h-16 rounded-full object-cover"
/>
<div>
<h3 className="text-xl font-bold">{name}</h3>
<p className="text-gray-600">{role}</p>
</div>
</div>
<p className="mt-4 text-gray-700">{bio}</p>
<div className="mt-6 flex justify-between">
<div className="text-center">
<span className="block font-bold">{stats.projects}</span>
<span className="text-sm text-gray-500">Projects</span>
</div>
<div className="text-center">
<span className="block font-bold">{stats.followers}</span>
<span className="text-sm text-gray-500">Followers</span>
</div>
<div className="text-center">
<span className="block font-bold">{stats.following}</span>
<span className="text-sm text-gray-500">Following</span>
</div>
</div>
</div>
);
};`,
},
architect: {
code: `// Architect Mode
// Project: E-commerce Platform
// Request: Design the authentication system
Roo is analyzing your project...
📂 Recommended Architecture:
auth/
services/
authService.ts // Core authentication logic
tokenService.ts // JWT handling and refresh
userService.ts // User data operations
hooks/
useAuth.ts // React hook for auth state
useProtectedRoute.ts // Route protection
components/
LoginForm.tsx // Login UI
SignupForm.tsx // Registration UI
PasswordReset.tsx // Password recovery
TwoFactorAuth.tsx // 2FA implementation
context/
AuthContext.tsx // Global auth state
🔐 Security Recommendations:
- Implement PKCE flow for auth code exchange
- Use HttpOnly cookies for refresh tokens
- Rate limit authentication attempts
- Add device fingerprinting for suspicious login detection
Performance Considerations:
- Prefetch user data on auth
- Implement token refresh without UI disruption
- Lazy load auth components
Would you like me to generate any of these files?`,
},
debug: {
code: `// Debug Mode
// Analyzing error: TypeError: Cannot read property 'map' of undefined
Roo has analyzed your code and found 3 issues:
🐛 Issue #1: Null data reference
Line 42: const items = data.items.map(item => item.name);
Root Cause: 'data' is undefined when component mounts
Context: API request in useEffect hasn't completed yet
Recommended Fix:
const items = data?.items?.map(item => item.name) || [];
🐛 Issue #2: Missing dependency in useEffect
Line 37: useEffect(() => { fetchData() }, []);
Root Cause: fetchData depends on 'userId' but isn't in deps array
Context: This causes stale data when userId changes
Recommended Fix:
useEffect(() => { fetchData() }, [userId, fetchData]);
🐛 Issue #3: Memory leak from unfinished API call
Line 38: const response = await api.getItems(userId);
Root Cause: No cleanup when component unmounts during API call
Context: This triggers React warning in development
Recommended Fix:
Add AbortController to cancel pending requests on unmount
Apply these fixes automatically? [Yes/No]`,
},
}

View file

@ -1,40 +0,0 @@
"use client"
import { motion } from "framer-motion"
export function CompanyLogos() {
const logos = [
{ name: "Company 1", logo: "/placeholder.svg?height=40&width=120" },
{ name: "Company 2", logo: "/placeholder.svg?height=40&width=120" },
{ name: "Company 3", logo: "/placeholder.svg?height=40&width=120" },
{ name: "Company 4", logo: "/placeholder.svg?height=40&width=120" },
{ name: "Company 5", logo: "/placeholder.svg?height=40&width=120" },
{ name: "Company 6", logo: "/placeholder.svg?height=40&width=120" },
]
return (
<div className="mt-10">
<div className="mx-auto grid max-w-5xl grid-cols-2 gap-8 py-8 md:grid-cols-3 lg:grid-cols-6">
{logos.map((company, index) => (
<motion.div
key={index}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{
duration: 0.5,
delay: index * 0.1,
ease: "easeOut",
}}
className="flex items-center justify-center">
{/* eslint-disable @next/next/no-img-element */}
<img
src={company.logo || "/placeholder.svg"}
alt={company.name}
className="h-10 w-auto opacity-70 grayscale transition-all duration-300 hover:opacity-100 hover:grayscale-0"
/>
</motion.div>
))}
</div>
</div>
)
}

View file

@ -1,140 +0,0 @@
"use client"
import { useState } from "react"
import { motion } from "framer-motion"
import { ChevronDown } from "lucide-react"
import { cn } from "@/lib/utils"
interface FAQItem {
question: string
answer: string
}
const faqs: FAQItem[] = [
{
question: "What exactly is Roo Code?",
answer: "Roo Code is an open-source, AI-powered coding assistant that runs in VS Code. It goes beyond simple autocompletion by reading and writing across multiple files, executing commands, and adapting to your workflow—like having a whole dev team right inside your editor.",
},
{
question: "How does Roo Code differ from Copilot, Cursor, or Windsurf?",
answer: "Open & Customizable: Roo Code is open-source and allows you to integrate any AI model (OpenAI, Anthropic, local LLMs, etc.). Multi-File Edits: It can read, refactor, and update multiple files at once for more holistic changes. Agentic Abilities: Roo Code can run tests, open a browser, or do deeper tasks than a typical AI autocomplete. Permission-Based: You control and approve any file changes or command executions.",
},
{
question: "Is Roo Code really free?",
answer: "Yes! Roo Code is completely free and open-source. You'll only pay for the AI model usage if you use a paid API (like OpenAI). If you choose free or self-hosted models, there's no cost at all.",
},
{
question: "Will my code stay private?",
answer: "Yes. Because Roo Code is an extension in your local VS Code, your code never leaves your machine unless you connect to an external AI API. Even then, you control exactly what is sent to the AI model. You can use tools like .rooignore to exclude sensitive files, and you can also run Roo Code with offline/local models for full privacy.",
},
{
question: "Which AI models does Roo Code support?",
answer: "Roo Code is model-agnostic. It works with: OpenAI models (GPT-3.5, GPT-4, etc.), Anthropic Claude, Local LLMs (through APIs or special plugins), Any other API that follows Roo Code's Model Context Protocol (MCP).",
},
{
question: "Does Roo Code support my programming language?",
answer: "Likely yes! Roo Code supports a wide range of languages—Python, Java, C#, JavaScript/TypeScript, Go, Rust, etc. Since it leverages the AI model's understanding, new or lesser-known languages may also work, depending on model support.",
},
{
question: "How do I install and get started?",
answer: "Install Roo Code from the VS Code Marketplace (or GitHub). Add your AI keys (OpenAI, Anthropic, or other) in the extension settings. Open the Roo panel (the rocket icon) in VS Code, and start typing commands in plain English!",
},
{
question: "Can it handle large, enterprise-scale projects?",
answer: "Absolutely. Roo Code uses efficient strategies (like partial-file analysis, summarization, or user-specified context) to handle large codebases. Enterprises especially appreciate the on-prem or self-hosted model option for compliance and security needs.",
},
{
question: "Is it safe for enterprise use?",
answer: "Yes. Roo Code was designed with enterprise in mind: Self-host AI models or choose your own provider. Permission gating on file writes and commands. Auditable: The entire code is open-source, so you know exactly how it operates.",
},
{
question: "Can Roo Code run commands and tests automatically?",
answer: "Yes! One of Roo Code's superpowers is command execution (optional and fully permission-based). It can: Run npm install or any terminal command you grant permission for. Execute your test suites. Open a web browser for integration tests.",
},
{
question: "What if I just want a casual coding 'vibe'?",
answer: 'Roo Code shines for both serious enterprise development and casual "vibe coding." You can ask it to quickly prototype ideas, refactor on the fly, or provide design suggestions—without a rigid, step-by-step process.',
},
{
question: "Can I contribute to Roo Code?",
answer: "Yes, please do! Roo Code is open-source on GitHub. Submit issues, suggest features, or open a pull request. There's also an active community on Discord and Reddit if you want to share feedback or help others.",
},
{
question: "Where can I learn more or get help?",
answer: "Check out: Official Documentation for setup and advanced guides. Discord & Reddit channels for community support. YouTube tutorials and blog posts from fellow developers showcasing real-world usage.",
},
]
export function FAQSection() {
const [openIndex, setOpenIndex] = useState<number | null>(null)
const toggleFAQ = (index: number) => {
setOpenIndex(openIndex === index ? null : index)
}
return (
<section id="faq-section" className="border-t border-border py-20">
<div className="container 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="bg-gradient-to-b from-foreground to-foreground/70 bg-clip-text text-4xl font-bold tracking-tight text-transparent sm:text-5xl">
Frequently Asked Questions
</h2>
<p className="mt-6 text-lg text-muted-foreground">
Everything you need to know about Roo Code and how it can transform your development
workflow.
</p>
</motion.div>
</div>
<div className="mx-auto max-w-3xl">
<div className="space-y-4">
{faqs.map((faq, index) => (
<motion.div
key={index}
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{
duration: 0.5,
delay: index * 0.1,
ease: [0.21, 0.45, 0.27, 0.9],
}}>
<div className="group relative rounded-lg border border-border/50 bg-background/30 backdrop-blur-xl transition-all duration-300 hover:border-border">
<button
onClick={() => toggleFAQ(index)}
className="flex w-full items-center justify-between p-6 text-left"
aria-expanded={openIndex === index}>
<h3 className="text-lg font-medium text-foreground/90">{faq.question}</h3>
<ChevronDown
className={cn(
"h-5 w-5 text-muted-foreground transition-transform duration-200",
openIndex === index ? "rotate-180" : "",
)}
/>
</button>
<div
className={cn(
"overflow-hidden transition-all duration-300 ease-in-out",
openIndex === index ? "max-h-96 pb-6" : "max-h-0",
)}>
<div className="px-6 text-muted-foreground">
<p>{faq.answer}</p>
</div>
</div>
</div>
</motion.div>
))}
</div>
</div>
</div>
</section>
)
}

View file

@ -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-colors duration-300 hover:border-border hover:bg-gray-900/20">
<div className="mb-2 inline-flex items-center justify-center rounded-xl bg-gradient-to-r from-blue-500/5 to-cyan-500/5 p-2.5">
<div className="rounded-lg bg-gradient-to-r from-blue-500/80 to-cyan-500/80 p-2.5">
<div className="text-foreground/90">{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>
)
}

View file

@ -1,172 +0,0 @@
"use client"
import { motion } from "framer-motion"
import { FaRobot, FaCode, FaBrain, FaTools, FaTerminal, FaPuzzlePiece, FaGlobe } from "react-icons/fa"
import { FeaturesMobile } from "./features-mobile"
import { ReactNode } from "react"
export interface Feature {
icon: ReactNode
title: string
description: string
size: "small" | "large"
}
export const features: Feature[] = [
{
icon: <FaRobot className="h-6 w-6" />,
title: "Your AI Dev Team in VS Code",
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.",
size: "large",
},
{
icon: <FaCode className="h-6 w-6" />,
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.",
size: "small",
},
{
icon: <FaBrain className="h-6 w-6" />,
title: "Deep Project-wide Context",
description:
"Roo Code reads your entire codebase, preserving valid code through diff-based edits for seamless multi-file refactors.",
size: "small",
},
{
icon: <FaTools className="h-6 w-6" />,
title: "Open-Source and Model-Agnostic",
description:
"Bring your own model or use local AI—no vendor lock-in. Roo Code is free, open, and adaptable to your needs.",
size: "large",
},
{
icon: <FaTerminal className="h-6 w-6" />,
title: "Guarded Command Execution",
description:
"Approve or deny commands as needed. Roo Code automates your dev workflow while keeping oversight firmly in your hands.",
size: "small",
},
{
icon: <FaPuzzlePiece className="h-6 w-6" />,
title: "Fully Customizable",
description:
"Create or tweak modes, define usage rules, and shape Roo Codes behavior precisely—your code, your way.",
size: "small",
},
{
icon: <FaGlobe className="h-6 w-6" />,
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.",
size: "small",
},
]
export function Features() {
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],
},
},
}
const backgroundVariants = {
hidden: {
opacity: 0,
},
visible: {
opacity: 1,
transition: {
duration: 1.2,
ease: "easeOut",
},
},
}
return (
<section 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="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="bg-gradient-to-b from-foreground to-foreground/70 bg-clip-text text-4xl font-bold tracking-tight text-transparent sm:text-5xl">
Powerful features for modern developers.
</h2>
<p className="mt-6 text-lg text-muted-foreground">
Everything you need to build faster and write better code.
</p>
</motion.div>
</div>
{/* Mobile Carousel */}
<FeaturesMobile />
{/* Desktop Grid */}
<motion.div
className="relative mx-auto hidden max-w-[1200px] md:block"
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 ${feature.size === "large" ? "lg:col-span-2" : ""} ${index % 2 === 0 ? "lg: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-opacity duration-500 group-hover:opacity-100" />
<div className="relative h-full rounded-2xl border border-border/50 bg-background/30 p-8 backdrop-blur-xl transition-colors duration-300 hover:border-border">
<div className="mb-5 inline-flex items-center justify-center rounded-xl bg-gradient-to-r from-blue-500/5 to-cyan-500/5 p-2.5">
<div className="rounded-lg bg-gradient-to-r from-blue-500/80 to-cyan-500/80 p-2.5">
<div className="text-foreground/90">{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>
</motion.div>
))}
</div>
</motion.div>
</div>
</section>
)
}

View file

@ -1,10 +0,0 @@
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"

View file

@ -1,87 +0,0 @@
"use client"
import { VscVscode } from "react-icons/vsc"
import Link from "next/link"
import { motion } from "framer-motion"
interface InstallSectionProps {
downloads: string | null
}
export function InstallSection({ downloads }: InstallSectionProps) {
const backgroundVariants = {
hidden: {
opacity: 0,
},
visible: {
opacity: 1,
transition: {
duration: 1.2,
ease: "easeOut",
},
},
}
return (
<section className="relative overflow-hidden border-t border-border py-16 sm:py-24 lg:py-32">
<motion.div
className="absolute inset-x-0 top-1/2 -translate-y-1/2"
initial="hidden"
whileInView="visible"
viewport={{ once: true }}
variants={backgroundVariants}>
<div className="relative mx-auto max-w-[1200px]">
<div className="absolute left-1/2 top-1/2 h-[500px] w-[700px] -translate-x-1/2 -translate-y-1/2 rounded-[100%] bg-blue-500/10 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 max-w-3xl text-center">
<h2 className="text-center text-xl font-semibold uppercase tracking-wider text-muted-foreground sm:text-2xl">
Install Roo Code Open & Flexible
</h2>
<p className="mt-4 text-center text-base text-muted-foreground sm:mt-6 sm:text-lg">
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.
</p>
<div className="mt-10 flex flex-col items-center justify-center gap-6">
<Link
href="https://marketplace.visualstudio.com/items?itemName=RooVeterinaryInc.roo-cline"
target="_blank"
className="group relative inline-flex w-full items-center justify-center gap-2 rounded-xl border border-border/50 bg-background/30 px-4 py-3 text-base backdrop-blur-xl transition-all duration-300 hover:border-border hover:bg-background/40 sm:w-auto sm:gap-3 sm:px-6 sm:py-4 sm:text-lg md:text-2xl">
<div className="absolute -inset-px rounded-xl bg-gradient-to-r from-blue-500/30 via-cyan-500/30 to-purple-500/30 opacity-0 blur-sm transition-opacity duration-500 group-hover:opacity-100" />
<div className="relative flex items-center gap-2 sm:gap-3">
<VscVscode className="h-5 w-5 text-blue-400 sm:h-6 sm:w-6 md:h-8 md:w-8" />
<span className="flex flex-wrap items-center gap-1 sm:gap-2 md:gap-3">
<span className="text-foreground/90">VSCode Marketplace</span>
{downloads !== null && (
<>
<span className="hidden font-black text-muted-foreground sm:inline">
&middot;
</span>
<span className="text-muted-foreground">{downloads} Downloads</span>
</>
)}
</span>
</div>
</Link>
<div className="group relative w-full max-w-xl">
<div className="absolute -inset-px rounded-xl bg-gradient-to-r from-blue-500/30 via-cyan-500/30 to-purple-500/30 opacity-0 blur-sm transition-opacity duration-500 group-hover:opacity-100" />
<div className="relative overflow-hidden rounded-xl border border-border/50 bg-background/30 backdrop-blur-xl transition-all duration-500 ease-out group-hover:border-border group-hover:bg-background/40">
<div className="border-b border-border/50 px-3 py-2 sm:px-4">
<div className="text-sm text-muted-foreground">Install via CLI</div>
</div>
<div className="overflow-x-auto">
<pre className="p-3 sm:p-4">
<code className="whitespace-pre-wrap break-all text-sm text-foreground/90 sm:break-normal">
code --install-extension RooVeterinaryInc.roo-cline
</code>
</pre>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
)
}

View file

@ -1,49 +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 py-8">
<svg
className="absolute left-0 top-0 h-8 w-8 text-blue-500/30"
fill="currentColor"
viewBox="0 0 32 32">
<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" />
</svg>
<blockquote className="mt-8">
<p className="text-lg font-light italic leading-relaxed text-muted-foreground">
&quot;{testimonial.quote}&quot;
</p>
<footer className="mt-6">
<div className="h-px w-12 bg-gradient-to-r from-blue-500/30 to-transparent" />
<p className="mt-4 font-medium text-foreground/90">{testimonial.name}</p>
<p className="text-sm text-muted-foreground">
{testimonial.role} at {testimonial.company}
</p>
</footer>
</blockquote>
</div>
</div>
))}
</div>
</div>
</div>
)
}

View file

@ -1,183 +0,0 @@
"use client"
import { useRef } from "react"
import { motion } from "framer-motion"
import Image from "next/image"
import { TestimonialsMobile } from "./testimonials-mobile"
export interface Testimonial {
id: number
name: string
role: string
company: string
image?: string
quote: string
}
export const testimonials: Testimonial[] = [
{
id: 1,
name: "Luca",
role: "Reviewer",
company: "VS Code Marketplace",
quote: "Roo Code is an absolute game-changer! 🚀 It makes coding faster, easier, and more intuitive with its smart AI-powered suggestions, real-time debugging, and automation features. The seamless integration with VS Code is a huge plus, and the constant updates ensure it keeps getting better",
},
{
id: 2,
name: "Taro Woollett-Chiba",
role: "AI Product Lead",
company: "Vendidit",
quote: "Easily the best AI code editor. Roo Code has the best features and capabilities, along with the best development team. I swear, they're the fastest to support new models and implement useful functionality whenever users mention it... simply amazing.",
},
{
id: 3,
name: "Can Nuri",
role: "Reviewer",
company: "VS Code Marketplace",
quote: "Roo Code is one of the most inspiring projects I have seen for a long time. It shapes the way I think and deal with software development.",
},
{
id: 4,
name: "Michael",
role: "Reviewer",
company: "VS Code Marketplace",
quote: "I switched from Windsurf to Roo Code in January and honestly, it's been a huge upgrade. Windsurf kept making mistakes and being dumb when I ask it for things. Roo just gets it. Projects that used to take a full day now wrap up before lunch. ",
},
]
export function Testimonials() {
const containerRef = useRef<HTMLDivElement>(null)
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],
},
},
}
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="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="bg-gradient-to-b from-foreground to-foreground/70 bg-clip-text text-4xl font-bold tracking-tight text-transparent 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>
{/* Mobile Carousel */}
<TestimonialsMobile />
{/* Desktop Grid */}
<motion.div
className="relative mx-auto hidden max-w-[1200px] md:block"
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" />
<div className="relative h-full rounded-2xl border border-border/50 bg-background/30 backdrop-blur-xl transition-all duration-500 ease-out group-hover:border-border group-hover:bg-background/40">
{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-105">
<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"
/>
</div>
</div>
)}
<div className="p-8">
<div className="mb-6">
<svg
className="h-8 w-8 text-blue-500/20"
fill="currentColor"
viewBox="0 0 32 32">
<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" />
</svg>
</div>
<p className="relative mb-6 text-lg leading-relaxed text-muted-foreground">
{testimonial.quote}
</p>
<div className="relative">
<div className="mb-4 h-px w-12 bg-gradient-to-r from-blue-500/50 to-transparent" />
<h3 className="font-medium text-foreground/90">{testimonial.name}</h3>
<p className="text-sm text-muted-foreground">
{testimonial.role} at {testimonial.company}
</p>
</div>
</div>
</div>
</motion.div>
))}
</div>
</motion.div>
</div>
</section>
)
}

View file

@ -1,229 +0,0 @@
"use client"
import { useState, useRef, useEffect } from "react"
import { motion, AnimatePresence } from "framer-motion"
import { X, ArrowRight, Code2, Users2, Zap } from "lucide-react"
import Link from "next/link"
interface FeatureProps {
icon: React.ComponentType<{ className?: string }>
color: "blue" | "purple" | "green"
title: string
description: string
}
function Feature({ icon: Icon, color, title, description }: FeatureProps) {
const bgColor = {
blue: "bg-blue-500/20",
purple: "bg-purple-500/20",
green: "bg-green-500/20",
}[color]
const textColor = {
blue: "text-blue-400",
purple: "text-purple-400",
green: "text-green-400",
}[color]
return (
<div className="space-y-1.5 sm:space-y-2">
<div className="flex items-center gap-1 space-x-2">
<div className={`rounded-full ${bgColor} p-3 ${textColor}`}>
<Icon className="h-6 w-6" />
</div>
<h3 className="text-base font-semibold sm:text-lg">{title}</h3>
</div>
<p className="text-sm text-gray-400 sm:text-base">{description}</p>
</div>
)
}
const version = "v3.8.0"
export function WhatsNewButton() {
const [isOpen, setIsOpen] = useState(false)
const buttonRef = useRef<HTMLDivElement>(null)
const canvasRef = useRef<HTMLCanvasElement>(null)
// animated border effect
useEffect(() => {
const canvas = canvasRef.current
const button = buttonRef.current
if (!canvas || !button) return
const ctx = canvas.getContext("2d")
if (!ctx) return
// set canvas size to match button size with extra space for glow
const updateCanvasSize = () => {
const rect = button.getBoundingClientRect()
// add extra padding for the glow effect
canvas.width = rect.width + 8
canvas.height = rect.height + 8
// position the canvas precisely
canvas.style.width = `${canvas.width}px`
canvas.style.height = `${canvas.height}px`
}
updateCanvasSize()
window.addEventListener("resize", updateCanvasSize)
// animation variables
let animationId: number
let position = 0
const animate = () => {
if (!ctx || !canvas) return
// clear canvas
ctx.clearRect(0, 0, canvas.width, canvas.height)
// calculate border path
const width = canvas.width - 4
const height = canvas.height - 4
const x = 2
const y = 2
const radius = height / 2
// draw rounded rectangle path
ctx.beginPath()
ctx.moveTo(x + radius, y)
ctx.lineTo(x + width - radius, y)
ctx.arcTo(x + width, y, x + width, y + radius, radius)
ctx.lineTo(x + width, y + height - radius)
ctx.arcTo(x + width, y + height, x + width - radius, y + height, radius)
ctx.lineTo(x + radius, y + height)
ctx.arcTo(x, y + height, x, y + height - radius, radius)
ctx.lineTo(x, y + radius)
ctx.arcTo(x, y, x + radius, y, radius)
ctx.closePath()
// create rotating gradient effect
position = (position + 0.016) % (Math.PI * 2)
const centerX = canvas.width / 2
const centerY = canvas.height / 2
const blueColor = "70, 130, 255"
// create rotating gradient
const gradient = ctx.createConicGradient(position, centerX, centerY)
// add color stops for a single flowing stream
gradient.addColorStop(0, `rgba(${blueColor}, 0)`)
gradient.addColorStop(0.2, `rgba(${blueColor}, 0.8)`)
gradient.addColorStop(0.4, `rgba(${blueColor}, 0)`)
gradient.addColorStop(1, `rgba(${blueColor}, 0)`)
// apply gradient
ctx.strokeStyle = gradient
ctx.lineWidth = 1.5
ctx.stroke()
// add subtle glow effect
ctx.shadowColor = `rgba(${blueColor}, 0.6)`
ctx.shadowBlur = 5
ctx.strokeStyle = `rgba(${blueColor}, 0.3)`
ctx.lineWidth = 0.5
ctx.stroke()
animationId = requestAnimationFrame(animate)
}
animate()
return () => {
window.removeEventListener("resize", updateCanvasSize)
if (animationId) cancelAnimationFrame(animationId)
}
}, [])
return (
<>
<div className="relative inline-flex" ref={buttonRef}>
<canvas
ref={canvasRef}
className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2"
style={{ pointerEvents: "none" }}
/>
<Link
href="#"
onClick={(e) => {
e.preventDefault()
setIsOpen(true)
}}
className="relative z-10 flex items-center space-x-2 rounded-full bg-black px-4 py-2 text-sm font-medium text-white transition-all hover:bg-gray-900">
<span>See what&apos;s new in {version}</span>
<ArrowRight className="h-3.5 w-3.5" />
</Link>
</div>
<AnimatePresence>
{isOpen && (
<>
<motion.div
className="fixed inset-0 z-40 bg-black/80 backdrop-blur-sm"
initial={{ opacity: 0, backdropFilter: "blur(0px)" }}
animate={{ opacity: 1, backdropFilter: "blur(8px)" }}
exit={{ opacity: 0, backdropFilter: "blur(0px)" }}
transition={{ duration: 0.2 }}
/>
<div className="fixed inset-0 z-50 overflow-y-auto" onClick={() => setIsOpen(false)}>
<div className="flex min-h-full items-center justify-center p-4">
<motion.div
className="relative w-full max-w-2xl rounded-lg border border-gray-800 bg-black p-6 sm:p-8"
initial={{ opacity: 0, y: 20, scale: 0.95 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
exit={{ opacity: 0, y: 20, scale: 0.95 }}
transition={{
type: "spring",
damping: 20,
stiffness: 400,
mass: 0.6,
duration: 0.25,
}}
onClick={(e) => {
// prevent clicks inside the panel from closing it
e.stopPropagation()
}}>
<div className="flex items-center justify-between gap-4">
<h2 className="text-xl font-bold sm:text-2xl">
What&apos;s New in Roo Code {version}
</h2>
<button
onClick={() => setIsOpen(false)}
className="flex-shrink-0 rounded-full p-1.5 text-gray-400 hover:bg-gray-800 hover:text-white">
<X className="h-5 w-5" />
</button>
</div>
<div className="mt-4 space-y-4 sm:mt-6 sm:space-y-6">
<Feature
icon={Code2}
color="blue"
title="AI-Powered Code Generation"
description="Generate high-quality code snippets and entire components with our new AI assistant. Trained on millions of code repositories to understand your project context."
/>
<Feature
icon={Users2}
color="purple"
title="Real-time Collaboration"
description="Work together with your team in real-time with our new collaborative editing features. See changes as they happen and resolve conflicts automatically."
/>
<Feature
icon={Zap}
color="green"
title="Performance Optimizations"
description="We've completely rewritten our core engine for blazing fast performance. Experience up to 10x faster build times and smoother development workflow."
/>
</div>
</motion.div>
</div>
</div>
</>
)}
</AnimatePresence>
</>
)
}

View file

@ -1 +0,0 @@
export { Providers } from "./providers"

View file

@ -1,78 +0,0 @@
"use client"
import { usePathname, useSearchParams } from "next/navigation"
import posthog from "posthog-js"
import { PostHogProvider as OriginalPostHogProvider } from "posthog-js/react"
import { useEffect, Suspense } from "react"
// Create a separate component for analytics tracking that uses useSearchParams
function PageViewTracker() {
const pathname = usePathname()
const searchParams = useSearchParams()
// Track page views
useEffect(() => {
// Only track page views if PostHog is properly initialized
if (pathname && process.env.NEXT_PUBLIC_POSTHOG_KEY) {
let url = window.location.origin + pathname
if (searchParams && searchParams.toString()) {
url = url + `?${searchParams.toString()}`
}
posthog.capture("$pageview", {
$current_url: url,
})
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [pathname, searchParams.toString()])
return null
}
export function PostHogProvider({ children }: { children: React.ReactNode }) {
useEffect(() => {
// Initialize PostHog only on the client side
if (typeof window !== "undefined") {
const posthogKey = process.env.NEXT_PUBLIC_POSTHOG_KEY
const posthogHost = process.env.NEXT_PUBLIC_POSTHOG_HOST
// Check if environment variables are set
if (!posthogKey) {
console.warn(
"PostHog API key is missing. Analytics will be disabled. " +
"Please set NEXT_PUBLIC_POSTHOG_KEY in your .env file.",
)
return
}
if (!posthogHost) {
console.warn(
"PostHog host URL is missing. Using default host. " +
"Please set NEXT_PUBLIC_POSTHOG_HOST in your .env file.",
)
}
posthog.init(posthogKey, {
api_host: posthogHost || "https://us.i.posthog.com",
capture_pageview: false, // We'll handle this manually
loaded: (posthogInstance) => {
if (process.env.NODE_ENV === "development") {
// Log to console in development
posthogInstance.debug()
}
},
respect_dnt: true, // Respect Do Not Track
})
}
// No explicit cleanup needed for posthog-js v1.231.0
}, [])
return (
<OriginalPostHogProvider client={posthog}>
<Suspense fallback={null}>
<PageViewTracker />
</Suspense>
{children}
</OriginalPostHogProvider>
)
}

View file

@ -1,20 +0,0 @@
"use client"
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
import { ThemeProvider } from "next-themes"
import { PostHogProvider } from "./posthog-provider"
const queryClient = new QueryClient()
export const Providers = ({ children }: { children: React.ReactNode }) => {
return (
<QueryClientProvider client={queryClient}>
<PostHogProvider>
<ThemeProvider attribute="class" defaultTheme="dark" enableSystem={false}>
{children}
</ThemeProvider>
</PostHogProvider>
</QueryClientProvider>
)
}

View file

@ -1,47 +0,0 @@
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground shadow hover:bg-primary/90",
destructive: "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
outline: "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
secondary: "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-9 px-4 py-2",
sm: "h-8 rounded-md px-3 text-xs",
lg: "h-10 rounded-md px-8",
icon: "h-9 w-9",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
},
)
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean
}
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button"
return <Comp className={cn(buttonVariants({ variant, size, className }))} ref={ref} {...props} />
},
)
Button.displayName = "Button"
export { Button, buttonVariants }

View file

@ -1,311 +0,0 @@
"use client"
import * as React from "react"
import * as RechartsPrimitive from "recharts"
import { cn } from "@/lib/utils"
// Format: { THEME_NAME: CSS_SELECTOR }
const THEMES = { light: "", dark: ".dark" } as const
export type ChartConfig = {
[k in string]: {
label?: React.ReactNode
icon?: React.ComponentType
} & ({ color?: string; theme?: never } | { color?: never; theme: Record<keyof typeof THEMES, string> })
}
type ChartContextProps = {
config: ChartConfig
}
const ChartContext = React.createContext<ChartContextProps | null>(null)
function useChart() {
const context = React.useContext(ChartContext)
if (!context) {
throw new Error("useChart must be used within a <ChartContainer />")
}
return context
}
const ChartContainer = React.forwardRef<
HTMLDivElement,
React.ComponentProps<"div"> & {
config: ChartConfig
children: React.ComponentProps<typeof RechartsPrimitive.ResponsiveContainer>["children"]
}
>(({ id, className, children, config, ...props }, ref) => {
const uniqueId = React.useId()
const chartId = `chart-${id || uniqueId.replace(/:/g, "")}`
return (
<ChartContext.Provider value={{ config }}>
<div
data-chart={chartId}
ref={ref}
className={cn(
"flex aspect-video justify-center text-xs [&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-none [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-sector]:outline-none [&_.recharts-surface]:outline-none",
className,
)}
{...props}>
<ChartStyle id={chartId} config={config} />
<RechartsPrimitive.ResponsiveContainer>{children}</RechartsPrimitive.ResponsiveContainer>
</div>
</ChartContext.Provider>
)
})
ChartContainer.displayName = "Chart"
const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
const colorConfig = Object.entries(config).filter(([, config]) => config.theme || config.color)
if (!colorConfig.length) {
return null
}
return (
<style
dangerouslySetInnerHTML={{
__html: Object.entries(THEMES)
.map(
([theme, prefix]) => `
${prefix} [data-chart=${id}] {
${colorConfig
.map(([key, itemConfig]) => {
const color = itemConfig.theme?.[theme as keyof typeof itemConfig.theme] || itemConfig.color
return color ? ` --color-${key}: ${color};` : null
})
.join("\n")}
}
`,
)
.join("\n"),
}}
/>
)
}
const ChartTooltip = RechartsPrimitive.Tooltip
const ChartTooltipContent = React.forwardRef<
HTMLDivElement,
React.ComponentProps<typeof RechartsPrimitive.Tooltip> &
React.ComponentProps<"div"> & {
hideLabel?: boolean
hideIndicator?: boolean
indicator?: "line" | "dot" | "dashed"
nameKey?: string
labelKey?: string
}
>(
(
{
active,
payload,
className,
indicator = "dot",
hideLabel = false,
hideIndicator = false,
label,
labelFormatter,
labelClassName,
formatter,
color,
nameKey,
labelKey,
},
ref,
) => {
const { config } = useChart()
const tooltipLabel = React.useMemo(() => {
if (hideLabel || !payload?.length) {
return null
}
const [item] = payload
const key = `${labelKey || item?.dataKey || item?.name || "value"}`
const itemConfig = getPayloadConfigFromPayload(config, item, key)
const value =
!labelKey && typeof label === "string"
? config[label as keyof typeof config]?.label || label
: itemConfig?.label
if (labelFormatter) {
return <div className={cn("font-medium", labelClassName)}>{labelFormatter(value, payload)}</div>
}
if (!value) {
return null
}
return <div className={cn("font-medium", labelClassName)}>{value}</div>
}, [label, labelFormatter, payload, hideLabel, labelClassName, config, labelKey])
if (!active || !payload?.length) {
return null
}
const nestLabel = payload.length === 1 && indicator !== "dot"
return (
<div
ref={ref}
className={cn(
"grid min-w-[8rem] items-start gap-1.5 rounded-lg border border-border/50 bg-background px-2.5 py-1.5 text-xs shadow-xl",
className,
)}>
{!nestLabel ? tooltipLabel : null}
<div className="grid gap-1.5">
{payload.map((item, index) => {
const key = `${nameKey || item.name || item.dataKey || "value"}`
const itemConfig = getPayloadConfigFromPayload(config, item, key)
const indicatorColor = color || item.payload.fill || item.color
return (
<div
key={item.dataKey}
className={cn(
"flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground",
indicator === "dot" && "items-center",
)}>
{formatter && item?.value !== undefined && item.name ? (
formatter(item.value, item.name, item, index, item.payload)
) : (
<>
{itemConfig?.icon ? (
<itemConfig.icon />
) : (
!hideIndicator && (
<div
className={cn(
"shrink-0 rounded-[2px] border-[--color-border] bg-[--color-bg]",
{
"h-2.5 w-2.5": indicator === "dot",
"w-1": indicator === "line",
"w-0 border-[1.5px] border-dashed bg-transparent":
indicator === "dashed",
"my-0.5": nestLabel && indicator === "dashed",
},
)}
style={
{
"--color-bg": indicatorColor,
"--color-border": indicatorColor,
} as React.CSSProperties
}
/>
)
)}
<div
className={cn(
"flex flex-1 justify-between leading-none",
nestLabel ? "items-end" : "items-center",
)}>
<div className="grid gap-1.5">
{nestLabel ? tooltipLabel : null}
<span className="text-muted-foreground">
{itemConfig?.label || item.name}
</span>
</div>
{item.value && (
<span className="font-mono font-medium tabular-nums text-foreground">
{item.value.toLocaleString()}
</span>
)}
</div>
</>
)}
</div>
)
})}
</div>
</div>
)
},
)
ChartTooltipContent.displayName = "ChartTooltip"
const ChartLegend = RechartsPrimitive.Legend
const ChartLegendContent = React.forwardRef<
HTMLDivElement,
React.ComponentProps<"div"> &
Pick<RechartsPrimitive.LegendProps, "payload" | "verticalAlign"> & {
hideIcon?: boolean
nameKey?: string
}
>(({ className, hideIcon = false, payload, verticalAlign = "bottom", nameKey }, ref) => {
const { config } = useChart()
if (!payload?.length) {
return null
}
return (
<div
ref={ref}
className={cn(
"flex items-center justify-center gap-4",
verticalAlign === "top" ? "pb-3" : "pt-3",
className,
)}>
{payload.map((item) => {
const key = `${nameKey || item.dataKey || "value"}`
const itemConfig = getPayloadConfigFromPayload(config, item, key)
return (
<div
key={item.value}
className={cn(
"flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground",
)}>
{itemConfig?.icon && !hideIcon ? (
<itemConfig.icon />
) : (
<div
className="h-2 w-2 shrink-0 rounded-[2px]"
style={{
backgroundColor: item.color,
}}
/>
)}
{itemConfig?.label}
</div>
)
})}
</div>
)
})
ChartLegendContent.displayName = "ChartLegend"
// Helper to extract item config from a payload.
function getPayloadConfigFromPayload(config: ChartConfig, payload: unknown, key: string) {
if (typeof payload !== "object" || payload === null) {
return undefined
}
const payloadPayload =
"payload" in payload && typeof payload.payload === "object" && payload.payload !== null
? payload.payload
: undefined
let configLabelKey: string = key
if (key in payload && typeof payload[key as keyof typeof payload] === "string") {
configLabelKey = payload[key as keyof typeof payload] as string
} else if (
payloadPayload &&
key in payloadPayload &&
typeof payloadPayload[key as keyof typeof payloadPayload] === "string"
) {
configLabelKey = payloadPayload[key as keyof typeof payloadPayload] as string
}
return configLabelKey in config ? config[configLabelKey] : config[key as keyof typeof config]
}
export { ChartContainer, ChartTooltip, ChartTooltipContent, ChartLegend, ChartLegendContent, ChartStyle }

View file

@ -1,5 +0,0 @@
export * from "./button"
export * from "./chart"
export * from "./modal"
export * from "./scroll-button"
export * from "./table"

View file

@ -1,95 +0,0 @@
"use client"
import * as React from "react"
import * as DialogPrimitive from "@radix-ui/react-dialog"
import { X } from "lucide-react"
import { cn } from "@/lib/utils"
const Dialog = DialogPrimitive.Root
const DialogTrigger = DialogPrimitive.Trigger
const DialogPortal = DialogPrimitive.Portal
const DialogClose = DialogPrimitive.Close
const DialogOverlay = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Overlay>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Overlay
ref={ref}
className={cn(
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
className,
)}
{...props}
/>
))
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName
const DialogContent = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
>(({ className, children, ...props }, ref) => (
<DialogPortal>
<DialogOverlay />
<DialogPrimitive.Content
ref={ref}
className={cn(
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
className,
)}
{...props}>
{children}
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
<X className="h-4 w-4" />
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
</DialogPrimitive.Content>
</DialogPortal>
))
DialogContent.displayName = DialogPrimitive.Content.displayName
const DialogHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
<div className={cn("flex flex-col space-y-1.5 text-center sm:text-left", className)} {...props} />
)
DialogHeader.displayName = "DialogHeader"
const DialogFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
<div className={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", className)} {...props} />
)
DialogFooter.displayName = "DialogFooter"
const DialogTitle = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Title
ref={ref}
className={cn("text-lg font-semibold leading-none tracking-tight", className)}
{...props}
/>
))
DialogTitle.displayName = DialogPrimitive.Title.displayName
const DialogDescription = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Description>,
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
>(({ className, ...props }, ref) => (
<DialogPrimitive.Description ref={ref} className={cn("text-sm text-muted-foreground", className)} {...props} />
))
DialogDescription.displayName = DialogPrimitive.Description.displayName
export {
Dialog,
DialogPortal,
DialogOverlay,
DialogTrigger,
DialogClose,
DialogContent,
DialogHeader,
DialogFooter,
DialogTitle,
DialogDescription,
}

View file

@ -1,33 +0,0 @@
"use client"
import { usePathname, useRouter } from "next/navigation"
interface ScrollButtonProps {
targetId: string
children: React.ReactNode
className?: string
onClick?: () => void
}
export function ScrollButton({ targetId, children, className = "", onClick }: ScrollButtonProps) {
const router = useRouter()
const pathname = usePathname()
const handleClick = () => {
if (pathname === "/") {
// if we're on the home page, use smooth scrolling
const section = document.getElementById(targetId)
section?.scrollIntoView({ behavior: "smooth" })
} else {
// if we're on a different page, navigate directly to the section
router.push(`/#${targetId}`)
}
onClick?.()
}
return (
<button onClick={handleClick} className={className}>
{children}
</button>
)
}

View file

@ -1,85 +0,0 @@
import * as React from "react"
import { cn } from "@/lib/utils"
const Table = React.forwardRef<HTMLTableElement, React.HTMLAttributes<HTMLTableElement>>(
({ className, ...props }, ref) => (
<div className="relative w-full overflow-auto">
<table ref={ref} className={cn("w-full caption-bottom text-sm", className)} {...props} />
</div>
),
)
Table.displayName = "Table"
const TableHeader = React.forwardRef<HTMLTableSectionElement, React.HTMLAttributes<HTMLTableSectionElement>>(
({ className, ...props }, ref) => <thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} />,
)
TableHeader.displayName = "TableHeader"
const TableBody = React.forwardRef<HTMLTableSectionElement, React.HTMLAttributes<HTMLTableSectionElement>>(
({ className, ...props }, ref) => (
<tbody ref={ref} className={cn("[&_tr:last-child]:border-0", className)} {...props} />
),
)
TableBody.displayName = "TableBody"
const TableFooter = React.forwardRef<HTMLTableSectionElement, React.HTMLAttributes<HTMLTableSectionElement>>(
({ className, ...props }, ref) => (
<tfoot
ref={ref}
className={cn("border-t bg-muted/50 font-medium [&>tr]:last:border-b-0", className)}
{...props}
/>
),
)
TableFooter.displayName = "TableFooter"
const TableRow = React.forwardRef<HTMLTableRowElement, React.HTMLAttributes<HTMLTableRowElement>>(
({ className, ...props }, ref) => (
<tr
ref={ref}
className={cn("border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted", className)}
{...props}
/>
),
)
TableRow.displayName = "TableRow"
const TableHead = React.forwardRef<HTMLTableCellElement, React.ThHTMLAttributes<HTMLTableCellElement>>(
// eslint-disable-next-line react/prop-types
({ className, ...props }, ref) => (
<th
ref={ref}
className={cn(
"h-10 px-2 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
className,
)}
{...props}
/>
),
)
TableHead.displayName = "TableHead"
const TableCell = React.forwardRef<HTMLTableCellElement, React.TdHTMLAttributes<HTMLTableCellElement>>(
// eslint-disable-next-line react/prop-types
({ className, ...props }, ref) => (
<td
ref={ref}
className={cn(
"p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
className,
)}
{...props}
/>
),
)
TableCell.displayName = "TableCell"
const TableCaption = React.forwardRef<HTMLTableCaptionElement, React.HTMLAttributes<HTMLTableCaptionElement>>(
({ className, ...props }, ref) => (
<caption ref={ref} className={cn("mt-4 text-sm text-muted-foreground", className)} {...props} />
),
)
TableCaption.displayName = "TableCaption"
export { Table, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption }

View file

@ -1,13 +0,0 @@
import { drizzle } from "drizzle-orm/libsql"
import { schema } from "./schema"
if ((!process.env.TURSO_CONNECTION_URL || !process.env.TURSO_AUTH_TOKEN) && !process.env.BENCHMARKS_DB_PATH) {
throw new Error("TURSO_CONNECTION_URL and TURSO_AUTH_TOKEN or BENCHMARKS_DB_PATH must be set")
}
const connection = process.env.BENCHMARKS_DB_PATH
? { url: process.env.BENCHMARKS_DB_PATH, concurrency: 50 }
: { url: process.env.TURSO_CONNECTION_URL!, authToken: process.env.TURSO_AUTH_TOKEN! }
export const db = drizzle({ schema, connection })

View file

@ -1,6 +0,0 @@
export { db } from "./db"
export * from "./schema"
export * from "./queries/runs"
export * from "./queries/tasks"
export * from "./queries/taskMetrics"

View file

@ -1,3 +0,0 @@
export class RecordNotFoundError extends Error {}
export class RecordNotCreatedError extends Error {}

View file

@ -1,19 +0,0 @@
import { desc, eq } from "drizzle-orm"
import { RecordNotFoundError } from "./errors"
import { schema } from "../schema"
import { db } from "../db"
const table = schema.runs
export const findRun = async (id: number) => {
const run = await db.query.runs.findFirst({ where: eq(table.id, id) })
if (!run) {
throw new RecordNotFoundError()
}
return run
}
export const getRuns = async () => db.query.runs.findMany({ orderBy: desc(table.id), with: { taskMetrics: true } })

View file

@ -1,17 +0,0 @@
import { eq } from "drizzle-orm"
import { RecordNotFoundError } from "./errors"
import { taskMetrics } from "../schema"
import { db } from "../db"
const table = taskMetrics
export const findTaskMetrics = async (id: number) => {
const run = await db.query.taskMetrics.findFirst({ where: eq(table.id, id) })
if (!run) {
throw new RecordNotFoundError()
}
return run
}

View file

@ -1,29 +0,0 @@
import { and, eq } from "drizzle-orm"
import { RecordNotFoundError } from "./errors"
import { tasks } from "../schema"
import { db } from "../db"
export const findTask = async (id: number) => {
const run = await db.query.tasks.findFirst({ where: eq(tasks.id, id) })
if (!run) {
throw new RecordNotFoundError()
}
return run
}
type GetTask = {
runId: number
language: string
exercise: string
}
export const getTask = async ({ runId, language, exercise }: GetTask) =>
db.query.tasks.findFirst({
where: and(eq(tasks.runId, runId), eq(tasks.language, language), eq(tasks.exercise, exercise)),
})
export const getTasks = async (runId: number) =>
db.query.tasks.findMany({ where: eq(tasks.runId, runId), with: { taskMetrics: true } })

View file

@ -1,78 +0,0 @@
import { sqliteTable, text, real, integer, blob, uniqueIndex } from "drizzle-orm/sqlite-core"
import { relations } from "drizzle-orm"
/**
* runs
*/
export const runs = sqliteTable("runs", {
id: integer({ mode: "number" }).primaryKey({ autoIncrement: true }),
taskMetricsId: integer({ mode: "number" }).references(() => taskMetrics.id),
model: text().notNull(),
description: text(),
settings: blob({ mode: "json" }).$type<unknown>(),
pid: integer({ mode: "number" }),
socketPath: text().notNull(),
passed: integer({ mode: "number" }).default(0).notNull(),
failed: integer({ mode: "number" }).default(0).notNull(),
createdAt: integer({ mode: "timestamp" }).notNull(),
})
export const runsRelations = relations(runs, ({ one }) => ({
taskMetrics: one(taskMetrics, { fields: [runs.taskMetricsId], references: [taskMetrics.id] }),
}))
export type Run = typeof runs.$inferSelect
/**
* tasks
*/
export const tasks = sqliteTable(
"tasks",
{
id: integer({ mode: "number" }).primaryKey({ autoIncrement: true }),
runId: integer({ mode: "number" })
.references(() => runs.id)
.notNull(),
taskMetricsId: integer({ mode: "number" }).references(() => taskMetrics.id),
language: text().notNull(),
exercise: text().notNull(),
passed: integer({ mode: "boolean" }),
startedAt: integer({ mode: "timestamp" }),
finishedAt: integer({ mode: "timestamp" }),
createdAt: integer({ mode: "timestamp" }).notNull(),
},
(table) => [uniqueIndex("tasks_language_exercise_idx").on(table.runId, table.language, table.exercise)],
)
export const tasksRelations = relations(tasks, ({ one }) => ({
run: one(runs, { fields: [tasks.runId], references: [runs.id] }),
taskMetrics: one(taskMetrics, { fields: [tasks.taskMetricsId], references: [taskMetrics.id] }),
}))
export type Task = typeof tasks.$inferSelect
/**
* taskMetrics
*/
export const taskMetrics = sqliteTable("taskMetrics", {
id: integer({ mode: "number" }).primaryKey({ autoIncrement: true }),
tokensIn: integer({ mode: "number" }).notNull(),
tokensOut: integer({ mode: "number" }).notNull(),
tokensContext: integer({ mode: "number" }).notNull(),
cacheWrites: integer({ mode: "number" }).notNull(),
cacheReads: integer({ mode: "number" }).notNull(),
cost: real().notNull(),
duration: integer({ mode: "number" }).notNull(),
createdAt: integer({ mode: "timestamp" }).notNull(),
})
export type TaskMetrics = typeof taskMetrics.$inferSelect
/**
* schema
*/
export const schema = { runs, runsRelations, tasks, tasksRelations, taskMetrics }

View file

@ -1,40 +0,0 @@
CREATE TABLE `runs` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`taskMetricsId` integer,
`model` text NOT NULL,
`description` text,
`pid` integer,
`socketPath` text NOT NULL,
`passed` integer DEFAULT 0 NOT NULL,
`failed` integer DEFAULT 0 NOT NULL,
`createdAt` integer NOT NULL,
FOREIGN KEY (`taskMetricsId`) REFERENCES `taskMetrics`(`id`) ON UPDATE no action ON DELETE no action
);
--> statement-breakpoint
CREATE TABLE `taskMetrics` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`tokensIn` integer NOT NULL,
`tokensOut` integer NOT NULL,
`tokensContext` integer NOT NULL,
`cacheWrites` integer NOT NULL,
`cacheReads` integer NOT NULL,
`cost` real NOT NULL,
`duration` integer NOT NULL,
`createdAt` integer NOT NULL
);
--> statement-breakpoint
CREATE TABLE `tasks` (
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
`runId` integer NOT NULL,
`taskMetricsId` integer,
`language` text NOT NULL,
`exercise` text NOT NULL,
`passed` integer,
`startedAt` integer,
`finishedAt` integer,
`createdAt` integer NOT NULL,
FOREIGN KEY (`runId`) REFERENCES `runs`(`id`) ON UPDATE no action ON DELETE no action,
FOREIGN KEY (`taskMetricsId`) REFERENCES `taskMetrics`(`id`) ON UPDATE no action ON DELETE no action
);
--> statement-breakpoint
CREATE UNIQUE INDEX `tasks_language_exercise_idx` ON `tasks` (`runId`,`language`,`exercise`);

View file

@ -1 +0,0 @@
ALTER TABLE `runs` ADD `settings` blob;

View file

@ -1,274 +0,0 @@
{
"version": "6",
"dialect": "sqlite",
"id": "c0fa8491-b5c0-493d-aa32-ddf280259c30",
"prevId": "00000000-0000-0000-0000-000000000000",
"tables": {
"runs": {
"name": "runs",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": true
},
"taskMetricsId": {
"name": "taskMetricsId",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"model": {
"name": "model",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"description": {
"name": "description",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"pid": {
"name": "pid",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"socketPath": {
"name": "socketPath",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"passed": {
"name": "passed",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": 0
},
"failed": {
"name": "failed",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": 0
},
"createdAt": {
"name": "createdAt",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"runs_taskMetricsId_taskMetrics_id_fk": {
"name": "runs_taskMetricsId_taskMetrics_id_fk",
"tableFrom": "runs",
"tableTo": "taskMetrics",
"columnsFrom": ["taskMetricsId"],
"columnsTo": ["id"],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"taskMetrics": {
"name": "taskMetrics",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": true
},
"tokensIn": {
"name": "tokensIn",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"tokensOut": {
"name": "tokensOut",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"tokensContext": {
"name": "tokensContext",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"cacheWrites": {
"name": "cacheWrites",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"cacheReads": {
"name": "cacheReads",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"cost": {
"name": "cost",
"type": "real",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"duration": {
"name": "duration",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"createdAt": {
"name": "createdAt",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"tasks": {
"name": "tasks",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": true
},
"runId": {
"name": "runId",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"taskMetricsId": {
"name": "taskMetricsId",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"language": {
"name": "language",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"exercise": {
"name": "exercise",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"passed": {
"name": "passed",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"startedAt": {
"name": "startedAt",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"finishedAt": {
"name": "finishedAt",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"createdAt": {
"name": "createdAt",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {
"tasks_language_exercise_idx": {
"name": "tasks_language_exercise_idx",
"columns": ["runId", "language", "exercise"],
"isUnique": true
}
},
"foreignKeys": {
"tasks_runId_runs_id_fk": {
"name": "tasks_runId_runs_id_fk",
"tableFrom": "tasks",
"tableTo": "runs",
"columnsFrom": ["runId"],
"columnsTo": ["id"],
"onDelete": "no action",
"onUpdate": "no action"
},
"tasks_taskMetricsId_taskMetrics_id_fk": {
"name": "tasks_taskMetricsId_taskMetrics_id_fk",
"tableFrom": "tasks",
"tableTo": "taskMetrics",
"columnsFrom": ["taskMetricsId"],
"columnsTo": ["id"],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
}
},
"views": {},
"enums": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
},
"internal": {
"indexes": {}
}
}

View file

@ -1,281 +0,0 @@
{
"version": "6",
"dialect": "sqlite",
"id": "8906647f-81d6-498a-897c-b1638c04c69a",
"prevId": "c0fa8491-b5c0-493d-aa32-ddf280259c30",
"tables": {
"runs": {
"name": "runs",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": true
},
"taskMetricsId": {
"name": "taskMetricsId",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"model": {
"name": "model",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"description": {
"name": "description",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"settings": {
"name": "settings",
"type": "blob",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"pid": {
"name": "pid",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"socketPath": {
"name": "socketPath",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"passed": {
"name": "passed",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": 0
},
"failed": {
"name": "failed",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": 0
},
"createdAt": {
"name": "createdAt",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {
"runs_taskMetricsId_taskMetrics_id_fk": {
"name": "runs_taskMetricsId_taskMetrics_id_fk",
"tableFrom": "runs",
"tableTo": "taskMetrics",
"columnsFrom": ["taskMetricsId"],
"columnsTo": ["id"],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"taskMetrics": {
"name": "taskMetrics",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": true
},
"tokensIn": {
"name": "tokensIn",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"tokensOut": {
"name": "tokensOut",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"tokensContext": {
"name": "tokensContext",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"cacheWrites": {
"name": "cacheWrites",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"cacheReads": {
"name": "cacheReads",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"cost": {
"name": "cost",
"type": "real",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"duration": {
"name": "duration",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"createdAt": {
"name": "createdAt",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
},
"tasks": {
"name": "tasks",
"columns": {
"id": {
"name": "id",
"type": "integer",
"primaryKey": true,
"notNull": true,
"autoincrement": true
},
"runId": {
"name": "runId",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"taskMetricsId": {
"name": "taskMetricsId",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"language": {
"name": "language",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"exercise": {
"name": "exercise",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"passed": {
"name": "passed",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"startedAt": {
"name": "startedAt",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"finishedAt": {
"name": "finishedAt",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"createdAt": {
"name": "createdAt",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
}
},
"indexes": {
"tasks_language_exercise_idx": {
"name": "tasks_language_exercise_idx",
"columns": ["runId", "language", "exercise"],
"isUnique": true
}
},
"foreignKeys": {
"tasks_runId_runs_id_fk": {
"name": "tasks_runId_runs_id_fk",
"tableFrom": "tasks",
"tableTo": "runs",
"columnsFrom": ["runId"],
"columnsTo": ["id"],
"onDelete": "no action",
"onUpdate": "no action"
},
"tasks_taskMetricsId_taskMetrics_id_fk": {
"name": "tasks_taskMetricsId_taskMetrics_id_fk",
"tableFrom": "tasks",
"tableTo": "taskMetrics",
"columnsFrom": ["taskMetricsId"],
"columnsTo": ["id"],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"checkConstraints": {}
}
},
"views": {},
"enums": {},
"_meta": {
"schemas": {},
"tables": {},
"columns": {}
},
"internal": {
"indexes": {}
}
}

View file

@ -1,20 +0,0 @@
{
"version": "7",
"dialect": "sqlite",
"entries": [
{
"idx": 0,
"version": "6",
"when": 1742599919625,
"tag": "0000_elite_raza",
"breakpoints": true
},
{
"idx": 1,
"version": "6",
"when": 1743089501047,
"tag": "0001_lush_reavers",
"breakpoints": true
}
]
}

View file

@ -1,19 +0,0 @@
export const EXTERNAL_LINKS = {
GITHUB: "https://github.com/RooVetGit/Roo-Code",
DISCORD: "https://discord.gg/roocode",
REDDIT: "https://reddit.com/r/RooCode",
DOCUMENTATION: "https://docs.roocode.com",
CAREERS: "https://careers.roocode.com",
ISSUES: "https://github.com/RooVetGit/Roo-Code/issues",
FEATURE_REQUESTS: "https://github.com/RooVetGit/Roo-Code/discussions/categories/feature-requests",
COMMUNITY: "https://github.com/RooVetGit/Roo-Code/discussions",
CHANGELOG: "https://github.com/RooVetGit/Roo-Code/blob/main/CHANGELOG.md",
PRIVACY_POLICY_EXTENSION: "https://github.com/RooVetGit/Roo-Code/blob/main/PRIVACY.md",
INTEGRATIONS: "https://docs.roocode.com/community",
TUTORIALS: "https://docs.roocode.com/tutorial-videos",
MARKETPLACE: "https://marketplace.visualstudio.com/items?itemName=RooVeterinaryInc.roo-cline",
}
export const INTERNAL_LINKS = {
PRIVACY_POLICY_WEBSITE: "/privacy",
}

View file

@ -1,8 +0,0 @@
const formatter = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
})
export const formatCurrency = (amount: number) => formatter.format(amount)
export const parsePrice = (price?: string) => (price ? parseFloat(price) * 1_000_000 : undefined)

View file

@ -1,22 +0,0 @@
export const formatDuration = (durationMs: number) => {
const seconds = Math.floor(durationMs / 1000)
const hours = Math.floor(seconds / 3600)
const minutes = Math.floor((seconds % 3600) / 60)
const remainingSeconds = seconds % 60
const parts = []
if (hours > 0) {
parts.push(`${hours}h`)
}
if (minutes > 0) {
parts.push(`${minutes}m`)
}
if (remainingSeconds > 0 || parts.length === 0) {
parts.push(`${remainingSeconds}s`)
}
return parts.join(" ")
}

View file

@ -1 +0,0 @@
export const formatScore = (score: number) => Math.round(score * 100)

View file

@ -1,15 +0,0 @@
export const formatTokens = (tokens: number, decimals = 0) => {
if (tokens < 1000) {
return tokens.toString()
}
if (tokens < 1000000) {
return `${(tokens / 1000).toFixed(decimals)}K`
}
if (tokens < 1000000000) {
return `${(tokens / 1000000).toFixed(decimals)}M`
}
return `${(tokens / 1000000000).toFixed(decimals)}B`
}

View file

@ -1,8 +0,0 @@
"use client"
import { useTheme } from "next-themes"
export function useLogoSrc(): string {
const { resolvedTheme } = useTheme()
return resolvedTheme === "light" ? "/Roo-Code-Logo-Horiz-blk.svg" : "/Roo-Code-Logo-Horiz-white.svg"
}

View file

@ -1,71 +0,0 @@
import { z } from "zod"
import { useQuery } from "@tanstack/react-query"
import { ModelInfo } from "@roo-code/types"
const parsePrice = (price?: string) => (price ? parseFloat(price) * 1_000_000 : undefined)
export const openRouterModelSchema = z.object({
id: z.string(),
name: z.string(),
description: z.string(),
created: z.number(),
context_length: z.number(),
pricing: z.object({
prompt: z.string().optional(),
completion: z.string().optional(),
}),
top_provider: z
.object({
max_completion_tokens: z.number().nullish(),
})
.optional(),
architecture: z
.object({
modality: z.string(),
})
.optional(),
})
export type OpenRouterModel = z.infer<typeof openRouterModelSchema>
export type OpenRouterModelRecord = Record<string, OpenRouterModel & { modelInfo: ModelInfo }>
export const getOpenRouterModels = async (): Promise<OpenRouterModelRecord> => {
const response = await fetch("https://openrouter.ai/api/v1/models")
if (!response.ok) {
console.error("Failed to fetch OpenRouter models")
return {}
}
const result = z.object({ data: z.array(openRouterModelSchema) }).safeParse(await response.json())
if (!result.success) {
console.error(result.error)
return {}
}
return result.data.data
.sort((a, b) => a.name.localeCompare(b.name))
.map((rawModel) => ({
...rawModel,
modelInfo: {
maxTokens: rawModel.top_provider?.max_completion_tokens ?? undefined,
contextWindow: rawModel.context_length,
inputPrice: parsePrice(rawModel.pricing?.prompt),
outputPrice: parsePrice(rawModel.pricing?.completion),
description: rawModel.description,
supportsPromptCache: false,
supportsImages: false,
supportsThinking: false,
tiers: [],
},
}))
.reduce((acc, model) => {
acc[model.id] = model
return acc
}, {} as OpenRouterModelRecord)
}
export const useOpenRouterModels = () =>
useQuery<OpenRouterModelRecord>({ queryKey: ["getOpenRouterModels"], queryFn: getOpenRouterModels })

View file

@ -1,4 +0,0 @@
export * from "./format-currency"
export * from "./format-duration"
export * from "./format-score"
export * from "./format-tokens"

View file

@ -1,29 +0,0 @@
"use server"
import { sql } from "drizzle-orm"
import { db, tasks } from "@/db"
export type Language = "go" | "java" | "javascript" | "python" | "rust"
export const getLanguageScores = async () => {
const records = await db
.select({
runId: tasks.runId,
language: sql<Language>`language`,
score: sql<number>`cast(sum(case when ${tasks.passed} = 1 then 1 else 0 end) as float) / count(*)`,
})
.from(tasks)
.groupBy(tasks.runId, tasks.language)
const results: Record<number, Record<Language, number>> = {}
for (const { runId, language, score } of records) {
if (!results[runId]) {
results[runId] = { go: 0, java: 0, javascript: 0, python: 0, rust: 0 }
}
results[runId][language] = score
}
return results
}

View file

@ -1 +0,0 @@
export * from "./get-language-scores"

View file

@ -1,118 +0,0 @@
export async function getGitHubStars() {
try {
const res = await fetch("https://api.github.com/repos/RooVetGit/Roo-Code")
const data = await res.json()
if (typeof data.stargazers_count !== "number") {
console.error("GitHub API: Invalid stargazers count. Possible that you got rate-limited?")
return null
}
return formatNumber(data.stargazers_count)
} catch (error) {
console.error("Error fetching GitHub stars:", error)
return null
}
}
export async function getVSCodeReviews() {
const res = await fetch("https://marketplace.visualstudio.com/_apis/public/gallery/extensionquery", {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json;api-version=7.1-preview.1",
},
body: JSON.stringify({
filters: [
{
criteria: [
{
filterType: 7,
value: "RooVeterinaryInc.roo-cline",
},
],
},
],
flags: 914,
}),
})
try {
const data = await res.json()
const reviews = data?.results?.[0]?.extensions?.[0]?.reviews
if (!reviews) {
console.error("VSCode API: Missing reviews in response")
return []
}
/* eslint-disable @typescript-eslint/no-explicit-any */
return reviews.map((review: any) => ({
name: review.reviewer?.displayName || "Anonymous",
rating: review.rating,
content: review.text,
date: new Date(review.date).toLocaleDateString(),
}))
} catch (error) {
console.error("Error fetching VSCode reviews:", error)
return []
}
}
export async function getVSCodeDownloads() {
const res = await fetch("https://marketplace.visualstudio.com/_apis/public/gallery/extensionquery", {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json;api-version=7.1-preview.1",
},
body: JSON.stringify({
filters: [
{
criteria: [
{
filterType: 7,
value: "RooVeterinaryInc.roo-cline",
},
],
},
],
flags: 914,
}),
})
try {
const data = await res.json()
const statistics = data?.results?.[0]?.extensions?.[0]?.statistics
if (!statistics) {
console.error("VSCode API: Missing statistics in response")
return null
}
/* eslint-disable @typescript-eslint/no-explicit-any */
const installStat = statistics.find((stat: any) => stat.statisticName === "install")
if (!installStat) {
console.error("VSCode API: Install count not found")
return null
}
return formatNumber(installStat.value)
} catch (error) {
console.error("Error fetching VSCode downloads:", error)
return null
}
}
function formatNumber(num: number): string {
// divide by 1000 to convert to "thousands" format,
// multiply by 10, floor the result, then divide by 10 to keep one decimal place.
const truncated = Math.floor((num / 1000) * 10) / 10
// ensure one decimal is always shown and append "k"
return truncated.toFixed(1) + "k"
// examples:
// console.log(formatNumber(337231)) -> "337.2k"
// console.log(formatNumber(23233)) -> "23.2k"
// console.log(formatNumber(2322)) -> "2.3k"
// console.log(formatNumber(212)) -> "0.2k"
}

View file

@ -1,6 +0,0 @@
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}

View file

@ -1,118 +0,0 @@
import type { Config } from "tailwindcss"
const config: Config = {
darkMode: ["class"],
content: [
"./pages/**/*.{ts,tsx}",
"./components/**/*.{ts,tsx}",
"./app/**/*.{ts,tsx}",
"./src/**/*.{ts,tsx}",
"*.{js,ts,jsx,tsx,mdx}",
],
theme: {
container: {
center: true,
padding: "2rem",
screens: {
"2xl": "1400px",
},
},
extend: {
colors: {
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
primary: {
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))",
},
secondary: {
DEFAULT: "hsl(var(--secondary))",
foreground: "hsl(var(--secondary-foreground))",
},
destructive: {
DEFAULT: "hsl(var(--destructive))",
foreground: "hsl(var(--destructive-foreground))",
},
muted: {
DEFAULT: "hsl(var(--muted))",
foreground: "hsl(var(--muted-foreground))",
},
accent: {
DEFAULT: "hsl(var(--accent))",
foreground: "hsl(var(--accent-foreground))",
},
popover: {
DEFAULT: "hsl(var(--popover))",
foreground: "hsl(var(--popover-foreground))",
},
card: {
DEFAULT: "hsl(var(--card))",
foreground: "hsl(var(--card-foreground))",
},
"chart-1": {
DEFAULT: "hsl(var(--chart-1))",
foreground: "hsl(var(--chart-1-foreground))",
},
"chart-2": {
DEFAULT: "hsl(var(--chart-2))",
foreground: "hsl(var(--chart-2-foreground))",
},
"chart-3": {
DEFAULT: "hsl(var(--chart-3))",
foreground: "hsl(var(--chart-3-foreground))",
},
"chart-4": {
DEFAULT: "hsl(var(--chart-4))",
foreground: "hsl(var(--chart-4-foreground))",
},
"chart-5": {
DEFAULT: "hsl(var(--chart-5))",
foreground: "hsl(var(--chart-5-foreground))",
},
},
borderRadius: {
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)",
},
keyframes: {
"accordion-down": {
from: { height: "0" },
to: { height: "var(--radix-accordion-content-height)" },
},
"accordion-up": {
from: { height: "var(--radix-accordion-content-height)" },
to: { height: "0" },
},
"fade-in": {
from: { opacity: "0" },
to: { opacity: "1" },
},
"fade-out": {
from: { opacity: "1" },
to: { opacity: "0" },
},
"pulse-glow": {
"0%, 100%": { opacity: "0.6" },
"50%": { opacity: "0.8" },
},
},
animation: {
"accordion-down": "accordion-down 0.2s ease-out",
"accordion-up": "accordion-up 0.2s ease-out",
"fade-in": "fade-in 0.5s ease-out",
"fade-out": "fade-out 0.5s ease-out",
"pulse-glow": "pulse-glow 3s infinite",
},
screens: {
xs: "420px",
},
},
},
plugins: [require("tailwindcss-animate"), require("@tailwindcss/typography")],
}
export default config

View file

@ -1,8 +0,0 @@
{
"extends": "@roo-code/config-typescript/nextjs.json",
"compilerOptions": {
"paths": { "@/*": ["./src/*"] }
},
"include": ["next-env.d.ts", "src/**/*.ts", "src/**/*.tsx", ".next/types/**/*.ts", "drizzle.config.ts"],
"exclude": ["node_modules"]
}

View file

@ -354,21 +354,3 @@ export const PROVIDER_SETTINGS_KEYS = keysOf<ProviderSettings>()([
"litellmApiKey",
"litellmModelId",
])
export const MODEL_ID_KEYS: Partial<keyof ProviderSettings>[] = [
"apiModelId",
"glamaModelId",
"openRouterModelId",
"openAiModelId",
"ollamaModelId",
"lmStudioModelId",
"lmStudioDraftModelId",
"unboundModelId",
"requestyModelId",
"litellmModelId",
]
export const getModelId = (settings: ProviderSettings): string | undefined => {
const modelIdKey = MODEL_ID_KEYS.find((key) => settings[key])
return modelIdKey ? (settings[modelIdKey] as string) : undefined
}

2346
pnpm-lock.yaml generated

File diff suppressed because it is too large Load diff