Files
ai-directory-platform/sites/devai-tools/src/site/home.tsx
T
AI Directory Platform 6baf0246d2 feat: 深度设计五站视觉语言 + 语言路由修复
- 语言路由:localeDetection 关闭,/ 固定英文、/zh 固定中文,URL 决定语言
  (不再按 cookie/Accept-Language 自动跳转;语言切换器显式切换不受影响)
- 五站独立视觉语言(data-theme 驱动的设计系统层):
  · AI Stack:深蓝渐变 Hero + 统计条 + 卡片左色条 hover(专业 SaaS)
  · LaunchAI:橙色渐变 Banner + 斜切底边 + 暖白底(活力社区)
  · The AI Ledger:黑色报头日期条 + 衬线字体 + 直角 + 双细线 + 报纸排版(媒体)
  · DevAI.tools:全站深色 + 等宽字体 + 终端 Hero + 扫描线纹理 + 系统状态条(终端)
  · findai:纯白极简 + 紫色聚焦光圈 + 无界卡片(搜索引擎)
- 修复 :first-of-type 误伤(hero 样式收敛到专用 class)
2026-09-09 19:22:58 +00:00

202 lines
7.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 自动生成:站点首页组件(源自底座 niche 形态,品牌来自 @/site/brand)
import { siteConfig } from "@/site/brand";
import { Code2, Terminal, Workflow, Layers } from "lucide-react";
import { getTranslations } from "next-intl/server";
import { ToolCard } from "@/components/tool-card";
import { LocaleLink } from "@/components/locale-link";
import { SectionHeader } from "@/kit/section";
import { getHomeDataSafe } from "@/kit/home-data";
/** Niche:垂直行业目录(AI for Developers) */
const navItems = [
{ label: "Tools", href: "/tools" },
{ label: "Workflows", href: "/best" },
{ label: "Use Cases", href: "/ai-tools-for" },
{ label: "Rankings", href: "/rankings" },
{ label: "Submit", href: "/submit" },
];
const footerGroups = [
{
title: "Directory",
links: [
{ label: "All Tools", href: "/tools" },
{ label: "Rankings", href: "/rankings" },
{ label: "Use Cases", href: "/ai-tools-for" },
],
},
{
title: "Build",
links: [
{ label: "Workflows", href: "/best" },
{ label: "Submit a Tool", href: "/submit" },
{ label: "Blog", href: "/blog" },
],
},
{
title: "Legal",
links: [
{ label: "Terms", href: "/terms" },
{ label: "Privacy", href: "/privacy" },
],
},
];
async function SiteHome({ locale }: { locale: string }) {
const t = await getTranslations("siteHome");
const th = await getTranslations("hero");
const data = await getHomeDataSafe(6);
return (
<div className="min-h-screen bg-background">
{/* Terminal 风格 Hero + 绿色系统状态条 */}
<div className="border-b border-border bg-background">
<div className="mx-auto flex max-w-5xl items-center gap-2 px-4 py-2 font-mono text-[11px] text-muted-foreground sm:px-6">
<span className="inline-block h-2 w-2 animate-pulse rounded-full bg-emerald-500" />
system: online · 1,000+ tools indexed · updated hourly
</div>
</div>
<section className="border-b border-border bg-[hsl(var(--secondary))]/40">
<div className="mx-auto max-w-5xl px-4 py-14 sm:px-6 md:py-20">
<div className="mx-auto max-w-2xl overflow-hidden rounded-xl border border-border bg-background shadow-sm">
<div className="flex items-center gap-1.5 border-b border-border bg-secondary/50 px-4 py-2.5">
<span className="h-2.5 w-2.5 rounded-full bg-red-400" />
<span className="h-2.5 w-2.5 rounded-full bg-amber-400" />
<span className="h-2.5 w-2.5 rounded-full bg-emerald-400" />
<span className="ml-2 text-xs text-muted-foreground">devai — zsh</span>
</div>
<div className="space-y-1.5 p-5 font-mono text-sm">
<p className="text-muted-foreground">
<span className="text-emerald-600 dark:text-emerald-400">$</span>{" "}
find-ai-tools --for developer
</p>
<p className="flex items-center gap-2 text-foreground">
<Terminal className="h-3.5 w-3.5 text-emerald-600 dark:text-emerald-400" />
{th("title")}
</p>
<p className="text-xs leading-relaxed text-muted-foreground">
{th("description")}
</p>
<p className="pt-2">
<LocaleLink
href="/tools"
className="inline-block rounded-md bg-primary px-4 py-1.5 font-sans text-xs font-semibold text-primary-foreground hover:opacity-90"
>
Browse tools
</LocaleLink>
</p>
</div>
</div>
</div>
</section>
<div className="mx-auto max-w-5xl space-y-12 px-4 py-12 sm:px-6">
{/* Use Cases as workflows */}
{data.useCases.length > 0 && (
<section>
<SectionHeader
title={t("shipTitle")}
subtitle={t("shipSubtitle")}
viewAllHref="/ai-tools-for"
viewAllLabel={t("useCasesViewAll")}
/>
<div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
{data.useCases.slice(0, 6).map((uc) => (
<LocaleLink
key={uc.id}
href={`/ai-tools-for/${uc.slug}`}
className="group rounded-xl border border-border p-4 transition-all hover:border-primary/40 hover:bg-primary/5"
>
<div className="flex items-center justify-between">
<Workflow className="h-4 w-4 text-primary" />
<span className="text-xs text-muted-foreground tabular-nums">
{uc.toolCount} tools
</span>
</div>
<h3 className="mt-2 text-sm font-semibold text-foreground group-hover:text-primary">
{locale === "zh" && uc.nameZh ? uc.nameZh : uc.nameEn}
</h3>
</LocaleLink>
))}
</div>
</section>
)}
{/* Trending tools */}
{data.trending.length > 0 && (
<section>
<SectionHeader
title={t("upvotedTitle")}
viewAllHref="/rankings?board=trending"
/>
<div className="grid gap-3 md:grid-cols-2">
{data.trending.slice(0, 6).map((tool, i) => (
<ToolCard
key={tool.id}
tool={tool}
locale={locale}
rank={i + 1}
/>
))}
</div>
</section>
)}
{/* Workflows(collections) */}
{data.collections.length > 0 && (
<section>
<SectionHeader
title={t("workflowsTitle")}
subtitle={t("workflowsSubtitle")}
viewAllHref="/best"
viewAllLabel={t("workflowsViewAll")}
/>
<div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
{data.collections.slice(0, 3).map((col) => (
<LocaleLink
key={col.id}
href={`/best/${col.slug}`}
className="group rounded-xl border border-border p-4 transition-all hover:border-primary/40"
>
<Layers className="h-4 w-4 text-primary" />
<h3 className="mt-2 text-sm font-semibold text-foreground group-hover:text-primary">
{locale === "zh" && col.nameZh ? col.nameZh : col.nameEn}
</h3>
<p className="mt-1 line-clamp-2 text-xs text-muted-foreground">
{locale === "zh" && col.introZh ? col.introZh : col.introEn}
</p>
</LocaleLink>
))}
</div>
</section>
)}
{/* Categories */}
{data.categories.length > 0 && (
<section>
<SectionHeader title={t("categoriesTitle")} viewAllHref="/tools" />
<div className="flex flex-wrap gap-2">
{data.categories.slice(0, 14).map((cat) => (
<LocaleLink
key={cat.id}
href={`/category/${cat.slug}`}
className="rounded-lg border border-border px-3 py-1.5 font-mono text-xs text-foreground transition-colors hover:border-primary/40 hover:bg-primary/5"
>
{locale === "zh" && cat.nameZh ? cat.nameZh : cat.nameEn}
<span className="ml-1.5 text-muted-foreground">
{cat.toolCount}
</span>
</LocaleLink>
))}
</div>
</section>
)}
</div>
</div>
);
}
export { SiteHome };