feat(web): add install modal with multi-editor options

This commit is contained in:
Roo Code 2026-01-30 19:17:23 +00:00
parent 0cd257af89
commit ac656dabd6
3 changed files with 166 additions and 17 deletions

View file

@ -13,6 +13,7 @@ import { EXTERNAL_LINKS } from "@/lib/constants"
import { useLogoSrc } from "@/lib/hooks/use-logo-src"
import { ScrollButton } from "@/components/ui"
import ThemeToggle from "@/components/chromes/theme-toggle"
import { InstallModal } from "@/components/homepage/install-modal"
import { Brain, Cloud, Puzzle, Slack, X } from "lucide-react"
import {
NavigationMenu,
@ -40,6 +41,7 @@ interface NavBarProps {
export function NavBar({ stars, downloads }: NavBarProps) {
const [isMenuOpen, setIsMenuOpen] = useState(false)
const [isInstallModalOpen, setIsInstallModalOpen] = useState(false)
const logoSrc = useLogoSrc()
return (
@ -202,16 +204,15 @@ export function NavBar({ stars, downloads }: NavBarProps) {
className="hidden items-center gap-1.5 rounded-md bg-primary px-4 py-2 text-sm text-primary-foreground transition-all duration-200 hover:shadow-lg hover:scale-105 md:flex">
Sign Up
</a>
<Link
href={EXTERNAL_LINKS.MARKETPLACE}
target="_blank"
<button
onClick={() => setIsInstallModalOpen(true)}
className="hidden items-center gap-1.5 rounded-md bg-primary px-4 py-2 text-sm text-primary-foreground transition-all duration-200 hover:shadow-lg hover:scale-105 md:flex whitespace-nowrap">
<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>
</button>
</div>
{/* Mobile Menu Button */}
@ -332,14 +333,15 @@ export function NavBar({ stars, downloads }: NavBarProps) {
<div className="flex items-center rounded-md p-3 transition-colors hover:bg-accent">
<ThemeToggle />
</div>
<Link
href={EXTERNAL_LINKS.MARKETPLACE}
target="_blank"
className="inline-flex items-center gap-2 rounded-md p-3 text-sm transition-colors hover:bg-accent hover:text-foreground"
onClick={() => setIsMenuOpen(false)}>
<button
onClick={() => {
setIsMenuOpen(false)
setIsInstallModalOpen(true)
}}
className="inline-flex items-center gap-2 rounded-md p-3 text-sm transition-colors hover:bg-accent hover:text-foreground">
<VscVscode className="h-6 w-6" />
{downloads !== null && <span>{downloads}</span>}
</Link>
</button>
</div>
<div className="flex gap-2 px-4 pb-4">
<a
@ -362,6 +364,9 @@ export function NavBar({ stars, downloads }: NavBarProps) {
</div>
</nav>
</div>
{/* Install Modal */}
<InstallModal open={isInstallModalOpen} onOpenChange={setIsInstallModalOpen} />
</header>
)
}

View file

@ -0,0 +1,139 @@
"use client"
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from "@/components/ui/modal"
import { motion } from "framer-motion"
interface Editor {
name: string
icon: React.ReactNode
href: string
}
// VS Code icon
const VSCodeIcon = () => (
<svg viewBox="0 0 100 100" className="w-full h-full" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M74.5 13.5L39.5 45L21.5 30.5L13.5 34V66L21.5 69.5L39.5 55L74.5 86.5L86.5 81.5V18.5L74.5 13.5Z"
fill="#007ACC"
/>
<path d="M74.5 13.5L39.5 45L21.5 30.5L13.5 34V66L21.5 69.5L39.5 55L74.5 86.5" fill="#1F9CF0" />
<path d="M21.5 30.5L13.5 34V66L21.5 69.5V30.5Z" fill="#0065A9" />
<path d="M74.5 13.5L86.5 18.5V81.5L74.5 86.5V13.5Z" fill="#0065A9" />
</svg>
)
// VS Code Insiders icon
const VSCodeInsidersIcon = () => (
<svg viewBox="0 0 100 100" className="w-full h-full" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M74.5 13.5L39.5 45L21.5 30.5L13.5 34V66L21.5 69.5L39.5 55L74.5 86.5L86.5 81.5V18.5L74.5 13.5Z"
fill="#24BFA5"
/>
<path d="M74.5 13.5L39.5 45L21.5 30.5L13.5 34V66L21.5 69.5L39.5 55L74.5 86.5" fill="#3DC9A9" />
<path d="M21.5 30.5L13.5 34V66L21.5 69.5V30.5Z" fill="#1A9E82" />
<path d="M74.5 13.5L86.5 18.5V81.5L74.5 86.5V13.5Z" fill="#1A9E82" />
</svg>
)
// Cursor icon
const CursorIcon = () => (
<svg viewBox="0 0 100 100" className="w-full h-full" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="100" height="100" rx="12" fill="#1E1E1E" />
<path d="M30 25L70 50L30 75V25Z" fill="none" stroke="#FFFFFF" strokeWidth="6" strokeLinejoin="round" />
</svg>
)
// Windsurf icon
const WindsurfIcon = () => (
<svg viewBox="0 0 100 100" className="w-full h-full" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="100" height="100" rx="12" fill="#0F0F0F" />
<path
d="M25 65L40 35L55 55L75 25"
fill="none"
stroke="#06B6D4"
strokeWidth="6"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M25 75L50 50L75 75"
fill="none"
stroke="#8B5CF6"
strokeWidth="6"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
)
const editors: Editor[] = [
{
name: "VS Code",
icon: <VSCodeIcon />,
href: "vscode:extension/RooVeterinaryInc.roo-cline",
},
{
name: "VS Code Insiders",
icon: <VSCodeInsidersIcon />,
href: "vscode-insiders:extension/RooVeterinaryInc.roo-cline",
},
{
name: "Cursor",
icon: <CursorIcon />,
href: "cursor:extension/RooVeterinaryInc.roo-cline",
},
{
name: "Windsurf",
icon: <WindsurfIcon />,
href: "windsurf:extension/RooVeterinaryInc.roo-cline",
},
]
interface InstallModalProps {
open: boolean
onOpenChange: (open: boolean) => void
}
export function InstallModal({ open, onOpenChange }: InstallModalProps) {
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="sm:max-w-[500px] p-6">
<DialogHeader className="text-center pb-4">
<DialogTitle className="text-2xl font-bold text-center">Install Roo Code</DialogTitle>
<DialogDescription className="text-center text-muted-foreground">Select your IDE</DialogDescription>
</DialogHeader>
<div className="space-y-2 max-h-[60vh] overflow-y-auto pr-2 scrollbar-thin scrollbar-thumb-gray-300 scrollbar-track-transparent">
{editors.map((editor, index) => (
<motion.div
key={editor.name}
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: index * 0.05, duration: 0.3 }}
className="group relative">
<div className="relative flex items-center gap-3 p-2.5 bg-muted/30 border border-border/50 rounded-lg hover:bg-muted/50 hover:border-border transition-colors duration-150">
<div className="relative flex-shrink-0">
<div className="w-12 h-12 bg-background shadow-sm border border-border/50 rounded-lg p-2 overflow-hidden">
{editor.icon}
</div>
</div>
<div className="flex-grow min-w-0">
<h3 className="text-base font-semibold text-foreground">{editor.name}</h3>
</div>
<div className="flex items-center">
<a
href={editor.href}
target={editor.href.startsWith("http") ? "_blank" : undefined}
rel={editor.href.startsWith("http") ? "noopener noreferrer" : undefined}
className="px-4 py-2 bg-primary hover:bg-primary/90 active:bg-primary/80 text-primary-foreground text-xs font-semibold rounded-md transition-colors duration-150 shadow-sm">
Install
</a>
</div>
</div>
</motion.div>
))}
</div>
</DialogContent>
</Dialog>
)
}

View file

@ -1,14 +1,17 @@
"use client"
import { useState } from "react"
import { VscVscode } from "react-icons/vsc"
import Link from "next/link"
import { motion } from "framer-motion"
import { InstallModal } from "./install-modal"
interface InstallSectionProps {
downloads: string | null
}
export function InstallSection({ downloads }: InstallSectionProps) {
const [isModalOpen, setIsModalOpen] = useState(false)
const backgroundVariants = {
hidden: {
opacity: 0,
@ -57,16 +60,15 @@ export function InstallSection({ downloads }: InstallSectionProps) {
</p>
<div className="mt-12 flex flex-col items-center justify-center gap-6">
{/* Enhanced VSCode Marketplace button */}
<Link
href="https://marketplace.visualstudio.com/items?itemName=RooVeterinaryInc.roo-cline"
target="_blank"
{/* Install button that opens modal */}
<button
onClick={() => setIsModalOpen(true)}
className="group relative inline-flex w-full items-center justify-center gap-3 rounded-xl bg-gradient-to-r from-blue-600 to-cyan-600 px-6 py-4 text-lg font-medium text-white shadow-lg transition-all duration-300 hover:from-blue-700 hover:to-cyan-700 hover:shadow-xl hover:shadow-blue-500/25 dark:from-blue-500 dark:to-cyan-500 dark:hover:from-blue-600 dark:hover:to-cyan-600 sm:w-auto sm:px-8 sm:text-xl">
<div className="absolute -inset-px rounded-xl bg-gradient-to-r from-blue-400 via-cyan-400 to-blue-400 opacity-0 blur transition-opacity duration-500 group-hover:opacity-70" />
<div className="relative flex flex-col md:flex-row items-center md:gap-3">
<VscVscode className="h-6 w-6 shrink-0" />
<span className="flex flex-col md:flex-row items-center md:gap-2">
<span>From VS Code Marketplace</span>
<span>Install Roo Code</span>
{downloads !== null && (
<>
<span className="font-black opacity-60 hidden md:inline">
@ -77,7 +79,7 @@ export function InstallSection({ downloads }: InstallSectionProps) {
)}
</span>
</div>
</Link>
</button>
{/* Enhanced CLI install section */}
<div className="group relative w-full max-w-xl">
@ -100,6 +102,9 @@ export function InstallSection({ downloads }: InstallSectionProps) {
</div>
</div>
</div>
{/* Install Modal */}
<InstallModal open={isModalOpen} onOpenChange={setIsModalOpen} />
</section>
)
}