diff --git a/apps/web/app/(pages)/compare/page.tsx b/apps/web/app/(pages)/compare/page.tsx new file mode 100644 index 0000000..d9d9cdf --- /dev/null +++ b/apps/web/app/(pages)/compare/page.tsx @@ -0,0 +1,429 @@ +"use client" + +import { motion } from "framer-motion" +import Navbar from "@/components/homepage/navbar" +import Footer from "@/components/homepage/footer" +import Link from "next/link" + +const tools = [ + { + name: "Supercode", + tagline: "your AI co-pilot, your rules", + href: "/", + badge: "", + badgeColor: "text-primary", + }, + { + name: "OpenCode", + tagline: "terminal coding agent", + href: "https://github.com/sst/opencode", + badge: "OSS", + badgeColor: "text-emerald-400", + }, + { + name: "Claude Code", + tagline: "agentic coding by Anthropic", + href: "https://docs.anthropic.com/en/docs/claude-code/overview", + badge: "PRO", + badgeColor: "text-orange-400", + }, + { + name: "Hermes Agent", + tagline: "autonomous AI agent", + href: "https://github.com/NousResearch/hermes-agent", + badge: "OSS", + badgeColor: "text-violet-400", + }, + { + name: "Warp", + tagline: "AI-native terminal", + href: "https://www.warp.dev", + badge: "GUI", + badgeColor: "text-sky-400", + }, + { + name: "Cursor", + tagline: "AI code editor", + href: "https://cursor.com", + badge: "GUI", + badgeColor: "text-indigo-400", + }, +] + +interface FeatureRow { + type: "feature" + name: string + supercode: boolean + opencode: boolean + claude: boolean + hermes: boolean + warp: boolean + cursor: boolean +} + +interface CategoryDivider { + type: "divider" + label: string +} + +type Row = CategoryDivider | FeatureRow + +const rows: Row[] = [ + { type: "divider", label: "Core" }, + { type: "feature", name: "Terminal-native", supercode: true, opencode: true, claude: true, hermes: false, warp: true, cursor: false }, + { type: "feature", name: "Open source", supercode: true, opencode: true, claude: false, hermes: true, warp: false, cursor: false }, + { type: "divider", label: "Access & Control" }, + { type: "feature", name: "Full machine access", supercode: true, opencode: false, claude: false, hermes: true, warp: false, cursor: false }, + { type: "feature", name: "Granular permissions", supercode: true, opencode: false, claude: false, hermes: false, warp: false, cursor: false }, + { type: "divider", label: "Models & Data" }, + { type: "feature", name: "Free models included", supercode: true, opencode: false, claude: false, hermes: false, warp: false, cursor: true }, + { type: "feature", name: "BYO API key", supercode: true, opencode: true, claude: false, hermes: false, warp: false, cursor: true }, + { type: "feature", name: "Multi-model support", supercode: true, opencode: false, claude: false, hermes: false, warp: false, cursor: true }, + { type: "feature", name: "Persistent memory", supercode: true, opencode: false, claude: false, hermes: false, warp: false, cursor: false }, + { type: "divider", label: "Capabilities" }, + { type: "feature", name: "File editing", supercode: true, opencode: true, claude: true, hermes: true, warp: false, cursor: true }, + { type: "feature", name: "Web search", supercode: true, opencode: false, claude: false, hermes: true, warp: false, cursor: false }, + { type: "feature", name: "Voice control", supercode: true, opencode: false, claude: false, hermes: false, warp: false, cursor: false }, + { type: "feature", name: "Session history", supercode: true, opencode: true, claude: true, hermes: false, warp: true, cursor: true }, +] + +function CheckMark() { + return +} + +function CrossMark() { + return +} + +function FeatureCell({ value }: { value: boolean }) { + return ( + + {value ? : } + + ) +} + +function ScoreBar({ value, label }: { value: number; label: string }) { + return ( +
+
+ {label} + {value}/12 +
+
+ +
+
+ ) +} + +const scores = [ + { label: "Supercode", value: 12 }, + { label: "OpenCode", value: 5 }, + { label: "Claude Code", value: 3 }, + { label: "Hermes Agent", value: 5 }, + { label: "Warp", value: 3 }, + { label: "Cursor", value: 6 }, +] + +function StatCard({ + icon, + value, + label, + index, +}: { + icon: string + value: string + label: string + index: number +}) { + return ( + +
{icon}
+
+ {value} +
+
{label}
+
+ ) +} + +export default function ComparePage() { + return ( +
+
+
+ + + + {/* Hero */} +
+
+ + + — Supercode v0.1.7 + +

+ + + Supercode + + + +
+ vs the rest +

+

+ An honest comparison. Supercode is the only terminal AI agent that gives you full machine + control with granular permissions — no vendor lock-in, no black boxes. +

+
+
+
+ + {/* Stats row */} +
+
+
+ + + + +
+
+
+ + {/* Comparison table */} +
+
+ +

+ Feature comparison +

+

+ Every feature that matters for a terminal AI coding agent. +

+
+ +
+ + + + + {tools.map((tool) => ( + + ))} + + + + {rows.map((row, i) => { + if (row.type === "divider") { + return ( + + + + ) + } + return ( + + + + + + + + + + ) + })} + +
+ + Feature + + +
+ + {tool.name} + + + {tool.badge} + +
+
+
+ + {row.label} + +
+
+
+ + {row.name} + + + +
+
+
+
+ + {/* Score bars */} +
+
+ +

+ At a glance +

+

+ How each tool stacks up across all 12 features. +

+
+ +
+ {scores.map((score) => ( + + ))} +
+
+
+ + {/* What makes supercode different */} +
+
+ +

+ The difference +

+
+ +
+ {[ + { + icon: "◈", + title: "Full machine control", + desc: "Not just your editor tab. Supercode reads files, runs commands, edits code, opens apps, and searches the web — all from the terminal.", + }, + { + icon: "⊡", + title: "You approve every action", + desc: "Granular permissions per file, command, or tool. Nothing happens without your say-so. No black-box agent running wild.", + }, + { + icon: "⊞", + title: "Zero lock-in", + desc: "Free models included out of the box. Or bring your own API key for Claude, GPT, Gemini — whatever you prefer. Your choice, always.", + }, + ].map((item, i) => ( + +
{item.icon}
+

+ {item.title} +

+

+ {item.desc} +

+
+ ))} +
+
+
+ + {/* CTA */} +
+
+ +

+ Try{" "} + + Supercode + +

+

+ Beta launches June 22. Join the waitlist for early access. +

+
+ + Join waitlist + + + View on GitHub + +
+
+
+
+ +
+
+ ) +} diff --git a/apps/web/app/globals.css b/apps/web/app/globals.css index 88538b9..49f8a15 100644 --- a/apps/web/app/globals.css +++ b/apps/web/app/globals.css @@ -175,6 +175,15 @@ body { letter-spacing: var(--tracking-normal); } +@keyframes cursor-blink { + 0%, 100% { opacity: 1; } + 50% { opacity: 0; } +} + +@utility animate-cursor-blink { + animation: cursor-blink 1s step-end infinite; +} + @layer base { * { @apply border-border outline-ring/50; diff --git a/apps/web/components/homepage/footer.tsx b/apps/web/components/homepage/footer.tsx index c71f78e..e812112 100644 --- a/apps/web/components/homepage/footer.tsx +++ b/apps/web/components/homepage/footer.tsx @@ -1,52 +1,341 @@ -import React from 'react'; -import { Github, Twitter } from 'lucide-react'; +"use client" -const DOCS_URL = process.env.NEXT_PUBLIC_DOCS_URL || 'http://localhost:3001/docs/intro'; +import React from "react" +import Link from "next/link" +import { Github, Twitter, Mail, ArrowUpRight } from "lucide-react" +import { motion } from "framer-motion" + +const DOCS_URL = + process.env.NEXT_PUBLIC_DOCS_URL || "http://localhost:3001/docs/intro" + +const PIXEL_FONT: Record = { + S: [ + [0, 1, 1, 1, 0], + [1, 0, 0, 0, 1], + [1, 0, 0, 0, 0], + [0, 1, 1, 1, 0], + [0, 0, 0, 0, 1], + [1, 0, 0, 0, 1], + [0, 1, 1, 1, 0], + ], + U: [ + [1, 0, 0, 0, 1], + [1, 0, 0, 0, 1], + [1, 0, 0, 0, 1], + [1, 0, 0, 0, 1], + [1, 0, 0, 0, 1], + [1, 0, 0, 0, 1], + [0, 1, 1, 1, 0], + ], + P: [ + [1, 1, 1, 1, 0], + [1, 0, 0, 0, 1], + [1, 0, 0, 0, 1], + [1, 1, 1, 1, 0], + [1, 0, 0, 0, 0], + [1, 0, 0, 0, 0], + [1, 0, 0, 0, 0], + ], + E: [ + [1, 1, 1, 1, 1], + [1, 0, 0, 0, 0], + [1, 0, 0, 0, 0], + [1, 1, 1, 1, 0], + [1, 0, 0, 0, 0], + [1, 0, 0, 0, 0], + [1, 1, 1, 1, 1], + ], + R: [ + [1, 1, 1, 1, 0], + [1, 0, 0, 0, 1], + [1, 0, 0, 0, 1], + [1, 1, 1, 1, 0], + [1, 0, 1, 0, 0], + [1, 0, 0, 1, 0], + [1, 0, 0, 0, 1], + ], + C: [ + [0, 1, 1, 1, 0], + [1, 0, 0, 0, 1], + [1, 0, 0, 0, 0], + [1, 0, 0, 0, 0], + [1, 0, 0, 0, 0], + [1, 0, 0, 0, 1], + [0, 1, 1, 1, 0], + ], + O: [ + [0, 1, 1, 1, 0], + [1, 0, 0, 0, 1], + [1, 0, 0, 0, 1], + [1, 0, 0, 0, 1], + [1, 0, 0, 0, 1], + [1, 0, 0, 0, 1], + [0, 1, 1, 1, 0], + ], + D: [ + [1, 1, 1, 1, 0], + [1, 0, 0, 0, 1], + [1, 0, 0, 0, 1], + [1, 0, 0, 0, 1], + [1, 0, 0, 0, 1], + [1, 0, 0, 0, 1], + [1, 1, 1, 1, 0], + ], +} + +const PIXEL_SIZE = 8 +const PIXEL_GAP = 1 + +const PixelLogo = () => { + const [hoverTick, setHoverTick] = React.useState(0) + + return ( +
setHoverTick((t) => t + 1)} + style={{ + filter: "drop-shadow(0 0 6px hsl(var(--primary) / 0.3))", + }} + > + {/* Scanline overlay */} +
+
+ {"SUPERCODE".split("").map((char, ci) => ( +
+ {PIXEL_FONT[char].map((row, ri) => ( +
+ {row.map((pixel, pi) => ( +
+ ))} +
+ ))} +
+ ))} +
+ +
+ ) +} + +const linkVariants = { + hidden: { opacity: 0, x: -6 }, + visible: (i: number) => ({ + opacity: 1, + x: 0, + transition: { delay: 0.3 + i * 0.04, duration: 0.3, ease: "easeOut" as const }, + }), +} + +interface LinkGroupProps { + title: string + links: { label: string; href: string; external?: boolean }[] +} + +const LinkGroup = ({ title, links }: LinkGroupProps) => ( +
+

+ ${title} +

+
    + {links.map((link, i) => ( + + {link.external ? ( + + + {link.label} + + + ) : ( + + + {link.label} + + )} + + ))} +
+
+) const Footer = () => { return ( -