{
	"$schema": "https://ui.shadcn.com/schema/registry-item.json",
	"name": "json-schema-viewer",
	"type": "registry:ui",
	"title": "JSON Schema Viewer",
	"description": "A read-only component for displaying JSON Schema structures with support for union types, nested objects, arrays, and enums.",
	"files": [
		{
			"path": "./src/registry/ui/json-schema-viewer.tsx",
			"content": "\"use client\";\n\nimport type { JSONSchema7 } from \"ai\";\nimport type { ComponentProps } from \"react\";\nimport { useMemo } from \"react\";\nimport { Badge } from \"@/components/ui/badge\";\nimport { Separator } from \"@/components/ui/separator\";\nimport { cn } from \"@/lib/utils\";\n\ninterface SchemaProperty {\n\tname: string;\n\ttype: string;\n\trequired: boolean;\n\tdescription?: string;\n\tisArray: boolean;\n\tenumValues?: string[];\n\tnested?: SchemaProperty[];\n}\n\ninterface UnionBranch {\n\tproperties: SchemaProperty[];\n\tdescription?: string;\n\tbranchIndex: number;\n}\n\nexport interface JsonSchemaViewerProps extends ComponentProps<\"div\"> {\n\tschema: JSONSchema7;\n\ttitle?: string;\n}\n\nfunction parseProperty(\n\tprop: Record<string, unknown>,\n\tname: string,\n\trequired: boolean,\n): SchemaProperty {\n\t// Determine property type\n\tlet type: string;\n\tif (prop.enum) {\n\t\ttype = \"enum\";\n\t} else if (prop.type === \"object\") {\n\t\ttype = \"object\";\n\t} else if (Array.isArray(prop.type)) {\n\t\ttype = prop.type[0] || \"any\";\n\t} else {\n\t\ttype = (prop.type as string) || \"any\";\n\t}\n\n\tconst result: SchemaProperty = {\n\t\tname,\n\t\ttype,\n\t\trequired,\n\t\tisArray: false,\n\t};\n\n\t// Handle arrays\n\tif (prop.type === \"array\" && prop.items) {\n\t\tresult.isArray = true;\n\t\tconst items = prop.items;\n\n\t\tif (typeof items === \"object\" && !Array.isArray(items)) {\n\t\t\tconst itemProp = parseProperty(\n\t\t\t\titems as Record<string, unknown>,\n\t\t\t\t\"\",\n\t\t\t\tfalse,\n\t\t\t);\n\t\t\tresult.type = itemProp.type;\n\t\t\tresult.enumValues = itemProp.enumValues;\n\t\t\tresult.nested = itemProp.nested;\n\t\t}\n\t}\n\n\t// Handle enums\n\tif (prop.enum && Array.isArray(prop.enum)) {\n\t\tresult.type = \"enum\";\n\t\tresult.enumValues = prop.enum.map(String);\n\t}\n\n\t// Handle objects\n\tif (prop.type === \"object\" && prop.properties) {\n\t\tresult.nested = [];\n\t\tconst requiredFields = Array.isArray(prop.required)\n\t\t\t? prop.required\n\t\t\t: [];\n\n\t\tfor (const [nestedName, nestedProp] of Object.entries(\n\t\t\tprop.properties,\n\t\t)) {\n\t\t\tconst isNestedRequired = requiredFields.includes(nestedName);\n\t\t\tresult.nested.push(\n\t\t\t\tparseProperty(nestedProp, nestedName, isNestedRequired),\n\t\t\t);\n\t\t}\n\t}\n\n\t// Extract description\n\tif (prop.description && typeof prop.description === \"string\") {\n\t\tresult.description = prop.description;\n\t}\n\n\treturn result;\n}\n\nfunction parseSchema(schema: JSONSchema7): {\n\tisUnion: boolean;\n\tproperties?: SchemaProperty[];\n\tunions?: UnionBranch[];\n} {\n\tif (!schema || typeof schema !== \"object\") {\n\t\treturn { isUnion: false, properties: [] };\n\t}\n\n\t// Handle union schemas (anyOf)\n\tif (schema.anyOf && Array.isArray(schema.anyOf)) {\n\t\tconst unions: UnionBranch[] = [];\n\n\t\tschema.anyOf.forEach((branch: unknown, index: number) => {\n\t\t\tif (\n\t\t\t\ttypeof branch === \"object\" &&\n\t\t\t\tbranch !== null &&\n\t\t\t\t!Array.isArray(branch)\n\t\t\t) {\n\t\t\t\tconst branchSchema = branch as JSONSchema7;\n\t\t\t\tconst properties: SchemaProperty[] = [];\n\n\t\t\t\tif (branchSchema.properties) {\n\t\t\t\t\tconst requiredFields = Array.isArray(branchSchema.required)\n\t\t\t\t\t\t? branchSchema.required\n\t\t\t\t\t\t: [];\n\n\t\t\t\t\tfor (const [name, prop] of Object.entries(\n\t\t\t\t\t\tbranchSchema.properties,\n\t\t\t\t\t)) {\n\t\t\t\t\t\tconst isRequired = requiredFields.includes(name);\n\t\t\t\t\t\tproperties.push(\n\t\t\t\t\t\t\tparseProperty(\n\t\t\t\t\t\t\t\tprop as Record<string, unknown>,\n\t\t\t\t\t\t\t\tname,\n\t\t\t\t\t\t\t\tisRequired,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tunions.push({\n\t\t\t\t\tproperties,\n\t\t\t\t\tdescription: branchSchema.description,\n\t\t\t\t\tbranchIndex: index,\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\n\t\treturn { isUnion: true, unions };\n\t}\n\n\t// Handle regular schemas\n\tconst properties: SchemaProperty[] = [];\n\tconst requiredFields = Array.isArray(schema.required)\n\t\t? schema.required\n\t\t: [];\n\n\tif (schema.properties) {\n\t\tfor (const [name, prop] of Object.entries(schema.properties)) {\n\t\t\tconst isRequired = requiredFields.includes(name);\n\t\t\tproperties.push(\n\t\t\t\tparseProperty(\n\t\t\t\t\tprop as Record<string, unknown>,\n\t\t\t\t\tname,\n\t\t\t\t\tisRequired,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t}\n\n\treturn { isUnion: false, properties };\n}\n\nfunction PropertyRow({\n\tproperty,\n\tdepth = 0,\n}: {\n\tproperty: SchemaProperty;\n\tdepth?: number;\n}) {\n\treturn (\n\t\t<div className=\"relative\">\n\t\t\t<div\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"flex items-start gap-3 py-2 px-3 text-sm rounded-sm hover:bg-muted/50 transition-colors\",\n\t\t\t\t\tdepth > 0 &&\n\t\t\t\t\t\t\"before:absolute before:top-0 before:bottom-0 before:w-px before:bg-border/40 before:left-(--indent-line-left)\",\n\t\t\t\t)}\n\t\t\t\tstyle={{\n\t\t\t\t\tpaddingLeft: `${12 + depth * 16}px`,\n\t\t\t\t\t// @ts-expect-error - CSS variables are not typed\n\t\t\t\t\t\"--indent-line-left\":\n\t\t\t\t\t\tdepth > 0 ? `${12 + (depth - 1) * 16}px` : undefined,\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t<div className=\"flex-1 min-w-0\">\n\t\t\t\t\t<div className=\"flex items-center gap-2 mb-1\">\n\t\t\t\t\t\t<span className=\"font-mono font-medium text-foreground\">\n\t\t\t\t\t\t\t{property.name}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t{property.required && (\n\t\t\t\t\t\t\t<span className=\"text-destructive text-xs font-bold\">\n\t\t\t\t\t\t\t\t*\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t<Badge variant=\"secondary\">\n\t\t\t\t\t\t\t{property.isArray\n\t\t\t\t\t\t\t\t? `${property.type}[]`\n\t\t\t\t\t\t\t\t: property.type}\n\t\t\t\t\t\t</Badge>\n\t\t\t\t\t</div>\n\t\t\t\t\t{property.type === \"enum\" && property.enumValues && (\n\t\t\t\t\t\t<div className=\"flex flex-wrap gap-1 mt-2\">\n\t\t\t\t\t\t\t{property.enumValues.map((value) => (\n\t\t\t\t\t\t\t\t<Badge\n\t\t\t\t\t\t\t\t\tkey={value}\n\t\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\t\tclassName=\"text-xs\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{value}\n\t\t\t\t\t\t\t\t</Badge>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t\t{property.description && (\n\t\t\t\t\t\t<div className=\"text-muted-foreground text-xs leading-relaxed\">\n\t\t\t\t\t\t\t{property.description}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t{property.nested && property.nested.length > 0 && (\n\t\t\t\t<div>\n\t\t\t\t\t{property.nested.map((nestedProp) => (\n\t\t\t\t\t\t<PropertyRow\n\t\t\t\t\t\t\tkey={nestedProp.name}\n\t\t\t\t\t\t\tproperty={nestedProp}\n\t\t\t\t\t\t\tdepth={depth + 1}\n\t\t\t\t\t\t/>\n\t\t\t\t\t))}\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\nfunction UnionBranchView({\n\tbranch,\n\tisLast,\n}: {\n\tbranch: UnionBranch;\n\tisLast: boolean;\n}) {\n\treturn (\n\t\t<div className=\"mb-4\">\n\t\t\t<div className=\"flex items-center gap-2 px-3 py-2 bg-muted/30 rounded-sm\">\n\t\t\t\t<Badge variant=\"outline\">Option {branch.branchIndex + 1}</Badge>\n\t\t\t\t{branch.description && (\n\t\t\t\t\t<span className=\"text-sm text-muted-foreground\">\n\t\t\t\t\t\t{branch.description}\n\t\t\t\t\t</span>\n\t\t\t\t)}\n\t\t\t</div>\n\n\t\t\t<div className=\"mt-1\">\n\t\t\t\t{branch.properties.length === 0 ? (\n\t\t\t\t\t<div className=\"p-4 text-sm text-muted-foreground text-center\">\n\t\t\t\t\t\tNo properties defined\n\t\t\t\t\t</div>\n\t\t\t\t) : (\n\t\t\t\t\tbranch.properties.map((property) => (\n\t\t\t\t\t\t<PropertyRow key={property.name} property={property} />\n\t\t\t\t\t))\n\t\t\t\t)}\n\t\t\t</div>\n\n\t\t\t{!isLast && <Separator className=\"my-3\" />}\n\t\t</div>\n\t);\n}\n\nexport interface JsonSchemaViewerProps extends ComponentProps<\"div\"> {\n\tschema: JSONSchema7;\n\ttitle?: string;\n\thideHeader?: boolean;\n}\n\nexport function JsonSchemaViewer({\n\tschema,\n\ttitle = \"Schema Structure\",\n\tclassName,\n\thideHeader = false,\n\t...props\n}: JsonSchemaViewerProps) {\n\tconst { isUnion, properties, unions } = useMemo(\n\t\t() => parseSchema(schema),\n\t\t[schema],\n\t);\n\n\treturn (\n\t\t<div\n\t\t\tclassName={cn(\"border rounded-md overflow-hidden\", className)}\n\t\t\t{...props}\n\t\t>\n\t\t\t{!hideHeader && (\n\t\t\t\t<div className=\"p-3 border-b bg-muted/50\">\n\t\t\t\t\t<h4 className=\"text-sm font-semibold\">{title}</h4>\n\t\t\t\t\t{schema.description && (\n\t\t\t\t\t\t<p className=\"text-xs text-muted-foreground mt-1\">\n\t\t\t\t\t\t\t{schema.description}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t)}\n\t\t\t<div className=\"max-h-[400px] overflow-y-auto p-2\">\n\t\t\t\t{isUnion ? (\n\t\t\t\t\tunions && unions.length > 0 ? (\n\t\t\t\t\t\tunions.map((branch: UnionBranch, index: number) => (\n\t\t\t\t\t\t\t<UnionBranchView\n\t\t\t\t\t\t\t\tkey={branch.branchIndex}\n\t\t\t\t\t\t\t\tbranch={branch}\n\t\t\t\t\t\t\t\tisLast={index === unions.length - 1}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t))\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<div className=\"p-4 text-sm text-muted-foreground text-center\">\n\t\t\t\t\t\t\tNo union branches defined\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)\n\t\t\t\t) : properties && properties.length > 0 ? (\n\t\t\t\t\tproperties.map((property) => (\n\t\t\t\t\t\t<PropertyRow key={property.name} property={property} />\n\t\t\t\t\t))\n\t\t\t\t) : (\n\t\t\t\t\t<div className=\"p-4 text-sm text-muted-foreground text-center\">\n\t\t\t\t\t\tNo properties defined\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n",
			"type": "registry:ui"
		}
	]
}
