import { useCallback, useEffect } from "react"; import type { ModelPreset } from "@fusion/core"; import type { ModelInfo } from "../api"; import { applyPresetToSelection } from "../utils/modelPresets"; import { CustomModelDropdown } from "./CustomModelDropdown"; import { Brain, X } from "lucide-react"; interface ModelSelectionModalProps { isOpen: boolean; onClose: () => void; models: ModelInfo[]; executorValue: string; validatorValue: string; onExecutorChange: (value: string) => void; onValidatorChange: (value: string) => void; modelsLoading: boolean; modelsError: string | null; onRetry: () => void; favoriteProviders?: string[]; onToggleFavorite?: (provider: string) => void; favoriteModels?: string[]; onToggleModelFavorite?: (modelId: string) => void; /** Available model presets for quick selection. When provided, a preset selector is shown. */ presets?: ModelPreset[]; /** Currently selected preset ID, or undefined if no preset is active. */ selectedPresetId?: string; /** Called when the user selects a preset or reverts to default/custom mode. */ onPresetChange?: (presetId: string | undefined) => void; } function getModelBadgeLabel(models: ModelInfo[], value: string): string { if (!value) return "Using default"; const slashIdx = value.indexOf("/"); if (slashIdx === -1) return value; const provider = value.slice(0, slashIdx); const modelId = value.slice(slashIdx + 1); const matched = models.find((m) => m.provider === provider && m.id === modelId); return matched ? `${matched.provider}/${matched.id}` : `${provider}/${modelId}`; } export function ModelSelectionModal({ isOpen, onClose, models, executorValue, validatorValue, onExecutorChange, onValidatorChange, modelsLoading, modelsError, onRetry, favoriteProviders = [], onToggleFavorite, favoriteModels = [], onToggleModelFavorite, presets, selectedPresetId, onPresetChange, }: ModelSelectionModalProps) { // Handle Escape key useEffect(() => { if (!isOpen) return; const handleKeyDown = (event: KeyboardEvent) => { if (event.key === "Escape") { event.preventDefault(); onClose(); } }; document.addEventListener("keydown", handleKeyDown); return () => document.removeEventListener("keydown", handleKeyDown); }, [isOpen, onClose]); // Handle overlay click const handleOverlayClick = useCallback( (event: React.MouseEvent) => { if (event.target === event.currentTarget) { onClose(); } }, [onClose], ); const showPresets = !!(presets && presets.length > 0 && onPresetChange); const selectedPreset = presets?.find((p) => p.id === selectedPresetId); const handlePresetSelect = useCallback( (value: string) => { if (!onPresetChange) return; if (value === "default") { onPresetChange(undefined); onExecutorChange(""); onValidatorChange(""); return; } if (value === "custom") { onPresetChange(undefined); return; } const preset = presets?.find((p) => p.id === value); if (preset) { const selection = applyPresetToSelection(preset); onExecutorChange(selection.executorValue); onValidatorChange(selection.validatorValue); onPresetChange(preset.id); } }, [onPresetChange, presets, onExecutorChange, onValidatorChange], ); const handleExecutorChange = useCallback( (value: string) => { // Manual model selection clears preset mode if (onPresetChange && selectedPresetId) { onPresetChange(undefined); } onExecutorChange(value); }, [onPresetChange, selectedPresetId, onExecutorChange], ); const handleValidatorChange = useCallback( (value: string) => { // Manual model selection clears preset mode if (onPresetChange && selectedPresetId) { onPresetChange(undefined); } onValidatorChange(value); }, [onPresetChange, selectedPresetId, onValidatorChange], ); if (!isOpen) return null; const hasExecutorOverride = Boolean(executorValue); const hasValidatorOverride = Boolean(validatorValue); return (
Loading models…
Choose models for this task. If not selected, default models will be used.