'use client'; import { createContext, Fragment, use, useState } from 'react'; import { Collapsible, CollapsibleContent, CollapsibleTrigger, } from 'fumadocs-ui/components/ui/collapsible'; import { cn } from '@/lib/utils'; import { Plus, X } from 'lucide-react'; import type { SchemaData, SchemaUIGeneratedData } from './schema-generator'; interface SchemaUIProps { name: string; required?: boolean; as?: 'property' | 'body'; generated: SchemaUIGeneratedData; isResponse?: boolean; } const DataContext = createContext(null); const ResponseContext = createContext(false); function useData() { const ctx = use(DataContext); if (!ctx) throw new Error('Missing DataContext'); return ctx; } function useIsResponse() { return use(ResponseContext); } export function CustomSchemaUI({ name, required = false, as = 'property', generated, isResponse = false, }: SchemaUIProps) { const schema = generated.refs[generated.$root]; const isProperty = as === 'property' || !isExpandable(schema, generated.refs); return ( {isProperty ? ( ) : ( )} ); } function SchemaContent({ $type, parentPath = '', }: { $type: string; parentPath?: string; }) { const { refs } = useData(); const schema = refs[$type]; if (schema.type === 'object' && schema.props.length > 0) { return (
{schema.props.map((prop) => ( ))}
); } if (schema.type === 'array') { return ; } if ((schema.type === 'or' || schema.type === 'and') && schema.items.length > 0) { const label = schema.type === 'or' ? 'One of:' : 'All of:'; return (

{label}

{schema.items.map((item, i) => (
{item.name} {isExpandable(refs[item.$type], refs) && ( )}
))}
); } return null; } function SchemaProperty({ name, $type, required, parentPath = '', isRoot = false, }: { name: string; $type: string; required: boolean; parentPath?: string; isRoot?: boolean; }) { const { refs } = useData(); const isResponse = useIsResponse(); const schema = refs[$type]; const fullPath = parentPath ? `${parentPath}.${name}` : name; const hasChildren = isExpandable(schema, refs); const typeDisplay = getTypeDisplay(schema); return (
{/* Property header */}
{name} {typeDisplay} {required && !isResponse && ( Required )} {schema.deprecated && ( Deprecated )}
{/* Description */} {schema.description && (
{schema.description}
)} {/* Info tags */} {schema.infoTags && schema.infoTags.length > 0 && (
{schema.infoTags.map((tag, i) => ( {tag} ))}
)} {/* Enum values */} {schema.enumValues && schema.enumValues.length > 0 && ( )} {/* Expandable child attributes */} {hasChildren && ( )}
); } function EnumValues({ values }: { values: string[] }) { return (
Possible values:
{values.map((value) => ( {value} ))}
); } function ExpandableContent({ $type, parentPath, }: { $type: string; parentPath: string; }) { const [isOpen, setIsOpen] = useState(false); const { refs } = useData(); const schema = refs[$type]; const childCount = getChildCount(schema); const label = schema.type === 'array' ? 'item properties' : 'child attributes'; return ( {isOpen ? ( <> Hide {label} ) : ( <> Show {childCount > 0 ? `${childCount} ` : ''}{label} )}
); } function isExpandable( schema: SchemaData, refs?: Record, visited: Set = new Set() ): boolean { if (schema.type === 'object' && schema.props.length > 0) return true; if (schema.type === 'array') { // Only expandable if items have structure (object/nested) if (!refs) return true; const itemType = schema.item.$type; if (visited.has(itemType)) return false; // Circular ref - not expandable const itemSchema = refs[itemType]; if (!itemSchema) return false; return itemSchema.type !== 'primitive'; } if ((schema.type === 'or' || schema.type === 'and') && schema.items.length > 0) { // Only expandable if at least one variant has nested structure if (!refs) return true; return schema.items.some((item) => { if (visited.has(item.$type)) return false; // Circular ref - not expandable const itemSchema = refs[item.$type]; if (!itemSchema) return false; visited.add(item.$type); return isExpandable(itemSchema, refs, visited); }); } return false; } function getTypeDisplay(schema: SchemaData): string { if (schema.type === 'array') { return `array of ${schema.aliasName}`; } return schema.typeName; } function getChildCount(schema: SchemaData): number { if (schema.type === 'object') return schema.props.length; if (schema.type === 'or' || schema.type === 'and') return schema.items.length; return 0; }