agent-ecosystem/src/renderer/components/team/members/LeadModelRow.tsx

151 lines
5.6 KiB
TypeScript

import React, { useState } from 'react';
import { Checkbox } from '@renderer/components/ui/checkbox';
import { Label } from '@renderer/components/ui/label';
import { EffortLevelSelector } from '@renderer/components/team/dialogs/EffortLevelSelector';
import { LimitContextCheckbox } from '@renderer/components/team/dialogs/LimitContextCheckbox';
import {
getTeamModelLabel,
TeamModelSelector,
} from '@renderer/components/team/dialogs/TeamModelSelector';
import { getTeamColorSet } from '@renderer/constants/teamColors';
import { useTheme } from '@renderer/hooks/useTheme';
import { getMemberColorByName } from '@shared/constants/memberColors';
import { ChevronDown, ChevronRight, Info } from 'lucide-react';
import { Button } from '../../ui/button';
import type { EffortLevel, TeamProviderId } from '@shared/types';
interface LeadModelRowProps {
providerId: TeamProviderId;
model: string;
effort?: EffortLevel;
limitContext: boolean;
onProviderChange: (providerId: TeamProviderId) => void;
onModelChange: (model: string) => void;
onEffortChange: (effort: string) => void;
onLimitContextChange: (value: boolean) => void;
syncModelsWithTeammates: boolean;
onSyncModelsWithTeammatesChange: (value: boolean) => void;
warningText?: string | null;
disableGeminiOption?: boolean;
}
export const LeadModelRow = ({
providerId,
model,
effort,
limitContext,
onProviderChange,
onModelChange,
onEffortChange,
onLimitContextChange,
syncModelsWithTeammates,
onSyncModelsWithTeammatesChange,
warningText,
disableGeminiOption = false,
}: LeadModelRowProps): React.JSX.Element => {
const { isLight } = useTheme();
const [modelExpanded, setModelExpanded] = useState(false);
const leadColorSet = getTeamColorSet(getMemberColorByName('lead'));
const modelButtonLabel = model.trim() ? getTeamModelLabel(model.trim()) : 'Default';
return (
<div
className="relative grid grid-cols-1 gap-2 rounded-md p-2 shadow-sm md:grid-cols-[auto_1fr_auto]"
style={{
backgroundColor: isLight
? 'color-mix(in srgb, var(--color-surface-raised) 22%, white 78%)'
: 'var(--color-surface-raised)',
boxShadow: isLight ? '0 1px 2px rgba(15, 23, 42, 0.06)' : '0 1px 2px rgba(0, 0, 0, 0.28)',
}}
>
<div
className="absolute inset-y-0 left-0 w-1 rounded-l-md"
style={{ backgroundColor: leadColorSet.border }}
aria-hidden="true"
/>
<div className="min-w-0">
<div className="flex h-8 items-center gap-3 px-2">
<span className="text-sm font-medium text-[var(--color-text)]">lead</span>
<span className="shrink-0 text-xs text-[var(--color-text-secondary)]">Team Lead</span>
</div>
</div>
<div className="min-w-0">
<div className="flex h-8 items-center justify-end px-2 text-xs text-[var(--color-text-secondary)]">
<div className="flex min-w-0 items-center gap-2">
<Checkbox
id="sync-models-with-lead"
checked={syncModelsWithTeammates}
onCheckedChange={(checked) => onSyncModelsWithTeammatesChange(checked === true)}
/>
<Label
htmlFor="sync-models-with-lead"
className="cursor-pointer truncate text-xs font-normal text-text-secondary"
>
Sync model with teammates
</Label>
</div>
</div>
</div>
<div className="space-y-1">
<div className="min-w-0 space-y-1">
<Button
variant="outline"
size="sm"
className="h-8 max-w-[220px] shrink-0 justify-start gap-1 overflow-hidden text-left"
onClick={() => setModelExpanded((prev) => !prev)}
>
{modelExpanded ? (
<ChevronDown className="size-3.5" />
) : (
<ChevronRight className="size-3.5" />
)}
<span className="truncate">Model: {modelButtonLabel}</span>
</Button>
</div>
</div>
{warningText ? (
<div className="md:col-span-3">
<div className="bg-amber-500/8 ml-3 flex items-start gap-2 rounded-md border border-amber-500/25 px-3 py-2 text-[11px] leading-relaxed text-amber-200">
<Info className="mt-0.5 size-3.5 shrink-0 text-amber-300" />
<p>{warningText}</p>
</div>
</div>
) : null}
{modelExpanded ? (
<div className="space-y-2 md:col-span-3">
<TeamModelSelector
providerId={providerId}
onProviderChange={onProviderChange}
value={model}
onValueChange={onModelChange}
id="lead-model"
disableGeminiOption={disableGeminiOption}
/>
<EffortLevelSelector
value={effort ?? ''}
onValueChange={onEffortChange}
id="lead-effort"
/>
{providerId === 'anthropic' ? (
<LimitContextCheckbox
id="lead-limit-context"
checked={limitContext}
onCheckedChange={onLimitContextChange}
disabled={model === 'haiku'}
/>
) : null}
<div className="flex items-start gap-2 rounded-md border border-sky-500/20 bg-sky-500/5 px-3 py-2">
<Info className="mt-0.5 size-3.5 shrink-0 text-sky-400" />
<p className="text-[11px] leading-relaxed text-sky-300">
These settings control the team lead and act as the default runtime for teammates that
do not have their own override.
</p>
</div>
</div>
) : null}
</div>
);
};