// ======================================== // SchemaFormRenderer Component // ======================================== // Renders structured form groups from EnvVarGroupsSchema definition // Supports select, number, checkbox, text, and model-select field types import { useMemo } from 'react'; import { useIntl } from 'react-intl'; import { Box, ArrowUpDown, Cpu, GitBranch, Scissors, type LucideIcon, } from 'lucide-react'; import { Label } from '@/components/ui/Label'; import { Input } from '@/components/ui/Input'; import { Checkbox } from '@/components/ui/Checkbox'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/Select'; import { Collapsible, CollapsibleContent, CollapsibleTrigger, } from '@/components/ui/Collapsible'; import { cn } from '@/lib/utils'; import { evaluateShowWhen } from './envVarSchema'; import { ModelSelectField } from './ModelSelectField'; import type { EnvVarGroupsSchema, EnvVarFieldSchema } from '@/types/codexlens'; import type { CodexLensModel } from '@/lib/api'; // Icon mapping for group icons const iconMap: Record = { box: Box, 'arrow-up-down': ArrowUpDown, cpu: Cpu, 'git-branch': GitBranch, scissors: Scissors, }; interface SchemaFormRendererProps { /** The schema defining all groups and fields */ groups: EnvVarGroupsSchema; /** Current form values keyed by env var name */ values: Record; /** Called when a field value changes */ onChange: (key: string, value: string) => void; /** Whether the form is disabled (loading state) */ disabled?: boolean; /** Local embedding models (installed) for model-select */ localEmbeddingModels?: CodexLensModel[]; /** Local reranker models (installed) for model-select */ localRerankerModels?: CodexLensModel[]; } export function SchemaFormRenderer({ groups, values, onChange, disabled = false, localEmbeddingModels = [], localRerankerModels = [], }: SchemaFormRendererProps) { const { formatMessage } = useIntl(); const groupEntries = useMemo(() => Object.entries(groups), [groups]); return (
{groupEntries.map(([groupKey, group]) => { const IconComponent = iconMap[group.icon] || Box; return (
{formatMessage({ id: group.labelKey, defaultMessage: groupKey })}
{Object.entries(group.vars).map(([varKey, field]) => { const visible = evaluateShowWhen(field, values); if (!visible) return null; return ( onChange(varKey, val)} allValues={values} disabled={disabled} localModels={ varKey.includes('EMBEDDING') ? localEmbeddingModels : localRerankerModels } formatMessage={formatMessage} /> ); })}
); })}
); } // ======================================== // Individual Field Renderer // ======================================== interface FieldRendererProps { field: EnvVarFieldSchema; value: string; onChange: (value: string) => void; allValues: Record; disabled: boolean; localModels: CodexLensModel[]; formatMessage: (descriptor: { id: string; defaultMessage?: string }) => string; } function FieldRenderer({ field, value, onChange, allValues, disabled, localModels, formatMessage, }: FieldRendererProps) { const label = formatMessage({ id: field.labelKey, defaultMessage: field.key }); switch (field.type) { case 'select': return (
); case 'number': return (
onChange(e.target.value)} placeholder={field.placeholder} min={field.min} max={field.max} step={field.step ?? 1} disabled={disabled} />
); case 'checkbox': return (
onChange(checked ? 'true' : 'false')} disabled={disabled} />
); case 'model-select': { // Determine backend type from related backend env var const isEmbedding = field.key.includes('EMBEDDING'); const backendKey = isEmbedding ? 'CODEXLENS_EMBEDDING_BACKEND' : 'CODEXLENS_RERANKER_BACKEND'; const backendType = allValues[backendKey] === 'api' ? 'api' : 'local'; return (
); } case 'text': default: return (
onChange(e.target.value)} placeholder={field.placeholder} disabled={disabled} />
); } } export default SchemaFormRenderer;