"use client"; import { Input } from "@kandev/ui/input"; import { Label } from "@kandev/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@kandev/ui/select"; import { Switch } from "@kandev/ui/switch"; import { useUtilityAgents } from "@/hooks/domains/settings/use-utility-agents"; import type { PluginConfigField } from "@/lib/plugins/config-schema"; /** Sentinel for the "Not set" select item — an actual enum option can never * collide with it, and Radix rejects value="" items. */ const ENUM_UNSET_SENTINEL = "__kandev_enum_unset__"; const UTILITY_AGENT_UNSET_SENTINEL = "__kandev_utility_agent_unset__"; type PluginConfigFormProps = { fields: PluginConfigField[]; values: Record; initialValues: Record; disabled: boolean; onChange: (name: string, value: string | boolean) => void; }; /** * Schema-driven settings form: one control per config_schema property. * Secret fields render as password inputs pre-filled with the backend's * mask; editing replaces the value, leaving it untouched keeps the stored * secret. Purely controlled — load/save/dirty state lives in PluginDetail. */ export function PluginConfigForm({ fields, values, initialValues, disabled, onChange, }: PluginConfigFormProps) { return (
{fields.map((field) => ( ))}
); } type ConfigFieldRowProps = { field: PluginConfigField; value: string | boolean; isDirty: boolean; disabled: boolean; onChange: (name: string, value: string | boolean) => void; }; function ConfigFieldRow({ field, value, isDirty, disabled, onChange }: ConfigFieldRowProps) { const inputId = `plugin-config-${field.name}`; return (
{field.description &&

{field.description}

}
); } type ConfigFieldControlProps = ConfigFieldRowProps & { inputId: string }; function ConfigFieldControl({ field, inputId, value, isDirty, disabled, onChange, }: ConfigFieldControlProps) { if (field.type === "boolean") { return (
onChange(field.name, checked)} />
); } if (field.type === "enum") { // Radix Select forbids an item with value="", so an explicit "Not set" // sentinel lets optional enums be cleared back to unset (serialization // omits empty strings). return ( ); } if (field.type === "utility_agent") { return ( ); } return ( onChange(field.name, event.target.value)} /> ); } function UtilityAgentSelect({ field, inputId, value, isDirty, disabled, onChange, }: ConfigFieldControlProps) { const { agents, loading, error } = useUtilityAgents(); const selectedID = typeof value === "string" ? value : ""; const selectedAgent = agents.find((agent) => agent.id === selectedID); const selectedLabel = selectedAgent?.name ?? selectedUtilityAgentFallback(selectedID, loading); const placeholder = utilityAgentPlaceholder(loading, error); return ( ); } function selectedUtilityAgentFallback(selectedID: string, loading: boolean): string | undefined { if (selectedID === "") return undefined; return loading ? "Loading selected utility agent..." : "Selected utility agent unavailable"; } function utilityAgentPlaceholder(loading: boolean, error: Error | null): string { if (loading) return "Loading utility agents..."; if (error) return "Utility agents unavailable"; return "Select a utility agent..."; } function inputType(field: PluginConfigField): string { if (field.secret) return "password"; if (field.type === "number" || field.type === "integer") return "number"; return "text"; }