import type { FieldPathList, FieldProps } from "@rjsf/utils"; import yaml from "js-yaml"; import { Textarea } from "@/components/ui/textarea"; import { cn } from "@/lib/utils"; import { useCallback, useEffect, useMemo, useState } from "react"; function formatYaml(value: unknown): string { if ( value == null || (typeof value === "object" && !Array.isArray(value) && Object.keys(value as Record).length === 0) ) { return ""; } try { return yaml.dump(value, { indent: 2, lineWidth: -1 }).trimEnd(); } catch { return ""; } } function parseYaml(text: string): { value: Record | undefined; error: string | undefined; } { const trimmed = text.trim(); if (trimmed === "") { return { value: {}, error: undefined }; } try { const parsed = yaml.load(trimmed); if ( typeof parsed !== "object" || parsed === null || Array.isArray(parsed) ) { return { value: undefined, error: "Must be a YAML mapping" }; } return { value: parsed as Record, error: undefined }; } catch (e) { const msg = e instanceof yaml.YAMLException ? e.reason : "Invalid YAML"; return { value: undefined, error: msg }; } } export function DictAsYamlField(props: FieldProps) { const { formData, onChange, readonly, disabled, idSchema, schema } = props; const emptyPath = useMemo(() => [] as FieldPathList, []); const fieldPath = (props as { fieldPathId?: { path?: FieldPathList } }).fieldPathId?.path ?? emptyPath; const [text, setText] = useState(() => formatYaml(formData)); const [error, setError] = useState(); useEffect(() => { setText(formatYaml(formData)); setError(undefined); }, [formData]); const handleChange = useCallback( (e: React.ChangeEvent) => { const raw = e.target.value; setText(raw); const { value, error: parseError } = parseYaml(raw); setError(parseError); if (value !== undefined) { onChange(value, fieldPath); } }, [onChange, fieldPath], ); const handleBlur = useCallback( (_e: React.FocusEvent) => { // Reformat on blur if valid const { value } = parseYaml(text); if (value !== undefined) { setText(formatYaml(value)); } }, [text], ); const id = idSchema?.$id ?? props.name; return (
{schema.title && ( )}