94 lines
2.9 KiB
TypeScript
94 lines
2.9 KiB
TypeScript
import Link from "next/link";
|
||
import {
|
||
BookOpen,
|
||
ClipboardCheck,
|
||
ListTree,
|
||
PenLine,
|
||
Sparkles,
|
||
type LucideIcon,
|
||
} from "lucide-react";
|
||
|
||
import { AiToolbarMoreMenu } from "@/components/AiToolbarMoreMenu";
|
||
import {
|
||
aiToolItems,
|
||
primaryAiToolItems,
|
||
secondaryAiToolItems,
|
||
} from "@/lib/nav/ai-tools";
|
||
import type { ActiveNav } from "@/lib/nav/items";
|
||
import { buttonClass } from "@/lib/ui/variants";
|
||
|
||
interface AiToolbarProps {
|
||
projectId: string;
|
||
activeNav?: ActiveNav;
|
||
}
|
||
|
||
// T4-a · 项目内页常驻 AI 工具条(顶栏下,UX §3/§5)。
|
||
// 桌面展示完整工具条;窄屏只保留写本章/审稿,把低频入口收进更多菜单。
|
||
export function AiToolbar({ projectId, activeNav }: AiToolbarProps) {
|
||
const primaryItems = primaryAiToolItems(projectId);
|
||
const secondaryItems = secondaryAiToolItems(projectId);
|
||
return (
|
||
<nav
|
||
aria-label="AI 工具条"
|
||
className="sticky top-16 z-20 flex h-12 items-center gap-2 border-b border-line bg-panel px-4 sm:px-6"
|
||
>
|
||
<div className="flex min-w-0 flex-1 items-center gap-2 overflow-hidden lg:hidden">
|
||
{primaryItems.map((item) => {
|
||
const isActive = item.key === activeNav;
|
||
const className = toolClassName(item.primary === true, isActive);
|
||
const Icon = toolIcon(item.key);
|
||
return (
|
||
<Link
|
||
key={item.href}
|
||
href={item.href}
|
||
aria-current={isActive ? "page" : undefined}
|
||
className={className}
|
||
>
|
||
<Icon className="h-4 w-4" aria-hidden="true" />
|
||
<span>{item.label}</span>
|
||
</Link>
|
||
);
|
||
})}
|
||
<AiToolbarMoreMenu items={secondaryItems} activeNav={activeNav} />
|
||
</div>
|
||
|
||
<div className="hidden items-center gap-2 overflow-x-auto whitespace-nowrap lg:flex">
|
||
{aiToolItems(projectId).map((item) => {
|
||
const isActive = item.key === activeNav;
|
||
const className = toolClassName(item.primary === true, isActive);
|
||
const Icon = toolIcon(item.key);
|
||
return (
|
||
<Link
|
||
key={item.href}
|
||
href={item.href}
|
||
aria-current={isActive ? "page" : undefined}
|
||
className={className}
|
||
>
|
||
<Icon className="h-4 w-4" aria-hidden="true" />
|
||
<span>{item.label}</span>
|
||
</Link>
|
||
);
|
||
})}
|
||
</div>
|
||
</nav>
|
||
);
|
||
}
|
||
|
||
function toolIcon(key: ActiveNav): LucideIcon {
|
||
if (key === "write") return PenLine;
|
||
if (key === "review") return ClipboardCheck;
|
||
if (key === "outline") return ListTree;
|
||
if (key === "codex") return BookOpen;
|
||
return Sparkles;
|
||
}
|
||
|
||
function toolClassName(isPrimary: boolean, isActive: boolean): string {
|
||
if (isActive) {
|
||
return buttonClass({ variant: "outline", size: "sm" });
|
||
}
|
||
if (isPrimary) {
|
||
return buttonClass({ variant: "primary", size: "sm" });
|
||
}
|
||
return buttonClass({ variant: "secondary", size: "sm" });
|
||
}
|