Files
heicode/website/components/hero.tsx
T

174 lines
7.9 KiB
TypeScript

import { Button } from "@/components/ui/button";
import { ArrowRight, CheckCircle2, LockKeyhole, Play, Sparkles } from "lucide-react";
import type { Lang } from "@/app/page";
const MANAGER_URL = "https://code.xinghanlab.com/";
const copy = {
en: {
eyebrow: "From idea to production software",
titleA: "One idea,",
titleB: "one AI development team.",
intro:
"Heicode organizes sub-Agnet teams, connects your code, documents, SK skills and cloud resources, and continuously carries work through requirements, development, testing, fixes, deployment and maintenance.",
primary: "Start with Heicode",
secondary: "Open Manager",
bullets: ["Input ideas in the client", "Bind resources in Manager", "Ship through Agnet"],
cockpit: "Heicode Task Cockpit",
project: "Small-team task management SaaS",
running: "Running",
inputLabel: "User input",
prompt:
"I want to build a small-team task management SaaS with login, projects, tasks, comments, notifications and admin, deployed to Azure.",
agents: [
["Product Agnet", "Requirements and acceptance", "Done"],
["Backend Agnet", "APIs and data model", "Building"],
["Frontend Agnet", "Pages and interaction", "Waiting for API"],
["Ops Agnet", "Deployment and observability", "Approval pending"],
],
approvalTitle: "Production deployment requires client approval",
approvalDesc:
"The secret vault will derive a 15-minute short-lived credential. Sub Agnets never receive long-term secrets.",
},
zh: {
eyebrow: "从想法到上线的软件生命周期平台",
titleA: "一个想法,",
titleB: "一支 AI 开发团队。",
intro:
"Heicode 组织子 Agnet,接入你的代码、文档、SK 和云资源,持续完成需求、开发、测试、修复、部署和后续维护。",
primary: "开始使用 Heicode",
secondary: "打开 Manager",
bullets: ["客户端输入想法", "Manager 绑定资源", "Agnet 交付部署"],
cockpit: "Heicode Task Cockpit",
project: "小团队任务管理 SaaS",
running: "运行中",
inputLabel: "用户输入",
prompt:
"我想做一个小团队任务管理 SaaS,需要登录、项目、任务、评论、通知和后台管理,希望部署到 Azure。",
agents: [
["Product Agnet", "需求和验收标准", "完成"],
["Backend Agnet", "API 与数据模型", "开发中"],
["Frontend Agnet", "页面与交互", "等待接口"],
["Ops Agnet", "部署与观测", "待审批"],
],
approvalTitle: "生产部署需要客户端审批",
approvalDesc: "密钥保管器将派生 15 分钟短期凭证,子 Agnet 不接触长期密钥。",
},
} satisfies Record<Lang, {
eyebrow: string;
titleA: string;
titleB: string;
intro: string;
primary: string;
secondary: string;
bullets: string[];
cockpit: string;
project: string;
running: string;
inputLabel: string;
prompt: string;
agents: string[][];
approvalTitle: string;
approvalDesc: string;
}>;
export function Hero({ lang }: { lang: Lang }) {
const t = copy[lang];
return (
<section className="relative overflow-hidden pt-28 pb-16 sm:pt-36 sm:pb-24">
<div className="pointer-events-none absolute inset-0 bg-[linear-gradient(to_right,color-mix(in_oklch,var(--foreground)_5%,transparent)_1px,transparent_1px),linear-gradient(to_bottom,color-mix(in_oklch,var(--foreground)_5%,transparent)_1px,transparent_1px)] bg-[size:42px_42px]" />
<div className="pointer-events-none absolute left-1/2 top-24 h-[420px] w-[70vw] -translate-x-1/2 rounded-full bg-primary/10 blur-[120px]" />
<div className="relative mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div className="grid gap-12 lg:grid-cols-[1.03fr_0.97fr] lg:items-center">
<div>
<div className="mb-6 inline-flex items-center gap-2 rounded-full border border-border bg-card/70 px-4 py-2 text-sm font-semibold shadow-sm backdrop-blur">
<Sparkles className="h-4 w-4 text-primary" />
{t.eyebrow}
</div>
<h1 className="max-w-4xl text-5xl font-black leading-[0.95] tracking-[-0.06em] sm:text-7xl lg:text-8xl">
{t.titleA}
<span className="block text-primary">{t.titleB}</span>
</h1>
<p className="mt-7 max-w-2xl text-lg leading-8 text-muted-foreground sm:text-xl">
{t.intro}
</p>
<div className="mt-9 flex flex-col gap-4 sm:flex-row">
<Button size="lg" className="h-12 rounded-full bg-foreground px-7 text-background hover:bg-foreground/90" asChild>
<a href={MANAGER_URL} target="_blank" rel="noreferrer">
{t.primary}
<ArrowRight className="h-4 w-4" />
</a>
</Button>
<Button variant="outline" size="lg" className="h-12 rounded-full border-foreground/20 bg-card/50 px-7" asChild>
<a href={MANAGER_URL} target="_blank" rel="noreferrer">
<Play className="h-4 w-4" />
{t.secondary}
</a>
</Button>
</div>
<div className="mt-8 grid max-w-2xl gap-3 text-sm text-muted-foreground sm:grid-cols-3">
{t.bullets.map((item) => (
<div key={item} className="flex items-center gap-2">
<CheckCircle2 className="h-4 w-4 text-accent" />
{item}
</div>
))}
</div>
</div>
<div className="relative">
<div className="absolute -left-6 -top-6 hidden h-24 w-24 rounded-full border border-primary/30 lg:block" />
<div className="rounded-[2rem] border border-foreground/10 bg-foreground p-3 shadow-2xl">
<div className="overflow-hidden rounded-[1.5rem] bg-[#10151d] text-white">
<div className="flex items-center justify-between border-b border-white/10 px-5 py-4">
<div>
<p className="text-xs uppercase tracking-[0.22em] text-white/45">{t.cockpit}</p>
<p className="mt-1 font-semibold">{t.project}</p>
</div>
<span className="rounded-full bg-emerald-400/15 px-3 py-1 text-xs font-semibold text-emerald-200">
{t.running}
</span>
</div>
<div className="grid gap-3 p-5">
<div className="rounded-2xl bg-white/[0.06] p-4">
<p className="text-xs text-white/45">{t.inputLabel}</p>
<p className="mt-2 text-sm leading-6 text-white/85">{t.prompt}</p>
</div>
<div className="grid gap-3 sm:grid-cols-2">
{t.agents.map(([role, work, state]) => (
<div key={role} className="rounded-2xl border border-white/10 bg-white/[0.04] p-4">
<p className="font-semibold">{role}</p>
<p className="mt-1 text-xs text-white/50">{work}</p>
<p className="mt-4 text-xs font-semibold text-primary-foreground/90">{state}</p>
</div>
))}
</div>
<div className="rounded-2xl border border-amber-300/20 bg-amber-300/10 p-4">
<div className="flex items-start gap-3">
<LockKeyhole className="mt-0.5 h-5 w-5 text-amber-200" />
<div>
<p className="text-sm font-semibold text-amber-100">{t.approvalTitle}</p>
<p className="mt-1 text-xs leading-5 text-amber-100/65">
{t.approvalDesc}
</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
);
}