"use client"; import { useMemo, type ReactNode } from "react"; import * as Option from "effect/Option"; import * as Schema from "effect/Schema"; import type { ProviderSettingsFormAnnotation, ProviderSettingsFormControl, ProviderSettingsFormSchemaAnnotation, } from "@t3tools/contracts"; import { cn } from "../../lib/utils"; import { DraftInput } from "../ui/draft-input"; import { Input } from "../ui/input"; import { Switch } from "../ui/switch"; import { Textarea } from "../ui/textarea"; import type { ProviderClientDefinition } from "./providerDriverMeta"; export interface ProviderSettingsFieldModel { readonly key: string; readonly control: ProviderSettingsFormControl; readonly label: string; readonly description?: string | undefined; readonly placeholder?: string | undefined; readonly clearWhenEmpty: "omit" | "persist"; readonly defaultBooleanValue?: boolean | undefined; } function titleizeFieldKey(key: string): string { return key .replace(/([a-z0-9])([A-Z])/g, "$1 $2") .replace(/[-_]+/g, " ") .replace(/^./, (char) => char.toUpperCase()); } function readFieldAnnotations( fieldSchema: ProviderClientDefinition["settingsSchema"]["fields"][string], ) { return Schema.resolveAnnotationsKey(fieldSchema) ?? Schema.resolveAnnotations(fieldSchema); } function readFieldAnnotationString( fieldSchema: ProviderClientDefinition["settingsSchema"]["fields"][string], key: "title" | "description", ): string | undefined { const annotations = readFieldAnnotations(fieldSchema); const value = annotations?.[key]; return typeof value === "string" ? value : undefined; } function readProviderSettingsFormAnnotation( fieldSchema: ProviderClientDefinition["settingsSchema"]["fields"][string], ): ProviderSettingsFormAnnotation { const annotation = readFieldAnnotations(fieldSchema)?.providerSettingsForm; return annotation ?? {}; } function readProviderSettingsFormSchemaAnnotation( definition: ProviderClientDefinition, ): ProviderSettingsFormSchemaAnnotation { return Schema.resolveAnnotations(definition.settingsSchema)?.providerSettingsFormSchema ?? {}; } function readFieldBooleanDefault( fieldSchema: ProviderClientDefinition["settingsSchema"]["fields"][string], ): boolean | undefined { const decodeDefault = Schema.decodeUnknownOption(fieldSchema as Schema.Decoder); const decoded = decodeDefault(undefined); return Option.isSome(decoded) && typeof decoded.value === "boolean" ? decoded.value : undefined; } export function deriveProviderSettingsFields( definition: ProviderClientDefinition, ): ReadonlyArray { const schemaAnnotation = readProviderSettingsFormSchemaAnnotation(definition); const orderedKeys = new Map( (schemaAnnotation.order ?? []).map((key, index) => [key, index] as const), ); const orderFallbackOffset = orderedKeys.size; return Object.keys(definition.settingsSchema.fields) .map((key, index) => ({ key, index })) .toSorted((left, right) => { return ( (orderedKeys.get(left.key) ?? orderFallbackOffset + left.index) - (orderedKeys.get(right.key) ?? orderFallbackOffset + right.index) ); }) .flatMap(({ key }) => { const fieldSchema = definition.settingsSchema.fields[key]!; const formAnnotation = readProviderSettingsFormAnnotation(fieldSchema); if (formAnnotation.hidden) return []; const annotatedTitle = readFieldAnnotationString(fieldSchema, "title"); const annotatedDescription = readFieldAnnotationString(fieldSchema, "description"); return [ { key, control: formAnnotation.control ?? "text", label: annotatedTitle ?? titleizeFieldKey(key), ...(annotatedDescription !== undefined ? { description: annotatedDescription } : {}), ...(formAnnotation.placeholder !== undefined ? { placeholder: formAnnotation.placeholder } : {}), clearWhenEmpty: formAnnotation.clearWhenEmpty ?? "omit", ...(formAnnotation.control === "switch" ? { defaultBooleanValue: readFieldBooleanDefault(fieldSchema) } : {}), } satisfies ProviderSettingsFieldModel, ]; }); } export function readProviderConfigString(config: unknown, key: string): string { if (config === null || typeof config !== "object") return ""; const value = (config as Record)[key]; return typeof value === "string" ? value : ""; } export function readProviderConfigBoolean( config: unknown, key: string, defaultValue = false, ): boolean { if (config === null || typeof config !== "object") return defaultValue; const value = (config as Record)[key]; return typeof value === "boolean" ? value : defaultValue; } export function nextProviderConfigWithFieldValue( config: unknown, field: ProviderSettingsFieldModel, value: string | boolean, ): Record | undefined { const base: Record = config !== null && typeof config === "object" ? { ...(config as Record) } : {}; if (typeof value === "boolean") { const emptyBooleanValue = field.defaultBooleanValue ?? false; if (field.clearWhenEmpty === "omit" && value === emptyBooleanValue) { delete base[field.key]; } else { base[field.key] = value; } return Object.keys(base).length > 0 ? base : undefined; } const trimmed = value.trim(); if (field.clearWhenEmpty === "omit" && trimmed.length === 0) { delete base[field.key]; } else { base[field.key] = value; } return Object.keys(base).length > 0 ? base : undefined; } interface ProviderSettingsFormProps { readonly definition: ProviderClientDefinition; readonly value: unknown; readonly idPrefix: string; /** * `card` stacks label over control, `dialog` is the compact wizard layout, * `grid` emits a label cell and a control cell per field for a parent * two-column grid (label column left, control right), with the description * beside a fixed-width control so each field stays on one line. */ readonly variant: "card" | "dialog" | "grid"; readonly onChange: (nextConfig: Record | undefined) => void; } function FieldFrame(props: { readonly variant: ProviderSettingsFormProps["variant"]; readonly children: ReactNode; }) { if (props.variant === "card") { return
{props.children}
; } return
{props.children}
; } interface ProviderSettingsFieldRowProps { readonly field: ProviderSettingsFieldModel; readonly value: unknown; readonly idPrefix: string; readonly variant: ProviderSettingsFormProps["variant"]; readonly onChange: ProviderSettingsFormProps["onChange"]; } function ProviderSettingsFieldRow({ field, value, idPrefix, variant, onChange, }: ProviderSettingsFieldRowProps) { const inputId = `${idPrefix}-${field.key}`; const descriptionClassName = variant === "dialog" ? "text-[11px] text-muted-foreground" : "mt-1 block text-xs text-muted-foreground"; const label = {field.label}; const description = field.description ? ( {field.description} ) : null; if (variant === "grid") { // Label cell, then a control cell where the description sits beside a // fixed-width control and wraps under it when the pane is narrow. The // description is outside the label, so the control points at it instead. const descriptionId = field.description ? `${inputId}-description` : undefined; return ( <> {field.control === "switch" ? ( {field.label} ) : ( )}
{field.control === "switch" ? ( onChange(nextProviderConfigWithFieldValue(value, field, Boolean(checked))) } aria-label={field.label} aria-describedby={descriptionId} /> ) : field.control === "textarea" ? (