Advertisement

← vibesdk

src/components/config-card.tsx

8.4 KB Download file
import { Settings, Play, RotateCcw, Zap, Brain, Code2 } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import { Card, CardContent, CardHeader } from '@/components/ui/card';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
import { getModelDisplayName, getProviderInfo } from '@/utils/model-helpers';
import type { ModelConfig, UserModelConfigWithMetadata, AgentDisplayConfig } from '@/api-types';

interface ConfigCardProps {
  agent: AgentDisplayConfig;
  userConfig?: UserModelConfigWithMetadata;
  defaultConfig?: ModelConfig;
  onConfigure: () => void;
  onTest: () => void;
  onReset: () => void;
  isTesting: boolean;
}

// Helper function to get agent icon based on type
const getAgentIcon = (agentKey: string) => {
  if (agentKey.includes('Code') || agentKey.includes('phase') || agentKey.includes('file')) return Code2;
  if (agentKey.includes('fast') || agentKey.includes('template')) return Zap;
  if (agentKey.includes('blueprint') || agentKey.includes('suggestion') || agentKey.includes('review')) return Brain;
  return Settings;
};

// Helper function to format parameter values for display
const formatParameterValue = (value: unknown, type: string): string | null => {
  if (value === null || value === undefined) return null;

  switch (type) {
    case 'temperature':
      return `T: ${value}`;
    case 'maxTokens':
      return typeof value === 'number' ? `${Math.round(value / 1000)}K tokens` : String(value);
    case 'reasoningEffort':
      return typeof value === 'string' ? `${value.charAt(0).toUpperCase()}${value.slice(1)}` : String(value);
    default:
      return String(value);
  }
};

export function ConfigCard({
  agent,
  userConfig,
  defaultConfig,
  onConfigure,
  onTest,
  onReset,
  isTesting
}: ConfigCardProps) {
  const isCustomized = userConfig?.isUserOverride || false;
  const currentModel = userConfig?.name || defaultConfig?.name;
  const modelDisplayName = getModelDisplayName(currentModel);
  const providerInfo = getProviderInfo(currentModel);
  const AgentIcon = getAgentIcon(agent.key);

  // Get current parameter values
  const temperature = userConfig?.temperature ?? defaultConfig?.temperature;
  const maxTokens = userConfig?.max_tokens ?? defaultConfig?.max_tokens;
  const reasoningEffort = userConfig?.reasoning_effort ?? defaultConfig?.reasoning_effort;

  return (
    <Card className={`h-full min-h-[280px] min-w-[280px] flex flex-col overflow-hidden transition-all dark:!bg-kumo-base !bg-kumo-base hover:shadow-md !border-bg-1/40`}>
      <CardHeader className="pb- flex-shrink-0">
        <div className="flex items-start justify-between gap-2">
          <div className="flex items-start gap-2 min-w-0 flex-1 overflow-hidden">
            <div className="p-1.5 rounded-md bg-kumo-base shrink-0">
              <AgentIcon className="h-4 w-4" />
            </div>
            <div className="min-w-0 flex-1 overflow-hidden">
              <h5 className="font-medium text-md leading-tight mb-1 break-words" title={agent.name}>
                {agent.name}
              </h5>
              <p className="text-xs text-text-tertiary line-clamp-3 leading-tight overflow-hidden break-words" title={agent.description}>
                {agent.description}
              </p>
            </div>
          </div>

          <div className="shrink-0">
            <Badge
              variant={isCustomized ? "default" : "outline"}
              className="text-xs px-1.5 py-0.5 whitespace-nowrap dark:!bg-bg-1"
            >
              {isCustomized ? "Custom" : "Default"}
            </Badge>
          </div>
        </div>
      </CardHeader>

      <CardContent className="pt-0 flex-1 flex flex-col justify-between overflow-hidden">
        <div className="space-y-3 overflow-hidden">
          {/* Current Model */}
          <div className="space-y-2 pl-7 mt-2">
            <div className="flex items-start justify-between gap-2 min-w-0">
              <span className="text-sm font-medium flex-1 min-w-0 break-words leading-tight" title={modelDisplayName}>
                {modelDisplayName}
              </span>
              <Badge
                variant="secondary"
                className={`text-xs shrink-0 px-1.5 py-0.5 mt-0.5 dark:contrast-50 ${providerInfo.color}`}
              >
                {providerInfo.name}
              </Badge>
            </div>

            {/* Parameter Summary - Contained within card bounds */}
            <div className="flex flex-wrap gap-1 overflow-hidden">
              {temperature !== null && temperature !== undefined ? (
                <TooltipProvider>
                  <Tooltip>
                    <TooltipTrigger asChild>
                      <Badge variant="outline" className="text-xs px-1.5 py-0.5 shrink-0 dark:bg-brand/20">
                        {formatParameterValue(temperature, 'temperature')}
                      </Badge>
                    </TooltipTrigger>
                    <TooltipContent>
                      <p>Temperature: {temperature}</p>
                    </TooltipContent>
                  </Tooltip>
                </TooltipProvider>
              ) : null}

              {maxTokens ? (
                <TooltipProvider>
                  <Tooltip>
                    <TooltipTrigger asChild>
                      <Badge variant="outline" className="text-xs px-1.5 py-0.5 shrink-0 dark:bg-brand/20">
                        {formatParameterValue(maxTokens, 'maxTokens')}
                      </Badge>
                    </TooltipTrigger>
                    <TooltipContent>
                      <p>Max Tokens: {maxTokens?.toLocaleString()}</p>
                    </TooltipContent>
                  </Tooltip>
                </TooltipProvider>
              ) : null}

              {reasoningEffort ? (
                <TooltipProvider>
                  <Tooltip>
                    <TooltipTrigger asChild>
                      <Badge variant="outline" className="text-xs px-1.5 py-0.5 shrink-0 dark:bg-brand/20">
                        {formatParameterValue(reasoningEffort, 'reasoningEffort')}
                      </Badge>
                    </TooltipTrigger>
                    <TooltipContent>
                      <p>Reasoning Effort: {reasoningEffort}</p>
                    </TooltipContent>
                  </Tooltip>
                </TooltipProvider>
              ) : null}
            </div>
          </div>
        </div>

        {/* Action Buttons - Fixed at bottom with proper containment */}
        <div className="flex gap-2 mt-3 shrink-0">
          <Button
            size="sm"
            variant="outline"
            onClick={onConfigure}
            className="flex-1 h-8 text-xs font-medium min-w-0 dark:bg-kumo-elevated"
          >
            <Settings className="h-3 w-3 mr-1" />
            <span className="truncate">Configure</span>
          </Button>

          <TooltipProvider>
            <Tooltip>
              <TooltipTrigger asChild>
                <Button
                  size="sm"
                  variant="ghost"
                  onClick={onTest}
                  disabled={isTesting}
                  className="h-8 w-8 p-0 shrink-0"
                >
                  {isTesting ? (
                    <Settings className="h-3 w-3 animate-spin" />
                  ) : (
                    <Play className="h-3 w-3" />
                  )}
                </Button>
              </TooltipTrigger>
              <TooltipContent>
                <p>{isTesting ? 'Testing...' : 'Test Config'}</p>
              </TooltipContent>
            </Tooltip>
          </TooltipProvider>

          {isCustomized && (
            <TooltipProvider>
              <Tooltip>
                <TooltipTrigger asChild>
                  <Button
                    size="sm"
                    variant="ghost"
                    onClick={onReset}
                    className="h-8 w-8 p-0 text-text-tertiary shrink-0"
                  >
                    <RotateCcw className="h-3 w-3" />
                  </Button>
                </TooltipTrigger>
                <TooltipContent>
                  <p>Reset to Default</p>
                </TooltipContent>
              </Tooltip>
            </TooltipProvider>
          )}
        </div>
      </CardContent>
    </Card>
  );
}