'use client'; import React, { useState, useEffect } from 'react'; import { useLanguage } from '@/contexts/LanguageContext'; // Define the interfaces for our model configuration interface Model { id: string; name: string; } interface Provider { id: string; name: string; models: Model[]; supportsCustomModel?: boolean; } interface ModelConfig { providers: Provider[]; defaultProvider: string; } interface ModelSelectorProps { provider: string; setProvider: (value: string) => void; model: string; setModel: (value: string) => void; isCustomModel: boolean; setIsCustomModel: (value: boolean) => void; customModel: string; setCustomModel: (value: string) => void; // File filter configuration showFileFilters?: boolean; excludedDirs?: string; setExcludedDirs?: (value: string) => void; excludedFiles?: string; setExcludedFiles?: (value: string) => void; includedDirs?: string; setIncludedDirs?: (value: string) => void; includedFiles?: string; setIncludedFiles?: (value: string) => void; } export default function UserSelector({ provider, setProvider, model, setModel, isCustomModel, setIsCustomModel, customModel, setCustomModel, // File filter configuration showFileFilters = false, excludedDirs = '', setExcludedDirs, excludedFiles = '', setExcludedFiles, includedDirs = '', setIncludedDirs, includedFiles = '', setIncludedFiles }: ModelSelectorProps) { // State to manage the visibility of the filters modal and filter section const [isFilterSectionOpen, setIsFilterSectionOpen] = useState(false); // State to manage filter mode: 'exclude' or 'include' const [filterMode, setFilterMode] = useState<'exclude' | 'include'>('exclude'); const { messages: t } = useLanguage(); // State for model configurations from backend const [modelConfig, setModelConfig] = useState(null); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); // State for viewing default values const [showDefaultDirs, setShowDefaultDirs] = useState(false); const [showDefaultFiles, setShowDefaultFiles] = useState(false); // Fetch model configurations from the backend useEffect(() => { const fetchModelConfig = async () => { try { setIsLoading(true); setError(null); const response = await fetch('/api/models/config'); if (!response.ok) { throw new Error(`Error fetching model configurations: ${response.status}`); } const data = await response.json(); setModelConfig(data); // Initialize provider and model with defaults from API if not already set if (!provider && data.defaultProvider) { setProvider(data.defaultProvider); // Find the default provider and set its default model const selectedProvider = data.providers.find((p: Provider) => p.id === data.defaultProvider); if (selectedProvider && selectedProvider.models.length > 0) { setModel(selectedProvider.models[0].id); } } } catch (err) { console.error('Failed to fetch model configurations:', err); setError('Failed to load model configurations. Using default options.'); } finally { setIsLoading(false); } }; fetchModelConfig(); }, [provider, setModel, setProvider]); // Handler for changing provider const handleProviderChange = (newProvider: string) => { setProvider(newProvider); setTimeout(() => { // Reset custom model state when changing providers setIsCustomModel(false); // Set default model for the selected provider if (modelConfig) { const selectedProvider = modelConfig.providers.find((p: Provider) => p.id === newProvider); if (selectedProvider && selectedProvider.models.length > 0) { setModel(selectedProvider.models[0].id); } } }, 10); }; // Default excluded directories from config.py const defaultExcludedDirs = `./.venv/ ./venv/ ./env/ ./virtualenv/ ./node_modules/ ./bower_components/ ./jspm_packages/ ./.git/ ./.svn/ ./.hg/ ./.bzr/ ./__pycache__/ ./.pytest_cache/ ./.mypy_cache/ ./.ruff_cache/ ./.coverage/ ./dist/ ./build/ ./out/ ./target/ ./bin/ ./obj/ ./docs/ ./_docs/ ./site-docs/ ./_site/ ./.idea/ ./.vscode/ ./.vs/ ./.eclipse/ ./.settings/ ./logs/ ./log/ ./tmp/ ./temp/ ./.eng`; // Default excluded files from config.py const defaultExcludedFiles = `package-lock.json yarn.lock pnpm-lock.yaml npm-shrinkwrap.json poetry.lock Pipfile.lock requirements.txt.lock Cargo.lock composer.lock .lock .DS_Store Thumbs.db desktop.ini *.lnk .env .env.* *.env *.cfg *.ini .flaskenv .gitignore .gitattributes .gitmodules .github .gitlab-ci.yml .prettierrc .eslintrc .eslintignore .stylelintrc .editorconfig .jshintrc .pylintrc .flake8 mypy.ini pyproject.toml tsconfig.json webpack.config.js babel.config.js rollup.config.js jest.config.js karma.conf.js vite.config.js next.config.js *.min.js *.min.css *.bundle.js *.bundle.css *.map *.gz *.zip *.tar *.tgz *.rar *.pyc *.pyo *.pyd *.so *.dll *.class *.exe *.o *.a *.jpg *.jpeg *.png *.gif *.ico *.svg *.webp *.mp3 *.mp4 *.wav *.avi *.mov *.webm *.csv *.tsv *.xls *.xlsx *.db *.sqlite *.sqlite3 *.pdf *.docx *.pptx`; // Display loading state if (isLoading) { return (
Loading model configurations...
); } return (
{error && (
{error}
)} {/* Provider Selection */}
{/* Model Selection - consistent height regardless of type */}
{isCustomModel ? ( { setCustomModel(e.target.value); setModel(e.target.value); }} placeholder={t.form?.customModelPlaceholder || 'Enter custom model name'} className="input-japanese block w-full px-2.5 py-1.5 text-sm rounded-md bg-transparent text-[var(--foreground)] focus:outline-none focus:border-[var(--accent-primary)]" /> ) : ( )}
{/* Custom model toggle - only when provider supports it */} {modelConfig?.providers.find((p: Provider) => p.id === provider)?.supportsCustomModel && (
{ const newValue = !isCustomModel; setIsCustomModel(newValue); if (newValue) { setCustomModel(model); } }} > {}} className="sr-only" />
)} {showFileFilters && (
{isFilterSectionOpen && (
{/* Filter Mode Selection */}

{filterMode === 'exclude' ? (t.form?.excludeModeDescription || 'Specify paths to exclude from processing (default behavior)') : (t.form?.includeModeDescription || 'Specify only the paths to include, ignoring all others') }

{/* Directories Section */}