Files
heicode-win/website/components/header.tsx
T
gongzhiyongandOmX 3ac920a493 Route website registration to CodeGW signup
Website visitors now have a single registration path through the CodeGW sign-up page, while the header no longer presents a separate login action beside registration. Static export output is included so the repository matches the deployed Azure Static Web Apps artifact.

Constraint: Website must use the CodeGW sign-up URL and remove the header login button.
Rejected: Keeping the separate Agnet login URL | it sends registration traffic to the wrong surface.
Confidence: high
Scope-risk: narrow
Directive: Keep public website registration links pointed at https://code.xinghanlab.com/sign-up unless product routing changes.
Tested: pnpm run build; Azure Static Web Apps production deploy; live curl checks for sign-up URL and removed login link.
Not-tested: Browser visual pass after deployment.
Co-authored-by: OmX <omx@oh-my-codex.dev>
2026-05-18 21:48:47 +08:00

161 lines
5.6 KiB
TypeScript

"use client";
import { useState } from "react";
import Link from "next/link";
import Image from "next/image";
import { Button } from "@/components/ui/button";
import { Menu, X } from "lucide-react";
import type { Lang } from "@/app/page";
const copy = {
en: {
navLinks: [
{ href: "/#workflow", label: "Workflow" },
{ href: "/#products", label: "Surfaces" },
{ href: "/#features", label: "Capabilities" },
{ href: "/swarm", label: "Swarm" },
{ href: "/launch", label: "Launch" },
{ href: "/#security", label: "Security" },
],
register: "Register",
start: "Get started",
toggle: "中文",
menuOpen: "Open menu",
menuClose: "Close menu",
},
zh: {
navLinks: [
{ href: "/#workflow", label: "流程" },
{ href: "/#products", label: "产品面" },
{ href: "/#features", label: "能力" },
{ href: "/swarm", label: "蜂群" },
{ href: "/launch", label: "上线" },
{ href: "/#security", label: "安全" },
],
register: "注册",
start: "开始使用",
toggle: "EN",
menuOpen: "打开菜单",
menuClose: "关闭菜单",
},
} satisfies Record<Lang, {
navLinks: Array<{ href: string; label: string }>;
register: string;
start: string;
toggle: string;
menuOpen: string;
menuClose: string;
}>;
const MANAGER_URL = "https://code.xinghanlab.com/";
const REGISTER_URL = "https://code.xinghanlab.com/sign-up";
const DOCS_URL = "https://doc.xinghanlab.com/";
export function Header({
lang,
setLang,
}: {
lang: Lang;
setLang: (lang: Lang) => void;
}) {
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
const t = copy[lang];
const toggleLang = () => setLang(lang === "en" ? "zh" : "en");
return (
<header className="fixed top-0 left-0 right-0 z-50 border-b border-border/60 bg-background/75 backdrop-blur-xl">
<div className="mx-auto flex h-16 max-w-7xl items-center justify-between px-4 sm:px-6 lg:px-8">
<Link href="/" className="flex items-center gap-2">
<div className="flex h-9 w-9 items-center justify-center rounded-xl border border-border/70 bg-card/80 text-foreground shadow-sm">
<Image src="/taiji-logo.png" alt="Heicode" width={18} height={18} />
</div>
<span className="text-lg font-black tracking-tight text-foreground">
Heicode
</span>
</Link>
{/* Desktop Navigation */}
<nav className="hidden items-center gap-8 md:flex">
{t.navLinks.map((link) => (
<Link
key={link.href}
href={link.href}
className="text-sm text-muted-foreground transition-colors hover:text-foreground"
>
{link.label}
</Link>
))}
</nav>
<div className="hidden items-center gap-4 md:flex">
<Button variant="outline" size="sm" className="rounded-full bg-card/50" onClick={toggleLang}>
{t.toggle}
</Button>
<Button variant="ghost" size="sm" className="rounded-full" asChild>
<a href={DOCS_URL} target="_blank" rel="noreferrer">
{lang === "zh" ? "文档" : "Docs"}
</a>
</Button>
<Button variant="outline" size="sm" className="rounded-full bg-card/50" asChild>
<a href={REGISTER_URL} target="_blank" rel="noreferrer">
{t.register}
</a>
</Button>
<Button size="sm" className="rounded-full bg-foreground text-background hover:bg-foreground/90" asChild>
<a href={MANAGER_URL} target="_blank" rel="noreferrer">
{t.start}
</a>
</Button>
</div>
{/* Mobile Menu Button */}
<button
className="md:hidden"
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
aria-label={mobileMenuOpen ? t.menuClose : t.menuOpen}
>
{mobileMenuOpen ? <X className="h-6 w-6" /> : <Menu className="h-6 w-6" />}
</button>
</div>
{/* Mobile Navigation */}
{mobileMenuOpen && (
<div className="border-t border-border bg-background md:hidden">
<nav className="flex flex-col px-4 py-4">
{t.navLinks.map((link) => (
<Link
key={link.href}
href={link.href}
className="py-3 text-sm text-muted-foreground transition-colors hover:text-foreground"
onClick={() => setMobileMenuOpen(false)}
>
{link.label}
</Link>
))}
<div className="mt-4 flex flex-col gap-2">
<Button variant="outline" size="sm" className="justify-start rounded-full bg-card/50" onClick={toggleLang}>
{t.toggle}
</Button>
<Button variant="ghost" size="sm" className="justify-start rounded-full" asChild>
<a href={DOCS_URL} target="_blank" rel="noreferrer">
{lang === "zh" ? "文档" : "Docs"}
</a>
</Button>
<Button variant="outline" size="sm" className="justify-start rounded-full bg-card/50" asChild>
<a href={REGISTER_URL} target="_blank" rel="noreferrer">
{t.register}
</a>
</Button>
<Button size="sm" className="rounded-full bg-foreground text-background hover:bg-foreground/90" asChild>
<a href={MANAGER_URL} target="_blank" rel="noreferrer">
{t.start}
</a>
</Button>
</div>
</nav>
</div>
)}
</header>
);
}