refactor: 主题系统 → monorepo 五个独立网站

- packages/core: @aidir/core 底座库(src/pages 页面组件体 1:1 镜像路由树 / kit 站点外壳工厂 / content/blog / scripts)
- sites/: 5 个独立 Next.js 网站(ai-stack / launchai / the-ai-ledger / devai-tools / findai)
  - 每站独立 package.json / next.config / tailwind / messages / .env / 构建
  - src/app 约 56 个薄路由文件(re-export 底座页面 + 内联路由段配置 revalidate/dynamic)
  - src/site/brand.tsx 站点配置 + src/site/home.tsx 站点首页(createMarketingLayout / createHomePage 工厂)
- generate-site.mjs: 站点生成器(新增网站 = SITES 加条目,零 Core 修改)
- start-all-sites.sh: 一键启动 3001-3005
- next-config.mjs: 底座统一 Next 配置工厂(next-intl + MDX + standalone)
- 五站共享同一套 PostgreSQL / Better Auth / Admin(设计文档 §42)
- 验证: 5 站 build OK / typecheck OK / 55 路由冒烟 200 / 品牌-内容-截图 差异确认 / 登录-投票-重复检测 e2e PASS
This commit is contained in:
AI Directory Platform
2026-09-08 19:19:33 +00:00
parent e46b4b52a3
commit 4a1a034386
699 changed files with 19829 additions and 2029 deletions
+107
View File
@@ -0,0 +1,107 @@
// 自动生成:站点首页组件(源自底座 minimal 形态,品牌来自 @/site/brand)
import { siteConfig } from "@/site/brand";
import { Search } from "lucide-react";
import { ToolCardGrid } from "@/components/tool-card";
import { LocaleLink } from "@/components/locale-link";
import { getHomeDataSafe } from "@/kit/home-data";
/** Minimal:极简 AI 搜索引擎(一个框,一个任务) */
const navItems = [
{ label: "Search", href: "/tools" },
{ label: "Use Cases", href: "/ai-tools-for" },
{ label: "Rankings", href: "/rankings" },
{ label: "Submit", href: "/submit" },
];
const footerGroups = [
{
title: "Search",
links: [
{ label: "All Tools", href: "/tools" },
{ label: "Use Cases", href: "/ai-tools-for" },
{ label: "Rankings", href: "/rankings" },
],
},
{
title: "About",
links: [
{ label: "Submit a Tool", href: "/submit" },
{ label: "Privacy", href: "/privacy" },
{ label: "Terms", href: "/terms" },
],
},
];
const popularSearches = [
"write a blog post",
"generate video",
"code assistant",
"make a logo",
"edit photos",
"transcribe meetings",
];
async function SiteHome({ locale }: { locale: string }) {
const data = await getHomeDataSafe(6);
return (
<div className="flex min-h-screen flex-col bg-background">
{/* 极简搜索首屏 */}
<section className="flex flex-1 flex-col items-center justify-center px-4 pb-16 pt-24 text-center">
<LocaleLink href="/" className="text-3xl font-bold tracking-tight text-foreground">
{siteConfig.brand.name}
</LocaleLink>
<p className="mt-1 text-xs uppercase tracking-[0.35em] text-muted-foreground">
AI tool search
</p>
<form action={`/${locale}/tools`} className="mt-10 w-full max-w-xl">
<div className="relative">
<Search className="absolute left-5 top-1/2 h-5 w-5 -translate-y-1/2 text-muted-foreground" />
<input
name="search"
autoFocus
placeholder="I need to create marketing videos..."
className="h-14 w-full rounded-full border border-border bg-background pl-14 pr-6 text-base text-foreground shadow-lg shadow-black/[0.03] placeholder:text-muted-foreground focus:border-primary/50 focus:outline-none focus:ring-4 focus:ring-primary/10"
aria-label="Search AI tools"
/>
</div>
</form>
<div className="mt-6 flex flex-wrap items-center justify-center gap-2">
{popularSearches.map((q) => (
<LocaleLink
key={q}
href={`/tools?search=${encodeURIComponent(q)}`}
className="rounded-full px-3 py-1.5 text-xs text-muted-foreground transition-colors hover:bg-secondary hover:text-foreground"
>
{q}
</LocaleLink>
))}
</div>
</section>
{/* 搜索之下:Trending */}
{data.trending.length > 0 && (
<section className="mx-auto w-full max-w-3xl px-4 pb-20 sm:px-6">
<h2 className="mb-4 text-center text-xs font-medium uppercase tracking-[0.25em] text-muted-foreground">
Trending now
</h2>
<ToolCardGrid tools={data.trending.slice(0, 4)} locale={locale} />
<div className="mt-6 text-center">
<LocaleLink
href="/rankings"
className="text-xs font-medium text-muted-foreground hover:text-primary"
>
See full rankings
</LocaleLink>
</div>
</section>
)}
</div>
);
}
export { SiteHome };