- FontSizePicker: 字体大小选择器,支持实时预览 - ModelCardSelector: 模型卡片选择组件(Haiku/Sonnet/Opus) - UserMenu: 用户菜单弹出组件,支持主题切换和设置导航
109 lines
2.9 KiB
TypeScript
109 lines
2.9 KiB
TypeScript
'use client';
|
|
|
|
import { cn } from '@/lib/utils';
|
|
|
|
// 模型卡片配置
|
|
const MODEL_CARDS = [
|
|
{
|
|
id: 'haiku',
|
|
name: 'Haiku',
|
|
description: 'Fast & efficient',
|
|
modelIdPattern: 'haiku',
|
|
},
|
|
{
|
|
id: 'sonnet',
|
|
name: 'Sonnet',
|
|
description: 'Balanced',
|
|
modelIdPattern: 'sonnet',
|
|
},
|
|
{
|
|
id: 'opus',
|
|
name: 'Opus',
|
|
description: 'Most capable',
|
|
modelIdPattern: 'opus',
|
|
},
|
|
];
|
|
|
|
interface ModelCardSelectorProps {
|
|
value: string;
|
|
onChange: (modelId: string) => void;
|
|
disabled?: boolean;
|
|
models?: { modelId: string; displayName: string }[];
|
|
}
|
|
|
|
export function ModelCardSelector({
|
|
value,
|
|
onChange,
|
|
disabled = false,
|
|
models = [],
|
|
}: ModelCardSelectorProps) {
|
|
// 根据当前选中的模型ID判断选中的卡片
|
|
const getSelectedCard = (modelId: string): string => {
|
|
const lowerModelId = modelId.toLowerCase();
|
|
if (lowerModelId.includes('haiku')) return 'haiku';
|
|
if (lowerModelId.includes('opus')) return 'opus';
|
|
return 'sonnet'; // 默认 sonnet
|
|
};
|
|
|
|
// 根据卡片类型找到对应的实际模型ID
|
|
const findModelIdByCard = (cardId: string): string => {
|
|
const model = models.find((m) =>
|
|
m.modelId.toLowerCase().includes(cardId)
|
|
);
|
|
return model?.modelId || value;
|
|
};
|
|
|
|
const selectedCard = getSelectedCard(value);
|
|
|
|
return (
|
|
<div className="flex gap-3">
|
|
{MODEL_CARDS.map((card) => {
|
|
const isSelected = selectedCard === card.id;
|
|
return (
|
|
<button
|
|
key={card.id}
|
|
onClick={() => {
|
|
if (!disabled) {
|
|
const newModelId = findModelIdByCard(card.id);
|
|
onChange(newModelId);
|
|
}
|
|
}}
|
|
disabled={disabled}
|
|
className={cn(
|
|
'flex flex-col items-center justify-center',
|
|
'w-[120px] h-[72px] rounded-md',
|
|
'border-2 transition-all duration-200',
|
|
'focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-[var(--color-primary)]',
|
|
isSelected
|
|
? 'bg-[var(--color-primary-light)] border-[var(--color-primary)]'
|
|
: 'bg-[var(--color-bg-tertiary)] border-transparent hover:border-[var(--color-border)]',
|
|
disabled && 'opacity-50 cursor-not-allowed'
|
|
)}
|
|
>
|
|
<span
|
|
className={cn(
|
|
'text-sm font-semibold',
|
|
isSelected
|
|
? 'text-[var(--color-primary)]'
|
|
: 'text-[var(--color-text-primary)]'
|
|
)}
|
|
>
|
|
{card.name}
|
|
</span>
|
|
<span
|
|
className={cn(
|
|
'text-xs mt-1',
|
|
isSelected
|
|
? 'text-[var(--color-primary)]'
|
|
: 'text-[var(--color-text-tertiary)]'
|
|
)}
|
|
>
|
|
{card.description}
|
|
</span>
|
|
</button>
|
|
);
|
|
})}
|
|
</div>
|
|
);
|
|
}
|