122 lines
2.8 KiB
TypeScript
122 lines
2.8 KiB
TypeScript
"use client";
|
|
|
|
import { ArrowDown, ArrowUp } from "lucide-react";
|
|
import type { ReactNode } from "react";
|
|
import { Button } from "@/components/ui/button";
|
|
import {
|
|
Card,
|
|
CardContent,
|
|
CardDescription,
|
|
CardHeader,
|
|
CardTitle,
|
|
} from "@/components/ui/card";
|
|
import { cn } from "@/lib/utils";
|
|
import type { SettingStatusTone } from "../_lib/types";
|
|
import { SettingStatusBadge } from "./setting-status-badge";
|
|
|
|
export function LocalAiGroupHeading({
|
|
eyebrow,
|
|
title,
|
|
description,
|
|
}: {
|
|
eyebrow: string;
|
|
title: string;
|
|
description: string;
|
|
}) {
|
|
return (
|
|
<div className="rounded-lg border bg-muted/20 px-3 py-2.5">
|
|
<div className="font-semibold text-[10px] text-muted-foreground uppercase">
|
|
{eyebrow}
|
|
</div>
|
|
<div className="mt-1 flex flex-col gap-1 md:flex-row md:items-end md:justify-between">
|
|
<h3 className="font-semibold text-sm text-foreground">{title}</h3>
|
|
<p className="max-w-3xl text-muted-foreground text-xs">{description}</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export function LocalAiSectionSummary({
|
|
items,
|
|
}: {
|
|
items: Array<{
|
|
label: string;
|
|
tone?: SettingStatusTone;
|
|
}>;
|
|
}) {
|
|
return (
|
|
<div className="flex flex-wrap gap-1.5 pt-1">
|
|
{items.map((item) => (
|
|
<SettingStatusBadge key={item.label} tone={item.tone || "neutral"}>
|
|
{item.label}
|
|
</SettingStatusBadge>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export function LocalAiSectionToggleButton({
|
|
expanded,
|
|
onClick,
|
|
}: {
|
|
expanded: boolean;
|
|
onClick: () => void;
|
|
}) {
|
|
return (
|
|
<Button
|
|
aria-expanded={expanded}
|
|
className="h-7 shrink-0 gap-1 rounded-md px-2 text-[11px]"
|
|
onClick={onClick}
|
|
size="sm"
|
|
type="button"
|
|
variant="outline"
|
|
>
|
|
{expanded ? (
|
|
<ArrowUp className="h-3 w-3" />
|
|
) : (
|
|
<ArrowDown className="h-3 w-3" />
|
|
)}
|
|
{expanded ? "收起配置" : "展开配置"}
|
|
</Button>
|
|
);
|
|
}
|
|
|
|
export function LocalAiSectionShell({
|
|
id,
|
|
title,
|
|
description,
|
|
summary,
|
|
expanded,
|
|
onToggle,
|
|
children,
|
|
}: {
|
|
id: string;
|
|
title: ReactNode;
|
|
description: ReactNode;
|
|
summary?: ReactNode;
|
|
expanded: boolean;
|
|
onToggle: () => void;
|
|
children: ReactNode;
|
|
}) {
|
|
return (
|
|
<Card id={id}>
|
|
<CardHeader className="flex flex-col gap-3 space-y-0 md:flex-row md:items-start md:justify-between">
|
|
<div className="space-y-1">
|
|
<CardTitle className="text-base">{title}</CardTitle>
|
|
<CardDescription>{description}</CardDescription>
|
|
{summary}
|
|
</div>
|
|
<LocalAiSectionToggleButton expanded={expanded} onClick={onToggle} />
|
|
</CardHeader>
|
|
<CardContent
|
|
className={cn(
|
|
"local-ai-config-card-content space-y-4 pt-4",
|
|
!expanded && "hidden"
|
|
)}
|
|
>
|
|
{children}
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|