{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "schema-builder",
  "title": "Schema Builder",
  "description": "Controlled JSON Schema editor for building Retab extraction schemas — fields, types, nested objects, arrays, enums, and required flags.",
  "dependencies": [
    "ajv@^8.17.1",
    "ajv-errors@^3.0.0",
    "ajv-formats@^3.0.1",
    "lucide-react@^0.460.0",
    "radix-ui@^1.5.0",
    "sonner@^2.0.0"
  ],
  "registryDependencies": [
    "@retab/utils",
    "@retab/alert-dialog",
    "@retab/form",
    "@retab/input",
    "@retab/label",
    "@retab/switch",
    "@retab/textarea",
    "dialog",
    "dropdown-menu",
    "tooltip",
    "@retab/effect-key",
    "@retab/use-keyed-layout-effect",
    "@retab/use-keyed-mount-effect"
  ],
  "files": [
    {
      "path": "registry/new-york-v4/ui/schema-builder.tsx",
      "content": "\"use client\";\n\nexport {\n  SchemaBuilder,\n  type ExtendedJSONSchema7,\n  type SchemaBuilderFeatures,\n  type SchemaBuilderProps,\n  type SchemaBuilderView,\n} from \"@/components/schema-editor/schema-builder\";\n",
      "type": "registry:ui",
      "target": "@ui/schema-builder.tsx"
    },
    {
      "path": "components/schema-editor/document-array-node-editor.tsx",
      "content": "\"use client\";\n\nimport type {\n  DocumentSchemaNodeEditorProps,\n  RenderDocumentNodeEditor,\n  SchemaEditorMode,\n} from \"@/components/schema-editor/document-node-editor-types\";\nimport type { SchemaDocument } from \"@/components/schema-editor/document/types\";\nimport type { DocumentNodeView } from \"@/components/schema-editor/document/view-model\";\nimport type { ResolvedSchemaBuilderFeatures } from \"@/components/schema-editor/schema-builder-types\";\n\ninterface DocumentArrayNodeEditorProps {\n  doc: SchemaDocument;\n  nodeId: string;\n  nodeView: DocumentNodeView;\n  path: string;\n  setDefsAccordionOpen: (open: boolean) => void;\n  draggedParentRef: DocumentSchemaNodeEditorProps[\"draggedParentRef\"];\n  draggedPropertyRef: DocumentSchemaNodeEditorProps[\"draggedPropertyRef\"];\n  mode: SchemaEditorMode;\n  features: ResolvedSchemaBuilderFeatures;\n  renderNode: RenderDocumentNodeEditor;\n  dispatch: DocumentSchemaNodeEditorProps[\"dispatch\"];\n}\n\nexport function DocumentArrayNodeEditor({\n  dispatch,\n  doc,\n  nodeId,\n  nodeView,\n  path,\n  setDefsAccordionOpen,\n  draggedParentRef,\n  draggedPropertyRef,\n  mode,\n  features,\n  renderNode,\n}: DocumentArrayNodeEditorProps) {\n  const itemView = nodeView.items;\n  if (!itemView) {\n    return null;\n  }\n\n  return (\n    <div className=\"ml-4\">\n      <div className=\"border-border ml-4 border-l\">\n        {renderNode({\n          dispatch,\n          doc,\n          draggedParentRef,\n          draggedPropertyRef,\n          mode,\n          features,\n          name: \"items\",\n          nodeId: itemView.nodeId,\n          nodeView: itemView,\n          path: `${path}.items`,\n          canDelete: false,\n          hidePencilButton: true,\n          setDefsAccordionOpen,\n        })}\n      </div>\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/document-array-node-editor.tsx"
    },
    {
      "path": "components/schema-editor/document-definitions-editor-controller.ts",
      "content": "import * as React from \"react\";\nimport { toast } from \"sonner\";\n\nimport type {\n  DocumentSchemaNodeEditorProps,\n  SchemaEditorMode,\n} from \"@/components/schema-editor/document-node-editor-types\";\nimport { nodeFromJson } from \"@/components/schema-editor/document/convert\";\nimport {\n  addDefinition,\n  removeDefinition,\n  renameDefinition,\n} from \"@/components/schema-editor/document/definition-operations\";\nimport { isDefinitionReferenced } from \"@/components/schema-editor/document/derive\";\nimport { replaceNodeJson } from \"@/components/schema-editor/document/json-node\";\nimport { definitionRef } from \"@/components/schema-editor/document/json-pointer\";\nimport type {\n  DefinitionEntry,\n  SchemaDocument,\n} from \"@/components/schema-editor/document/types\";\nimport { getDocumentNodeView } from \"@/components/schema-editor/document/view-model\";\nimport type { SchemaDispatch } from \"@/components/schema-editor/schema-builder-types\";\n\ninterface DocumentDefinitionsEditorControllerOptions {\n  dispatch: SchemaDispatch;\n  doc: SchemaDocument;\n  mode: SchemaEditorMode;\n  definitionsEnabled: boolean;\n  accordionOpen: boolean;\n  setAccordionOpen: (open: boolean) => void;\n}\n\nexport function useDocumentDefinitionsEditorController({\n  dispatch,\n  doc,\n  mode,\n  definitionsEnabled,\n  accordionOpen,\n  setAccordionOpen,\n}: DocumentDefinitionsEditorControllerOptions) {\n  const [newDefinitionName, setNewDefinitionName] = React.useState(\"\");\n  const editable = mode === \"editable\";\n  const shouldShowClosedPrompt =\n    doc.defs.length === 0 && (!accordionOpen || !definitionsEnabled);\n  const accordionValue = accordionOpen ? \"defs\" : \"\";\n\n  const openDefinitions = React.useCallback(() => {\n    setAccordionOpen(true);\n  }, [setAccordionOpen]);\n\n  const addNewDefinition = React.useCallback(() => {\n    const definitionName = newDefinitionName.trim();\n    if (!definitionName) return;\n\n    dispatch(\n      (current) =>\n        addDefinition(current, {\n          name: definitionName,\n          node: nodeFromJson(\n            { type: \"object\", properties: {}, required: [] },\n            current,\n          ),\n        }).doc,\n    );\n    setNewDefinitionName(\"\");\n    setAccordionOpen(true);\n  }, [dispatch, newDefinitionName, setAccordionOpen]);\n\n  const deleteDefinition = React.useCallback(\n    (definition: DefinitionEntry) => {\n      if (\n        isDefinitionReferenced(doc, definition.id, {\n          exceptDefId: definition.id,\n        })\n      ) {\n        toast.error(\n          `Cannot delete \"${definition.name}\" because it is referenced by one or more $ref properties. Remove or update those references first.`,\n        );\n        return;\n      }\n\n      dispatch((current) => removeDefinition(current, definition.id));\n      if (doc.defs.length <= 1) {\n        setAccordionOpen(false);\n      }\n    },\n    [dispatch, doc, setAccordionOpen],\n  );\n\n  const updateDefinition = React.useCallback(\n    (\n      definition: DefinitionEntry,\n      newName: string,\n      updatedDefinition?: Parameters<\n        NonNullable<DocumentSchemaNodeEditorProps[\"onNameChange\"]>\n      >[1],\n    ) => {\n      if (newName === definition.name && !updatedDefinition) return;\n\n      dispatch((current) => {\n        let next = current;\n        if (newName !== definition.name) {\n          next = renameDefinition(next, definition.id, newName);\n        }\n        if (updatedDefinition) {\n          next = replaceNodeJson(next, definition.node.id, updatedDefinition);\n        }\n        return next;\n      });\n    },\n    [dispatch],\n  );\n\n  const definitionViews = React.useMemo(\n    () =>\n      doc.defs.map((definition) => ({\n        definition,\n        nodeView: getDocumentNodeView(doc, definition.node),\n        path: definitionRef(\"$defs\", definition.name),\n        canDelete: !isDefinitionReferenced(doc, definition.id, {\n          exceptDefId: definition.id,\n        }),\n      })),\n    [doc],\n  );\n\n  return {\n    accordionValue,\n    definitionViews,\n    editable,\n    newDefinitionName,\n    setNewDefinitionName,\n    shouldShowClosedPrompt,\n    openDefinitions,\n    addNewDefinition,\n    deleteDefinition,\n    updateDefinition,\n  };\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/document-definitions-editor-controller.ts"
    },
    {
      "path": "components/schema-editor/document-definitions-editor.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { PlusIcon } from \"lucide-react\";\n\nimport {\n  Accordion,\n  AccordionContent,\n  AccordionItem,\n  AccordionTrigger,\n} from \"@/components/ui/accordion\";\nimport { Button } from \"@/components/ui/button\";\nimport { Input } from \"@/components/ui/input\";\nimport { useDocumentDefinitionsEditorController } from \"@/components/schema-editor/document-definitions-editor-controller\";\nimport type { SchemaEditorMode } from \"@/components/schema-editor/document-node-editor-types\";\nimport {\n  definitionElementId,\n  DEFINITIONS_SECTION_ID,\n} from \"@/components/schema-editor/document-node-reveal\";\nimport { DocumentSchemaNodeEditor } from \"@/components/schema-editor/document-schema-node-editor\";\nimport type { SchemaDocument } from \"@/components/schema-editor/document/types\";\nimport type {\n  ResolvedSchemaBuilderFeatures,\n  SchemaDispatch,\n} from \"@/components/schema-editor/schema-builder-types\";\n\ninterface DocumentDefinitionsEditorProps {\n  dispatch: SchemaDispatch;\n  doc: SchemaDocument;\n  mode: SchemaEditorMode;\n  definitionsEnabled: boolean;\n  features: ResolvedSchemaBuilderFeatures;\n  accordionOpen: boolean;\n  setAccordionOpen: (open: boolean) => void;\n  draggedParentRef: React.RefObject<string | null>;\n  draggedPropertyRef: React.RefObject<string | null>;\n}\n\nexport function DocumentDefinitionsEditor({\n  dispatch,\n  doc,\n  mode,\n  definitionsEnabled,\n  features,\n  accordionOpen,\n  setAccordionOpen,\n  draggedParentRef,\n  draggedPropertyRef,\n}: DocumentDefinitionsEditorProps) {\n  const controller = useDocumentDefinitionsEditorController({\n    dispatch,\n    doc,\n    mode,\n    definitionsEnabled,\n    accordionOpen,\n    setAccordionOpen,\n  });\n\n  if (controller.shouldShowClosedPrompt) {\n    return mode === \"descriptionOnly\" ? null : (\n      <div className=\"mt-4 flex\">\n        <div\n          className=\"rounded-md transition-colors duration-300\"\n          id={DEFINITIONS_SECTION_ID}\n        >\n          {definitionsEnabled ? (\n            <Button\n              type=\"button\"\n              variant=\"outline\"\n              size=\"sm\"\n              onClick={controller.openDefinitions}\n            >\n              <PlusIcon className=\"h-4 w-4\" />\n              <span>Add definition</span>\n            </Button>\n          ) : null}\n        </div>\n      </div>\n    );\n  }\n\n  return (\n    <Accordion\n      type=\"single\"\n      collapsible\n      id={DEFINITIONS_SECTION_ID}\n      className=\"border-border mt-6 w-full rounded-lg border px-4 pb-0\"\n      value={controller.accordionValue}\n      onValueChange={(value) => setAccordionOpen(value === \"defs\")}\n    >\n      <AccordionItem value=\"defs\" className=\"border-none bg-transparent\">\n        <AccordionTrigger className=\"text-muted-foreground bg-transparent font-medium\">\n          <div className=\"flex items-center\">\n            Definitions ({doc.defs.length})\n          </div>\n        </AccordionTrigger>\n        <AccordionContent className=\"bg-transparent px-1 pt-2\">\n          <div className=\"space-y-4\">\n            {doc.defs.length === 0 && (\n              <p className=\"text-muted-foreground text-sm\">\n                Create reusable schema components to avoid duplication with a\n                new definition.\n              </p>\n            )}\n            {controller.definitionViews.map((definitionView) => {\n              const { definition } = definitionView;\n              return (\n                <div\n                  key={definition.id}\n                  className=\"py-2 transition-colors duration-300\"\n                  id={definitionElementId(definition.id)}\n                >\n                  <DocumentSchemaNodeEditor\n                    dispatch={dispatch}\n                    doc={doc}\n                    draggedParentRef={draggedParentRef}\n                    draggedPropertyRef={draggedPropertyRef}\n                    mode={definitionsEnabled ? mode : \"readOnly\"}\n                    features={features}\n                    name={definition.name}\n                    nodeId={definition.node.id}\n                    nodeView={definitionView.nodeView}\n                    onNameChange={(newName, updatedDefinition) =>\n                      controller.updateDefinition(\n                        definition,\n                        newName,\n                        updatedDefinition,\n                      )\n                    }\n                    path={definitionView.path}\n                    canDelete={definitionView.canDelete}\n                    onDelete={() => controller.deleteDefinition(definition)}\n                    setDefsAccordionOpen={setAccordionOpen}\n                  />\n                </div>\n              );\n            })}\n            {controller.editable && definitionsEnabled && (\n              <div className=\"flex items-center gap-3\">\n                <Input\n                  placeholder=\"New definition name\"\n                  className=\"w-40\"\n                  value={controller.newDefinitionName}\n                  onChange={(event) =>\n                    controller.setNewDefinitionName(event.target.value)\n                  }\n                />\n                <Button\n                  type=\"button\"\n                  variant=\"outline\"\n                  size=\"sm\"\n                  disabled={!controller.newDefinitionName.trim()}\n                  onClick={controller.addNewDefinition}\n                >\n                  <PlusIcon className=\"h-4 w-4\" />\n                  <span>Add</span>\n                </Button>\n              </div>\n            )}\n          </div>\n        </AccordionContent>\n      </AccordionItem>\n    </Accordion>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/document-definitions-editor.tsx"
    },
    {
      "path": "components/schema-editor/document-enum-node-editor.tsx",
      "content": "\"use client\";\n\nimport { useState } from \"react\";\n\nimport type {\n  DocumentSchemaNodeEditorProps,\n  SchemaEditorMode,\n} from \"@/components/schema-editor/document-node-editor-types\";\nimport {\n  addEnumValue,\n  removeEnumValue,\n  updateEnumValue,\n} from \"@/components/schema-editor/document/enum-operations\";\nimport type { EnumValue } from \"@/components/schema-editor/document/types\";\nimport { SchemaChipAddRow } from \"@/components/schema-editor/primitives/schema-chip-add-row\";\nimport { SchemaChipList } from \"@/components/schema-editor/primitives/schema-chip-list\";\n\ninterface DocumentEnumNodeEditorProps {\n  dispatch: DocumentSchemaNodeEditorProps[\"dispatch\"];\n  mode: SchemaEditorMode;\n  nodeId: string;\n  enumEntries: EnumValue[];\n}\n\nexport function DocumentEnumNodeEditor({\n  dispatch,\n  mode,\n  nodeId,\n  enumEntries,\n}: DocumentEnumNodeEditorProps) {\n  const [newEnumValue, setNewEnumValue] = useState(\"\");\n  const editable = mode === \"editable\";\n  const canAddEnumValue = newEnumValue.trim().length > 0;\n\n  const handleAddEnum = () => {\n    if (!canAddEnumValue) return;\n    dispatch((current) => addEnumValue(current, nodeId, newEnumValue.trim()));\n    setNewEnumValue(\"\");\n  };\n\n  const handleRemoveEnum = (id: string) => {\n    dispatch((current) => removeEnumValue(current, nodeId, id));\n  };\n\n  const handleEditEnum = (id: string, newValue: string) => {\n    dispatch((current) =>\n      updateEnumValue(current, nodeId, id, { value: newValue }),\n    );\n  };\n  const addInput = {\n    focusAfterSubmit: true,\n    inputLabel: \"New choice\",\n    placeholder: \"New choice\",\n    submitLabel: \"Add\",\n    value: newEnumValue,\n    onChange: setNewEnumValue,\n    onSubmit: handleAddEnum,\n  };\n\n  return (\n    <div className=\"ml-6\">\n      <div className=\"mt-1 mb-2\">\n        <SchemaChipList\n          editable={editable}\n          items={enumEntries.map((entry, index) => {\n            const value = String(entry.value);\n            return {\n              id: entry.id,\n              inputLabel: `Option ${index + 1}: ${value || \"empty\"}`,\n              removeLabel: `Remove option ${value}`,\n              value,\n            };\n          })}\n          onRemove={handleRemoveEnum}\n          onReplace={handleEditEnum}\n        />\n        {editable && (\n          <SchemaChipAddRow addInput={addInput} editable={editable} />\n        )}\n      </div>\n\n      {enumEntries.length === 0 && !editable && (\n        <div className=\"text-muted-foreground mb-2 text-sm\">\n          No enum values defined.\n        </div>\n      )}\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/document-enum-node-editor.tsx"
    },
    {
      "path": "components/schema-editor/document-node-actions.tsx",
      "content": "\"use client\";\n\nimport { SchemaRowActions } from \"@/components/schema-editor/primitives/schema-row-actions\";\n\ninterface DocumentNodeActionsProps {\n  canDelete: boolean;\n  mode: \"descriptionOnly\" | \"readOnly\" | \"editable\";\n  editable: boolean;\n  hidePencilButton: boolean;\n  onDelete?: () => void;\n  onOpenMetadata: () => void;\n}\n\nexport function DocumentNodeActions(props: DocumentNodeActionsProps) {\n  const details =\n    props.hidePencilButton || !props.onOpenMetadata\n      ? undefined\n      : props.mode === \"readOnly\"\n        ? {\n            label: \"View field properties\",\n            mode: \"view\" as const,\n            onOpen: props.onOpenMetadata,\n          }\n        : {\n            label: \"Edit field properties\",\n            mode: \"edit\" as const,\n            onOpen: props.onOpenMetadata,\n          };\n\n  return (\n    <SchemaRowActions\n      canDelete={props.canDelete}\n      editable={props.editable}\n      details={details}\n      onDelete={props.onDelete}\n    />\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/document-node-actions.tsx"
    },
    {
      "path": "components/schema-editor/document-node-description-control.tsx",
      "content": "\"use client\";\n\nimport { SchemaInlineDescription } from \"@/components/schema-editor/primitives/schema-inline-description\";\n\ninterface DocumentNodeDescriptionControlProps {\n  description: string;\n  mode: \"descriptionOnly\" | \"readOnly\" | \"editable\";\n  onOpenMetadata: () => void;\n  onSubmitDescription: (description: string) => void;\n}\n\nexport function DocumentNodeDescriptionControl({\n  description,\n  mode,\n  onOpenMetadata,\n  onSubmitDescription,\n}: DocumentNodeDescriptionControlProps) {\n  return (\n    <SchemaInlineDescription\n      ariaLabel=\"Field description\"\n      value={description}\n      editable={mode !== \"readOnly\"}\n      onOpenDetails={onOpenMetadata}\n      onCommit={onSubmitDescription}\n    />\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/document-node-description-control.tsx"
    },
    {
      "path": "components/schema-editor/document-node-editor-types.ts",
      "content": "import type * as React from \"react\";\n\nimport type { SchemaDocument } from \"@/components/schema-editor/document/types\";\nimport type { DocumentNodeView } from \"@/components/schema-editor/document/view-model\";\nimport type { ExtendedJSONSchema7 } from \"@/components/schema-editor/lib/json-schema-types\";\nimport type {\n  ResolvedSchemaBuilderFeatures,\n  SchemaDispatch,\n} from \"@/components/schema-editor/schema-builder-types\";\nimport type { SchemaEditorMode } from \"@/components/schema-editor/schema-editor-mode\";\n\nexport type { SchemaEditorMode };\n\nexport interface DocumentSchemaNodeEditorProps {\n  dispatch: SchemaDispatch;\n  doc: SchemaDocument;\n  name: string;\n  nodeId: string;\n  nodeView: DocumentNodeView;\n  path: string;\n  canDelete?: boolean;\n  onDelete?: () => void;\n  onNameChange?: (newName: string, updatedNode?: ExtendedJSONSchema7) => void;\n  setDefsAccordionOpen: (open: boolean) => void;\n  draggedParentRef: React.RefObject<string | null>;\n  draggedPropertyRef: React.RefObject<string | null>;\n  mode?: SchemaEditorMode;\n  hidePencilButton?: boolean;\n  isRequired?: boolean;\n  onRequiredChange?: (required: boolean) => void;\n  siblingNames?: string[];\n  features?: ResolvedSchemaBuilderFeatures;\n}\n\nexport type RenderDocumentNodeEditor = (\n  props: DocumentSchemaNodeEditorProps,\n) => React.ReactNode;\n",
      "type": "registry:component",
      "target": "@components/schema-editor/document-node-editor-types.ts"
    },
    {
      "path": "components/schema-editor/document-node-header-controller.ts",
      "content": "import * as React from \"react\";\nimport type { JSONSchema7Definition } from \"json-schema\";\n\nimport {\n  revealDefinitionElement,\n  revealDefinitionsSection,\n} from \"@/components/schema-editor/document-node-reveal\";\nimport { projectNode } from \"@/components/schema-editor/document/convert\";\nimport { setRefByName } from \"@/components/schema-editor/document/definition-operations\";\nimport { setEnumValues } from \"@/components/schema-editor/document/enum-operations\";\nimport { setNodeDescription } from \"@/components/schema-editor/document/node-metadata\";\nimport {\n  setNodeEditorType,\n  type SchemaEditorType,\n} from \"@/components/schema-editor/document/type-operations\";\nimport type { SchemaDocument } from \"@/components/schema-editor/document/types\";\nimport type { DocumentNodeView } from \"@/components/schema-editor/document/view-model\";\nimport type { ExtendedJSONSchema7 } from \"@/components/schema-editor/lib/json-schema-types\";\nimport type { PropertyFormCommand } from \"@/components/schema-editor/property-form/types\";\nimport type { SchemaDispatch } from \"@/components/schema-editor/schema-builder-types\";\n\ninterface DocumentNodeHeaderControllerOptions {\n  dispatch: SchemaDispatch;\n  doc: SchemaDocument;\n  nodeId: string;\n  nodeView: DocumentNodeView;\n  setDefsAccordionOpen: (open: boolean) => void;\n}\n\nexport function useDocumentNodeHeaderController({\n  dispatch,\n  doc,\n  nodeId,\n  nodeView,\n  setDefsAccordionOpen,\n}: DocumentNodeHeaderControllerOptions) {\n  const schemaNode = projectNode(doc, nodeView.docNode) as ExtendedJSONSchema7;\n  const defs = React.useMemo(() => {\n    const next: Record<string, JSONSchema7Definition> = {};\n    for (const definition of doc.defs) {\n      setRecordValue(next, definition.name, projectNode(doc, definition.node));\n    }\n    return next;\n  }, [doc]);\n  const localType = nodeView.type;\n  const description = nodeView.description || \"\";\n  const refName = localType === \"$ref\" ? nodeView.refName : undefined;\n\n  const [metadataDialogOpen, setMetadataDialogOpen] = React.useState(false);\n  const [enumCreationDialogOpen, setEnumCreationDialogOpen] =\n    React.useState(false);\n\n  const showDefinition = React.useCallback(\n    (definitionName: string) => {\n      setDefsAccordionOpen(true);\n      const definition = doc.defs.find((def) => def.name === definitionName);\n      if (definition) {\n        revealDefinitionElement(definition.id);\n      }\n    },\n    [doc.defs, setDefsAccordionOpen],\n  );\n\n  const showDefinitionsSection = React.useCallback(() => {\n    setDefsAccordionOpen(true);\n    revealDefinitionsSection();\n  }, [setDefsAccordionOpen]);\n\n  const selectType = React.useCallback(\n    (newType: SchemaEditorType | \"enum\") => {\n      if (newType === \"enum\") {\n        if (localType !== \"enum\") {\n          setEnumCreationDialogOpen(true);\n        }\n        return;\n      }\n      dispatch((current) =>\n        setNodeEditorType(current, nodeId, newType as SchemaEditorType),\n      );\n    },\n    [dispatch, localType, nodeId],\n  );\n\n  const confirmEnumValues = React.useCallback(\n    (enumValues: string[]) => {\n      dispatch((current) => setEnumValues(current, nodeId, enumValues));\n    },\n    [dispatch, nodeId],\n  );\n\n  const submitDescription = React.useCallback(\n    (nextDescription: string) => {\n      dispatch((current) =>\n        setNodeDescription(current, nodeId, nextDescription || undefined),\n      );\n    },\n    [dispatch, nodeId],\n  );\n\n  const selectDefinition = React.useCallback(\n    (definitionName: string) => {\n      dispatch((current) => setRefByName(current, nodeId, definitionName));\n    },\n    [dispatch, nodeId],\n  );\n\n  const selectObjectTemplate = React.useCallback(\n    (templateName: string) => {\n      void import(\"./optional/object-templates/object-template-reference\").then(\n        ({ applyObjectTemplateReferenceToDocument }) => {\n          dispatch((current) =>\n            applyObjectTemplateReferenceToDocument(\n              current,\n              nodeId,\n              templateName,\n            ),\n          );\n        },\n      );\n    },\n    [dispatch, nodeId],\n  );\n\n  const handlePropertyFormCommand = React.useCallback(\n    async (command: PropertyFormCommand) => {\n      if (command.type === \"createDefinition\") {\n        showDefinitionsSection();\n        return;\n      }\n\n      if (command.type === \"installObjectTemplate\") {\n        const { addObjectTemplateDefinitionsToDocument } = await import(\n          \"./optional/object-templates/object-template-reference\"\n        );\n        dispatch((current) =>\n          addObjectTemplateDefinitionsToDocument(current, command.templateName),\n        );\n      }\n    },\n    [dispatch, showDefinitionsSection],\n  );\n\n  return {\n    schemaNode,\n    defs,\n    localType,\n    description,\n    refName,\n    metadataDialogOpen,\n    setMetadataDialogOpen,\n    enumCreationDialogOpen,\n    setEnumCreationDialogOpen,\n    showDefinition,\n    showDefinitionsSection,\n    selectType,\n    confirmEnumValues,\n    submitDescription,\n    selectDefinition,\n    selectObjectTemplate,\n    handlePropertyFormCommand,\n  };\n}\n\nfunction setRecordValue<T>(record: Record<string, T>, key: string, value: T) {\n  Object.defineProperty(record, key, {\n    value,\n    enumerable: true,\n    configurable: true,\n    writable: true,\n  });\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/document-node-header-controller.ts"
    },
    {
      "path": "components/schema-editor/document-node-header.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { DocumentNodeActions } from \"@/components/schema-editor/document-node-actions\";\nimport { DocumentNodeDescriptionControl } from \"@/components/schema-editor/document-node-description-control\";\nimport type {\n  DocumentSchemaNodeEditorProps,\n  SchemaEditorMode,\n} from \"@/components/schema-editor/document-node-editor-types\";\nimport { useDocumentNodeHeaderController } from \"@/components/schema-editor/document-node-header-controller\";\nimport { DocumentNodeNameControl } from \"@/components/schema-editor/document-node-name-control\";\nimport { DocumentNodeTypeMenu } from \"@/components/schema-editor/document-node-type-menu\";\nimport type { SchemaDocument } from \"@/components/schema-editor/document/types\";\nimport type { DocumentNodeView } from \"@/components/schema-editor/document/view-model\";\nimport type { ExtendedJSONSchema7 } from \"@/components/schema-editor/lib/json-schema-types\";\nimport { NodeDialog } from \"@/components/schema-editor/node-dialog\";\nimport { SchemaFieldRow } from \"@/components/schema-editor/primitives/schema-field-row\";\n\nimport { EnumCreationDialog } from \"./enum-creation-dialog\";\n\ninterface DocumentNodeHeaderProps\n  extends Omit<\n    DocumentSchemaNodeEditorProps,\n    \"draggedParentRef\" | \"draggedPropertyRef\"\n  > {\n  doc: SchemaDocument;\n  nodeView: DocumentNodeView;\n  mode: SchemaEditorMode;\n  features: NonNullable<DocumentSchemaNodeEditorProps[\"features\"]>;\n  onChange: (newNode: ExtendedJSONSchema7) => void;\n}\n\nexport function DocumentNodeHeader({\n  dispatch,\n  doc,\n  name,\n  nodeView,\n  nodeId,\n  path,\n  canDelete = false,\n  onDelete,\n  onNameChange,\n  setDefsAccordionOpen,\n  mode,\n  hidePencilButton = false,\n  siblingNames = [],\n  features,\n  onChange,\n}: DocumentNodeHeaderProps) {\n  const editable = mode === \"editable\";\n  const controller = useDocumentNodeHeaderController({\n    dispatch,\n    doc,\n    nodeId,\n    nodeView,\n    setDefsAccordionOpen,\n  });\n\n  return (\n    <>\n      {path !== \"#\" && (\n        <SchemaFieldRow\n          id={`schema-field-${path.replace(/^#\\.?/, \"\").split(\".\").join(\"-\")}`}\n          grip={editable ? \"drag\" : \"empty\"}\n          name={\n            <DocumentNodeNameControl\n              editable={editable}\n              name={name}\n              siblingNames={siblingNames}\n              canRename={Boolean(onNameChange)}\n              isReference={controller.localType === \"$ref\"}\n              refName={controller.refName}\n              onNameChange={onNameChange}\n              onShowDefinition={controller.showDefinition}\n            />\n          }\n          description={\n            <DocumentNodeDescriptionControl\n              description={controller.description}\n              mode={mode}\n              onOpenMetadata={() => controller.setMetadataDialogOpen(true)}\n              onSubmitDescription={controller.submitDescription}\n            />\n          }\n          actions={\n            <DocumentNodeActions\n              canDelete={canDelete}\n              editable={editable}\n              mode={mode}\n              hidePencilButton={hidePencilButton}\n              onDelete={onDelete}\n              onOpenMetadata={() => controller.setMetadataDialogOpen(true)}\n            />\n          }\n          type={\n            <DocumentNodeTypeMenu\n              defs={controller.defs}\n              editable={editable}\n              features={features}\n              localType={controller.localType}\n              refName={controller.refName}\n              onCreateDefinition={controller.showDefinitionsSection}\n              onSelectDefinition={controller.selectDefinition}\n              onSelectObjectTemplate={controller.selectObjectTemplate}\n              onSelectType={controller.selectType}\n            />\n          }\n        />\n      )}\n\n      {path !== \"#\" && controller.metadataDialogOpen ? (\n        <NodeDialog\n          isOpen={controller.metadataDialogOpen}\n          onClose={() => controller.setMetadataDialogOpen(false)}\n          onChange={onChange}\n          onNameChange={onNameChange || (() => {})}\n          onDelete={\n            editable && canDelete\n              ? () => {\n                  if (onDelete) {\n                    onDelete();\n                    controller.setMetadataDialogOpen(false);\n                  }\n                }\n              : undefined\n          }\n          node={controller.schemaNode}\n          name={name}\n          mode={mode}\n          siblingNames={siblingNames}\n          formContext={{\n            schemaDefinitions: controller.defs || {},\n            fieldPath: path,\n            objectTemplatesEnabled: features.objectTemplates,\n            onCommand: controller.handlePropertyFormCommand,\n          }}\n        />\n      ) : null}\n\n      <EnumCreationDialog\n        isOpen={controller.enumCreationDialogOpen}\n        onClose={() => controller.setEnumCreationDialogOpen(false)}\n        onConfirm={controller.confirmEnumValues}\n        onCancel={() => undefined}\n      />\n    </>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/document-node-header.tsx"
    },
    {
      "path": "components/schema-editor/document-node-name-control.tsx",
      "content": "\"use client\";\n\nimport { validateName } from \"@/components/schema-editor/lib/json-schema-utils\";\nimport { SchemaInlineName } from \"@/components/schema-editor/primitives/schema-inline-name\";\n\ninterface DocumentNodeNameControlProps {\n  editable: boolean;\n  name: string;\n  siblingNames: string[];\n  canRename: boolean;\n  isReference: boolean;\n  refName?: string;\n  onNameChange?: (newName: string) => void;\n  onShowDefinition: (definitionName: string) => void;\n}\n\nexport function DocumentNodeNameControl({\n  editable,\n  name,\n  siblingNames,\n  canRename,\n  isReference,\n  refName,\n  onNameChange,\n  onShowDefinition,\n}: DocumentNodeNameControlProps) {\n  return (\n    <SchemaInlineName\n      ariaLabel={`Field name ${name}`}\n      value={name}\n      editable={editable}\n      canRename={canRename}\n      validate={(value) => validateName(value, siblingNames, name, \"property\")}\n      reference={\n        isReference && refName\n          ? {\n              label: refName,\n              onReveal: () => onShowDefinition(refName),\n            }\n          : undefined\n      }\n      onCommit={onNameChange}\n    />\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/document-node-name-control.tsx"
    },
    {
      "path": "components/schema-editor/document-node-reveal.ts",
      "content": "export const DEFINITIONS_SECTION_ID = \"schema-definitions-section\";\n\nconst REVEAL_AFTER_RENDER_DELAY_MS = 0;\nconst HIGHLIGHT_DURATION_MS = 2500;\n\nexport function definitionElementId(definitionId: string) {\n  return `schema-definition-${definitionId}`;\n}\n\nexport function revealDefinitionElement(definitionId: string) {\n  revealElementAfterRender(definitionElementId(definitionId));\n}\n\nexport function revealDefinitionsSection() {\n  revealElementAfterRender(DEFINITIONS_SECTION_ID);\n}\n\nfunction revealElementAfterRender(elementId: string) {\n  window.setTimeout(() => {\n    const element = document.getElementById(elementId);\n    if (!element) return;\n\n    element.scrollIntoView({ behavior: \"smooth\", block: \"center\" });\n    element.classList.add(\"bg-accent\");\n    window.setTimeout(() => {\n      element.classList.remove(\"bg-accent\");\n    }, HIGHLIGHT_DURATION_MS);\n  }, REVEAL_AFTER_RENDER_DELAY_MS);\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/document-node-reveal.ts"
    },
    {
      "path": "components/schema-editor/document-node-type-menu.tsx",
      "content": "\"use client\";\n\nimport type { JSONSchema7Definition } from \"json-schema\";\n\nimport type { SchemaEditorType } from \"@/components/schema-editor/document/type-operations\";\nimport { createObjectTemplateTypeTrailingContent } from \"@/components/schema-editor/object-template-type-section\";\nimport {\n  SchemaTypeMenu,\n  type SchemaTypeMenuSection,\n} from \"@/components/schema-editor/primitives/schema-type-menu\";\nimport type { ResolvedSchemaBuilderFeatures } from \"@/components/schema-editor/schema-builder-types\";\nimport {\n  createDefinitionTypeSubmenu,\n  createPrimitiveTypeItems,\n  createTypeMenuValue,\n} from \"@/components/schema-editor/schema-type-menu-sections\";\n\ninterface DocumentNodeTypeMenuProps {\n  defs: Record<string, JSONSchema7Definition>;\n  editable: boolean;\n  features: ResolvedSchemaBuilderFeatures;\n  localType: string;\n  refName?: string;\n  onCreateDefinition: () => void;\n  onSelectDefinition: (definitionName: string) => void;\n  onSelectObjectTemplate: (templateName: string) => void;\n  onSelectType: (type: SchemaEditorType | \"enum\") => void;\n}\n\nexport function DocumentNodeTypeMenu({\n  defs,\n  editable,\n  features,\n  localType,\n  refName,\n  onCreateDefinition,\n  onSelectDefinition,\n  onSelectObjectTemplate,\n  onSelectType,\n}: DocumentNodeTypeMenuProps) {\n  const definitionNames = Object.keys(defs);\n  const sections: SchemaTypeMenuSection[] = [\n    {\n      id: \"types\",\n      kind: \"items\",\n      items: createPrimitiveTypeItems({\n        onSelectType: (type) => onSelectType(type as SchemaEditorType | \"enum\"),\n      }),\n    },\n  ];\n\n  if (features.definitions) {\n    sections.push(\n      createDefinitionTypeSubmenu({\n        createDefinitionLabel: \"Create a new definition to get started\",\n        definitionNames,\n        onCreateDefinition,\n        onSelectDefinition,\n      }),\n    );\n  }\n\n  const trailingContent = features.objectTemplates\n    ? createObjectTemplateTypeTrailingContent({\n        onSelectTemplate: onSelectObjectTemplate,\n      })\n    : undefined;\n\n  return (\n    <SchemaTypeMenu\n      variant=\"row\"\n      editable={editable}\n      sections={sections}\n      trailingContent={trailingContent}\n      value={createTypeMenuValue({ type: localType, refName })}\n    />\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/document-node-type-menu.tsx"
    },
    {
      "path": "components/schema-editor/document-object-node-editor-controller.ts",
      "content": "import type * as React from \"react\";\n\nimport type { DocumentSchemaNodeEditorProps } from \"@/components/schema-editor/document-node-editor-types\";\nimport {\n  beginSchemaRowDrag,\n  leaveSchemaRowDragTarget,\n  resolveSchemaRowDrop,\n  updateSchemaRowDragTarget,\n} from \"@/components/schema-editor/primitives/schema-row-drag\";\nimport { replaceNodeJson } from \"@/components/schema-editor/document/json-node\";\nimport {\n  addProperty,\n  moveProperty,\n  removeProperty,\n  renameProperty,\n  setRequired,\n} from \"@/components/schema-editor/document/property-operations\";\nimport { createNode } from \"@/components/schema-editor/document/type-operations\";\nimport type { DocumentPropertyView } from \"@/components/schema-editor/document/view-model\";\nimport { formatTitle } from \"@/components/schema-editor/schema-title\";\n\ninterface DocumentObjectNodeEditorControllerOptions {\n  dispatch: DocumentSchemaNodeEditorProps[\"dispatch\"];\n  objectNodeId: string;\n  properties: DocumentPropertyView[];\n  draggedPropertyRef: DocumentSchemaNodeEditorProps[\"draggedPropertyRef\"];\n}\n\nexport function useDocumentObjectNodeEditorController({\n  dispatch,\n  objectNodeId,\n  properties,\n  draggedPropertyRef,\n}: DocumentObjectNodeEditorControllerOptions) {\n  const propertyNames = properties.map((property) => property.propertyName);\n  const propertyIds = properties.map((property) => property.propertyId);\n\n  const addNewProperty = (propertyName: string) => {\n    dispatch((current) =>\n      addProperty(current, objectNodeId, {\n        key: propertyName,\n        required: true,\n        node: { ...createNode(\"string\"), title: formatTitle(propertyName) },\n      }),\n    );\n  };\n\n  const setPropertyRequired = (\n    property: DocumentPropertyView,\n    required: boolean,\n  ) => {\n    dispatch((current) => setRequired(current, property.propertyId, required));\n  };\n\n  const updateProperty = (\n    property: DocumentPropertyView,\n    newName: string,\n    updatedNode?: Parameters<\n      NonNullable<DocumentSchemaNodeEditorProps[\"onNameChange\"]>\n    >[1],\n  ) => {\n    dispatch((current) => {\n      let next = current;\n      if (newName !== property.propertyName) {\n        next = renameProperty(next, property.propertyId, newName);\n      }\n      if (updatedNode) {\n        next = replaceNodeJson(next, property.nodeView.nodeId, updatedNode);\n      }\n      return next;\n    });\n  };\n\n  const deleteProperty = (property: DocumentPropertyView) => {\n    dispatch((current) => removeProperty(current, property.propertyId));\n  };\n\n  const startDrag = (\n    event: React.DragEvent<HTMLDivElement>,\n    property: DocumentPropertyView,\n  ) => {\n    beginSchemaRowDrag({\n      event,\n      item: {\n        id: property.propertyId,\n        label: property.propertyName,\n      },\n      draggedRowIdRef: draggedPropertyRef,\n    });\n  };\n\n  const dragOver = (\n    event: React.DragEvent<HTMLDivElement>,\n    property: DocumentPropertyView,\n  ) => {\n    updateSchemaRowDragTarget({\n      event,\n      rowIds: propertyIds,\n      targetRowId: property.propertyId,\n      draggedRowIdRef: draggedPropertyRef,\n    });\n  };\n\n  const drop = (\n    event: React.DragEvent<HTMLDivElement>,\n    property: DocumentPropertyView,\n  ) => {\n    const move = resolveSchemaRowDrop({\n      event,\n      rowIds: propertyIds,\n      targetRowId: property.propertyId,\n      draggedRowIdRef: draggedPropertyRef,\n    });\n    if (!move) return;\n    dispatch((current) =>\n      moveProperty(current, move.sourceRowId, objectNodeId, move.targetIndex),\n    );\n  };\n\n  return {\n    propertyNames,\n    addNewProperty,\n    setPropertyRequired,\n    updateProperty,\n    deleteProperty,\n    startDrag,\n    dragOver,\n    leaveDragTarget: leaveSchemaRowDragTarget,\n    drop,\n  };\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/document-object-node-editor-controller.ts"
    },
    {
      "path": "components/schema-editor/document-object-node-editor.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport {\n  Accordion,\n  AccordionContent,\n  AccordionItem,\n  AccordionTrigger,\n} from \"@/components/ui/accordion\";\nimport type {\n  DocumentSchemaNodeEditorProps,\n  RenderDocumentNodeEditor,\n  SchemaEditorMode,\n} from \"@/components/schema-editor/document-node-editor-types\";\nimport { useDocumentObjectNodeEditorController } from \"@/components/schema-editor/document-object-node-editor-controller\";\nimport { DocumentPropertyAddRow } from \"@/components/schema-editor/document-property-add-row\";\nimport { DocumentPropertyRow } from \"@/components/schema-editor/document-property-row\";\nimport type { SchemaDocument } from \"@/components/schema-editor/document/types\";\nimport type {\n  DocumentNodeView,\n  DocumentPropertyView,\n} from \"@/components/schema-editor/document/view-model\";\nimport type { ResolvedSchemaBuilderFeatures } from \"@/components/schema-editor/schema-builder-types\";\n\ninterface DocumentObjectNodeEditorProps {\n  dispatch: DocumentSchemaNodeEditorProps[\"dispatch\"];\n  doc: SchemaDocument;\n  nodeId: string;\n  nodeView: DocumentNodeView;\n  path: string;\n  setDefsAccordionOpen: (open: boolean) => void;\n  draggedParentRef: DocumentSchemaNodeEditorProps[\"draggedParentRef\"];\n  draggedPropertyRef: DocumentSchemaNodeEditorProps[\"draggedPropertyRef\"];\n  mode: SchemaEditorMode;\n  features: ResolvedSchemaBuilderFeatures;\n  renderNode: RenderDocumentNodeEditor;\n}\n\nexport function DocumentObjectNodeEditor({\n  dispatch,\n  doc,\n  nodeId,\n  nodeView,\n  path,\n  setDefsAccordionOpen,\n  draggedParentRef,\n  draggedPropertyRef,\n  mode,\n  features,\n  renderNode,\n}: DocumentObjectNodeEditorProps) {\n  const editable = mode === \"editable\";\n  const objectNodeId = nodeView.effectiveNode.id ?? nodeId;\n  const properties = nodeView.properties;\n  const controller = useDocumentObjectNodeEditorController({\n    dispatch,\n    objectNodeId,\n    properties,\n    draggedPropertyRef,\n  });\n\n  const renderProperty = (\n    property: DocumentPropertyView,\n    rootLayout: boolean,\n  ) => {\n    return (\n      <DocumentPropertyRow\n        key={property.propertyId}\n        propertyId={property.propertyId}\n        dispatch={dispatch}\n        doc={doc}\n        propertyName={property.propertyName}\n        nodeView={property.nodeView}\n        rootLayout={rootLayout}\n        path={path}\n        setDefsAccordionOpen={setDefsAccordionOpen}\n        draggedParentRef={draggedParentRef}\n        draggedPropertyRef={draggedPropertyRef}\n        mode={mode}\n        features={features}\n        editable={editable}\n        isRequired={property.isRequired}\n        siblingNames={controller.propertyNames}\n        renderNode={renderNode}\n        onRequiredChange={(required) =>\n          controller.setPropertyRequired(property, required)\n        }\n        onNameChange={(newName, updatedNode) =>\n          controller.updateProperty(property, newName, updatedNode)\n        }\n        onDelete={() => controller.deleteProperty(property)}\n        onDragStart={(event) => controller.startDrag(event, property)}\n        onDragOver={(event) => controller.dragOver(event, property)}\n        onDragLeave={controller.leaveDragTarget}\n        onDrop={(event) => controller.drop(event, property)}\n      />\n    );\n  };\n\n  const addPropertyControl = (rootLayout: boolean) =>\n    editable ? (\n      <DocumentPropertyAddRow\n        rootLayout={rootLayout}\n        siblingNames={controller.propertyNames}\n        onAddProperty={controller.addNewProperty}\n      />\n    ) : null;\n\n  if (path === \"#\") {\n    return (\n      <div>\n        <Accordion\n          type=\"single\"\n          collapsible\n          defaultValue=\"properties\"\n          className=\"border-border w-full rounded-lg border px-4 pb-0\"\n        >\n          <AccordionItem value=\"properties\" className=\"border-none\">\n            <AccordionTrigger className=\"text-muted-foreground text-sm font-medium\">\n              Properties ({properties.length})\n            </AccordionTrigger>\n            <AccordionContent className=\"px-1 pt-2\">\n              <div>\n                {properties.length === 0 && (\n                  <p className=\"text-muted-foreground py-2 text-sm\">\n                    Define the data structure for this object.\n                  </p>\n                )}\n                {properties.map((property) => renderProperty(property, true))}\n                {addPropertyControl(true)}\n              </div>\n            </AccordionContent>\n          </AccordionItem>\n        </Accordion>\n      </div>\n    );\n  }\n\n  return (\n    <div className=\"pl-2\">\n      <div>\n        {properties.map((property) => renderProperty(property, false))}\n        {addPropertyControl(false)}\n      </div>\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/document-object-node-editor.tsx"
    },
    {
      "path": "components/schema-editor/document-property-add-row.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { validateName } from \"@/components/schema-editor/lib/json-schema-utils\";\nimport { SchemaAddRow } from \"@/components/schema-editor/primitives/schema-add-row\";\n\ninterface DocumentPropertyAddRowProps {\n  rootLayout: boolean;\n  siblingNames: string[];\n  onAddProperty: (propertyName: string) => void;\n}\n\nexport function DocumentPropertyAddRow({\n  rootLayout,\n  siblingNames,\n  onAddProperty,\n}: DocumentPropertyAddRowProps) {\n  const [propertyName, setPropertyName] = React.useState(\"\");\n  const [propertyNameError, setPropertyNameError] = React.useState<\n    string | null\n  >(null);\n\n  const validatePropertyName = React.useCallback(\n    (value: string) => validateName(value, siblingNames, undefined, \"property\"),\n    [siblingNames],\n  );\n\n  const addProperty = () => {\n    const key = propertyName.trim();\n    const error = validatePropertyName(key);\n    if (error) {\n      setPropertyNameError(error);\n      return;\n    }\n    if (!key) return;\n\n    onAddProperty(key);\n    setPropertyName(\"\");\n    setPropertyNameError(null);\n  };\n\n  return (\n    <SchemaAddRow\n      className={\n        rootLayout ? \"mt-3 ml-4\" : \"border-border mt-2 ml-4 border-l pl-4\"\n      }\n      disabled={false}\n      error={propertyNameError}\n      inputLabel=\"New property name\"\n      placeholder=\"New property name\"\n      submitLabel=\"Add\"\n      value={propertyName}\n      onChange={(value) => {\n        setPropertyName(value);\n        setPropertyNameError(value ? validatePropertyName(value) : null);\n      }}\n      onSubmit={addProperty}\n    />\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/document-property-add-row.tsx"
    },
    {
      "path": "components/schema-editor/document-property-row.tsx",
      "content": "\"use client\";\n\nimport type * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\nimport type {\n  DocumentSchemaNodeEditorProps,\n  RenderDocumentNodeEditor,\n  SchemaEditorMode,\n} from \"@/components/schema-editor/document-node-editor-types\";\nimport type { SchemaDocument } from \"@/components/schema-editor/document/types\";\nimport type { DocumentNodeView } from \"@/components/schema-editor/document/view-model\";\nimport type { ResolvedSchemaBuilderFeatures } from \"@/components/schema-editor/schema-builder-types\";\n\ninterface DocumentPropertyRowProps {\n  propertyId: string;\n  dispatch: DocumentSchemaNodeEditorProps[\"dispatch\"];\n  doc: SchemaDocument;\n  propertyName: string;\n  nodeView: DocumentNodeView;\n  rootLayout: boolean;\n  path: string;\n  setDefsAccordionOpen: (open: boolean) => void;\n  draggedParentRef: DocumentSchemaNodeEditorProps[\"draggedParentRef\"];\n  draggedPropertyRef: DocumentSchemaNodeEditorProps[\"draggedPropertyRef\"];\n  mode: SchemaEditorMode;\n  features: ResolvedSchemaBuilderFeatures;\n  editable: boolean;\n  isRequired: boolean;\n  siblingNames: string[];\n  renderNode: RenderDocumentNodeEditor;\n  onRequiredChange: (required: boolean) => void;\n  onNameChange: DocumentSchemaNodeEditorProps[\"onNameChange\"];\n  onDelete: () => void;\n  onDragStart: React.DragEventHandler<HTMLDivElement>;\n  onDragOver: React.DragEventHandler<HTMLDivElement>;\n  onDragLeave: React.DragEventHandler<HTMLDivElement>;\n  onDrop: React.DragEventHandler<HTMLDivElement>;\n}\n\nexport function DocumentPropertyRow({\n  propertyId,\n  dispatch,\n  doc,\n  propertyName,\n  nodeView,\n  rootLayout,\n  path,\n  setDefsAccordionOpen,\n  draggedParentRef,\n  draggedPropertyRef,\n  mode,\n  features,\n  editable,\n  isRequired,\n  siblingNames,\n  renderNode,\n  onRequiredChange,\n  onNameChange,\n  onDelete,\n  onDragStart,\n  onDragOver,\n  onDragLeave,\n  onDrop,\n}: DocumentPropertyRowProps) {\n  return (\n    <div\n      className={cn(\n        rootLayout ? \"\" : \"border-border ml-4 border-l\",\n        editable && \"cursor-grab\",\n      )}\n      draggable={editable}\n      onDragStart={onDragStart}\n      onDragOver={onDragOver}\n      onDragLeave={onDragLeave}\n      onDrop={onDrop}\n      data-property-id={propertyId}\n      data-property-name={propertyName}\n    >\n      {renderNode({\n        dispatch,\n        doc,\n        draggedParentRef,\n        draggedPropertyRef,\n        mode,\n        features,\n        isRequired,\n        siblingNames,\n        onRequiredChange,\n        name: propertyName,\n        nodeId: nodeView.nodeId,\n        nodeView,\n        onNameChange,\n        path: `${path}.${propertyName}`,\n        canDelete: true,\n        onDelete,\n        setDefsAccordionOpen,\n      })}\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/document-property-row.tsx"
    },
    {
      "path": "components/schema-editor/document-schema-editor-controller.ts",
      "content": "import * as React from \"react\";\n\nimport { replaceNodeJson } from \"@/components/schema-editor/document/json-node\";\nimport {\n  setNodeDescription,\n  setNodeTitle,\n  stripDescriptions,\n} from \"@/components/schema-editor/document/node-metadata\";\nimport type { SchemaDocument } from \"@/components/schema-editor/document/types\";\nimport { getDocumentNodeView } from \"@/components/schema-editor/document/view-model\";\nimport type { ExtendedJSONSchema7 } from \"@/components/schema-editor/lib/json-schema-types\";\nimport type {\n  ResolvedSchemaBuilderFeatures,\n  SchemaDispatch,\n  SchemaValidationResult,\n} from \"@/components/schema-editor/schema-builder-types\";\nimport { resolveSchemaBuilderFeatures } from \"@/components/schema-editor/schema-builder-types\";\nimport { validationErrorsText } from \"@/components/schema-editor/validation\";\n\ninterface DocumentSchemaEditorControllerOptions {\n  doc: SchemaDocument;\n  validation: SchemaValidationResult;\n  dispatch: SchemaDispatch;\n  features?: ResolvedSchemaBuilderFeatures;\n}\n\nexport function useDocumentSchemaEditorController({\n  doc,\n  validation,\n  dispatch,\n  features: featuresProp,\n}: DocumentSchemaEditorControllerOptions) {\n  const features = featuresProp ?? resolveSchemaBuilderFeatures();\n  const [defsAccordionOpen, setDefsAccordionOpen] = React.useState(\n    () => doc.defs.length > 0,\n  );\n  const draggedParentRef = React.useRef<string | null>(null);\n  const draggedPropertyRef = React.useRef<string | null>(null);\n  const validationErrors = React.useMemo(\n    () => validationErrorsText(validation),\n    [validation],\n  );\n  const rootNodeView = React.useMemo(\n    () => getDocumentNodeView(doc, doc.root),\n    [doc],\n  );\n\n  const setRootTitle = React.useCallback(\n    (title: string) => {\n      dispatch((current) => setNodeTitle(current, current.root.id, title));\n    },\n    [dispatch],\n  );\n\n  const setRootDescription = React.useCallback(\n    (description: string) => {\n      dispatch((current) =>\n        setNodeDescription(current, current.root.id, description),\n      );\n    },\n    [dispatch],\n  );\n\n  const eraseRootSchema = React.useCallback(() => {\n    dispatch((current) =>\n      replaceNodeJson(current, current.root.id, {\n        title: \"\",\n        type: \"object\",\n        properties: {},\n      }),\n    );\n  }, [dispatch]);\n\n  const eraseDescriptions = React.useCallback(() => {\n    dispatch((current) => stripDescriptions(current));\n  }, [dispatch]);\n\n  const replaceRoot = React.useCallback(\n    (newNode: ExtendedJSONSchema7) => {\n      dispatch((current) => replaceNodeJson(current, current.root.id, newNode));\n    },\n    [dispatch],\n  );\n\n  return {\n    features,\n    defsAccordionOpen,\n    setDefsAccordionOpen,\n    draggedParentRef,\n    draggedPropertyRef,\n    validationErrors,\n    rootNodeView,\n    setRootTitle,\n    setRootDescription,\n    eraseRootSchema,\n    eraseDescriptions,\n    replaceRoot,\n  };\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/document-schema-editor-controller.ts"
    },
    {
      "path": "components/schema-editor/document-schema-editor.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { DocumentDefinitionsEditor } from \"@/components/schema-editor/document-definitions-editor\";\nimport type { SchemaEditorMode } from \"@/components/schema-editor/document-node-editor-types\";\nimport { useDocumentSchemaEditorController } from \"@/components/schema-editor/document-schema-editor-controller\";\nimport { DocumentSchemaNodeEditor } from \"@/components/schema-editor/document-schema-node-editor\";\nimport type { SchemaDocument } from \"@/components/schema-editor/document/types\";\nimport type { ExtendedJSONSchema7 } from \"@/components/schema-editor/lib/json-schema-types\";\nimport type {\n  ResolvedSchemaBuilderFeatures,\n  SchemaDispatch,\n  SchemaValidationResult,\n} from \"@/components/schema-editor/schema-builder-types\";\nimport { TopLevelEditor } from \"@/components/schema-editor/top-level-editor\";\nimport { ValidationErrorDisplay } from \"@/components/schema-editor/validation-error-display\";\n\ninterface DocumentSchemaEditorProps {\n  doc: SchemaDocument;\n  schema: ExtendedJSONSchema7;\n  validation: SchemaValidationResult;\n  dispatch: SchemaDispatch;\n  mode?: SchemaEditorMode;\n  features?: ResolvedSchemaBuilderFeatures;\n}\n\nexport function DocumentSchemaEditor({\n  doc,\n  schema,\n  validation,\n  dispatch,\n  mode = \"editable\",\n  features: featuresProp,\n}: DocumentSchemaEditorProps) {\n  const controller = useDocumentSchemaEditorController({\n    doc,\n    validation,\n    dispatch,\n    features: featuresProp,\n  });\n\n  return (\n    <div className=\"flex min-h-0 w-full flex-1 flex-col overflow-y-auto\">\n      <div className=\"group flex w-full flex-col\">\n        <ValidationErrorDisplay\n          validationErrors={controller.validationErrors}\n          variant=\"full\"\n        />\n        <TopLevelEditor\n          node={schema}\n          mode={mode}\n          showImportExportActions={controller.features.importExport}\n          onTitleChange={controller.setRootTitle}\n          onDescriptionChange={controller.setRootDescription}\n          onEraseAll={controller.eraseRootSchema}\n          onEraseDescriptions={controller.eraseDescriptions}\n          onReplaceRoot={controller.replaceRoot}\n        />\n      </div>\n\n      <div className=\"flex min-h-0 flex-1 flex-col\">\n        <DocumentSchemaNodeEditor\n          dispatch={dispatch}\n          doc={doc}\n          name=\"root\"\n          nodeId={doc.root.id}\n          nodeView={controller.rootNodeView}\n          path=\"#\"\n          mode={mode}\n          features={controller.features}\n          canDelete={false}\n          setDefsAccordionOpen={controller.setDefsAccordionOpen}\n          draggedParentRef={controller.draggedParentRef}\n          draggedPropertyRef={controller.draggedPropertyRef}\n        />\n        <DocumentDefinitionsEditor\n          dispatch={dispatch}\n          doc={doc}\n          mode={mode}\n          definitionsEnabled={controller.features.definitions}\n          features={controller.features}\n          accordionOpen={controller.defsAccordionOpen}\n          setAccordionOpen={controller.setDefsAccordionOpen}\n          draggedParentRef={controller.draggedParentRef}\n          draggedPropertyRef={controller.draggedPropertyRef}\n        />\n      </div>\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/document-schema-editor.tsx"
    },
    {
      "path": "components/schema-editor/document-schema-node-editor.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { DocumentArrayNodeEditor } from \"@/components/schema-editor/document-array-node-editor\";\nimport { DocumentEnumNodeEditor } from \"@/components/schema-editor/document-enum-node-editor\";\nimport type {\n  DocumentSchemaNodeEditorProps,\n  RenderDocumentNodeEditor,\n} from \"@/components/schema-editor/document-node-editor-types\";\nimport { DocumentNodeHeader } from \"@/components/schema-editor/document-node-header\";\nimport { DocumentObjectNodeEditor } from \"@/components/schema-editor/document-object-node-editor\";\nimport { replaceNodeJson } from \"@/components/schema-editor/document/json-node\";\nimport type { SchemaDocument } from \"@/components/schema-editor/document/types\";\nimport type { ExtendedJSONSchema7 } from \"@/components/schema-editor/lib/json-schema-types\";\nimport { resolveSchemaBuilderFeatures } from \"@/components/schema-editor/schema-builder-types\";\n\nfunction changeNodeJson(\n  doc: SchemaDocument,\n  nodeId: string,\n  nextNode: ExtendedJSONSchema7,\n) {\n  return replaceNodeJson(doc, nodeId, nextNode);\n}\n\nexport function DocumentSchemaNodeEditor({\n  dispatch,\n  doc,\n  name,\n  nodeId,\n  nodeView,\n  path,\n  canDelete = false,\n  onDelete,\n  onNameChange,\n  setDefsAccordionOpen,\n  draggedParentRef,\n  draggedPropertyRef,\n  mode = \"editable\",\n  hidePencilButton = false,\n  isRequired,\n  onRequiredChange,\n  siblingNames = [],\n  features: featuresProp,\n}: DocumentSchemaNodeEditorProps) {\n  const features =\n    featuresProp ??\n    resolveSchemaBuilderFeatures({\n      definitions: true,\n      objectTemplates: true,\n      jsonMode: true,\n      importExport: true,\n    });\n  const onChange = React.useCallback(\n    (newNode: ExtendedJSONSchema7) => {\n      dispatch((current) => changeNodeJson(current, nodeId, newNode));\n    },\n    [dispatch, nodeId],\n  );\n\n  const renderNode = React.useCallback<RenderDocumentNodeEditor>(\n    (props) => <DocumentSchemaNodeEditor {...props} />,\n    [],\n  );\n\n  const localType = nodeView.type;\n\n  return (\n    <div>\n      <DocumentNodeHeader\n        dispatch={dispatch}\n        doc={doc}\n        name={name}\n        nodeView={nodeView}\n        nodeId={nodeId}\n        path={path}\n        canDelete={canDelete}\n        onDelete={onDelete}\n        onNameChange={onNameChange}\n        setDefsAccordionOpen={setDefsAccordionOpen}\n        mode={mode}\n        hidePencilButton={hidePencilButton}\n        isRequired={isRequired}\n        onRequiredChange={onRequiredChange}\n        siblingNames={siblingNames}\n        features={features}\n        onChange={onChange}\n      />\n\n      {localType === \"object\" && (\n        <DocumentObjectNodeEditor\n          dispatch={dispatch}\n          doc={doc}\n          nodeId={nodeId}\n          nodeView={nodeView}\n          path={path}\n          setDefsAccordionOpen={setDefsAccordionOpen}\n          draggedParentRef={draggedParentRef}\n          draggedPropertyRef={draggedPropertyRef}\n          mode={mode}\n          features={features}\n          renderNode={renderNode}\n        />\n      )}\n\n      {localType === \"array\" && (\n        <DocumentArrayNodeEditor\n          dispatch={dispatch}\n          doc={doc}\n          nodeId={nodeId}\n          nodeView={nodeView}\n          path={path}\n          setDefsAccordionOpen={setDefsAccordionOpen}\n          draggedParentRef={draggedParentRef}\n          draggedPropertyRef={draggedPropertyRef}\n          mode={mode}\n          features={features}\n          renderNode={renderNode}\n        />\n      )}\n\n      {localType === \"enum\" && (\n        <DocumentEnumNodeEditor\n          dispatch={dispatch}\n          mode={mode}\n          nodeId={nodeId}\n          enumEntries={nodeView.enumEntries}\n        />\n      )}\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/document-schema-node-editor.tsx"
    },
    {
      "path": "components/schema-editor/document/array.ts",
      "content": "export function mapPreserve<T>(\n  items: T[],\n  fn: (item: T, index: number) => T,\n): T[] {\n  let changed = false;\n  const next = items.map((item, index) => {\n    const result = fn(item, index);\n    if (result !== item) changed = true;\n    return result;\n  });\n  return changed ? next : items;\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/document/array.ts"
    },
    {
      "path": "components/schema-editor/document/convert.ts",
      "content": "import type { JSONSchema7, JSONSchema7Definition } from \"json-schema\";\n\nimport { createId } from \"./id\";\nimport {\n  definitionRefAliases,\n  definitionRef,\n  type DefinitionsKeyword,\n} from \"./json-pointer\";\nimport type {\n  DefinitionEntry,\n  DocumentNode,\n  EnumValue,\n  JsonValue,\n  PropertyEntry,\n  SchemaDocument,\n} from \"./types\";\n\n/**\n * Boundary conversions between vanilla JSON Schema (the wire format) and the\n * editor Document (the in-memory source of truth).\n *\n *  - `fromJsonSchema` is TOTAL: it mints fresh ids, distributes `required`, and\n *    carries every unmodeled keyword into `rest`. Run it ONCE when a new external\n *    schema arrives — not on every render.\n *  - `toJsonSchema` is a PURE PROJECTION: it rebuilds `required[]`, re-projects\n *    `$ref` from the target definition's current name, and drops transient-invalid\n *    artifacts (empty/duplicate property keys). Run it on demand for\n *    `onSchemaChange` and the JSON preview.\n *\n * Together they round-trip losslessly for everything the editor surfaces, and\n * carry everything it doesn't.\n */\n\n/** Keys consumed structurally by a node; everything else falls into `rest`. */\nconst MODELED_NODE_KEYS = new Set<string>([\n  \"type\",\n  \"title\",\n  \"description\",\n  \"properties\",\n  \"required\",\n  \"items\",\n  \"enum\",\n  \"$ref\",\n  \"anyOf\",\n  \"oneOf\",\n  \"allOf\",\n]);\n\ntype RefMap = Map<string, string>; // json-pointer string -> definition NodeId\ntype CreateDocumentId = (prefix?: string) => string;\n\n// ---------------------------------------------------------------------------\n// Import: JSON Schema -> Document\n// ---------------------------------------------------------------------------\n\nexport function fromJsonSchema(schema: JSONSchema7): SchemaDocument {\n  const createImportId = createDeterministicImportIdFactory();\n  const hasDefsKeyword = schema.$defs !== undefined;\n  const hasDefinitionsKeyword = schema.definitions !== undefined;\n  const defsKeyword = schema.$defs\n    ? \"$defs\"\n    : schema.definitions\n      ? \"definitions\"\n      : \"$defs\";\n  const rawDefs = (schema.$defs ?? schema.definitions ?? {}) as Record<\n    string,\n    JSONSchema7Definition\n  >;\n\n  // First pass: give every top-level definition an id so refs can resolve to it.\n  const defEntries: DefinitionEntry[] = Object.keys(rawDefs).map((name) => ({\n    id: createImportId(\"def\"),\n    name,\n    node: { id: createImportId(), rest: {} }, // placeholder, filled in second pass\n  }));\n  const refMap: RefMap = new Map();\n  for (const def of defEntries) {\n    addDefinitionRefMapEntries(refMap, def, {\n      defsKeyword,\n      hasOtherDefsKeyword:\n        defsKeyword === \"$defs\" ? hasDefinitionsKeyword : hasDefsKeyword,\n    });\n  }\n\n  // Second pass: build each definition's node now that the ref map exists.\n  for (const def of defEntries) {\n    def.node = nodeFromSchema(rawDefs[def.name], refMap, {\n      createDocumentId: createImportId,\n    });\n  }\n\n  // Strip only the PRIMARY defs keyword from the root; if the (unusual) other\n  // keyword is also present, it's carried verbatim in `rest` so it isn't lost.\n  const root = nodeFromSchema(schema, refMap, {\n    stripKeyword: defsKeyword,\n    createDocumentId: createImportId,\n  });\n\n  return {\n    root,\n    defs: defEntries,\n    rest: { defsKeyword },\n  };\n}\n\nfunction nodeFromSchema(\n  schema: JSONSchema7Definition,\n  refMap: RefMap,\n  options: {\n    createDocumentId?: CreateDocumentId;\n    stripKeyword?: string;\n  } = {},\n): DocumentNode {\n  const createDocumentId = options.createDocumentId ?? createId;\n\n  // A boolean schema (`true` / `false`) has no structure to model — preserve it.\n  if (typeof schema === \"boolean\") {\n    return { id: createDocumentId(), rest: {}, booleanSchema: schema };\n  }\n\n  const node: DocumentNode = { id: createDocumentId(), rest: {} };\n\n  // Record the source key order so the projection can replay it exactly,\n  // keeping round-trips byte-faithful (no $defs/keyword reshuffling on edit).\n  node.order = Object.keys(schema);\n\n  if (typeof schema.$ref === \"string\") {\n    const defId = refMap.get(schema.$ref);\n    if (defId) node.ref = defId;\n    else {\n      // unresolved pointer — keep verbatim\n      setRecordValue(node.rest, \"$ref\", schema.$ref);\n    }\n  }\n\n  if (schema.type !== undefined) node.type = schema.type;\n  if (schema.title !== undefined) node.title = schema.title;\n  if (schema.description !== undefined) node.description = schema.description;\n\n  if (Array.isArray(schema.enum)) {\n    node.enum = enumFromSchema(schema, createDocumentId);\n  }\n\n  const required = Array.isArray(schema.required) ? schema.required : [];\n  if (required.length > 0 || node.order?.includes(\"required\")) {\n    node.requiredOrder = required;\n  }\n\n  if (schema.properties) {\n    node.properties = Object.entries(schema.properties).map(\n      ([key, child]): PropertyEntry => ({\n        id: createDocumentId(\"prop\"),\n        key,\n        required: required.includes(key),\n        node: nodeFromSchema(child, refMap, { createDocumentId }),\n      }),\n    );\n  }\n\n  if (required.length > 0) {\n    const propertyKeys = new Set(\n      node.properties?.map((property) => property.key),\n    );\n    const extraRequired = required.filter((key) => !propertyKeys.has(key));\n    if (extraRequired.length > 0) node.extraRequired = extraRequired;\n  }\n\n  if (schema.items !== undefined) {\n    if (Array.isArray(schema.items)) {\n      // Tuple `items` (array form) is rare and not UI-editable; carry it\n      // verbatim in `rest` so it survives the round-trip losslessly.\n      setRecordValue(node.rest, \"items\", schema.items);\n    } else {\n      node.items = nodeFromSchema(schema.items, refMap, { createDocumentId });\n    }\n  }\n\n  for (const key of [\"anyOf\", \"oneOf\", \"allOf\"] as const) {\n    const value = schema[key];\n    if (Array.isArray(value)) {\n      node[key] = value.map((sub) =>\n        nodeFromSchema(sub, refMap, { createDocumentId }),\n      );\n    }\n  }\n\n  // Carry every keyword we don't model.\n  for (const [key, value] of Object.entries(schema)) {\n    if (MODELED_NODE_KEYS.has(key)) continue;\n    if (options.stripKeyword && key === options.stripKeyword) continue;\n    setRecordValue(node.rest, key, value);\n  }\n\n  return node;\n}\n\nfunction enumFromSchema(\n  schema: JSONSchema7,\n  createDocumentId: CreateDocumentId = createId,\n): EnumValue[] {\n  return (schema.enum ?? []).map(\n    (value): EnumValue => ({\n      id: createDocumentId(\"enum\"),\n      value: value as JsonValue,\n    }),\n  );\n}\n\nfunction createDeterministicImportIdFactory(): CreateDocumentId {\n  let nextId = 0;\n  return (prefix = \"node\") => {\n    nextId += 1;\n    return `${prefix}-import-${nextId}`;\n  };\n}\n\n// ---------------------------------------------------------------------------\n// Export: Document -> JSON Schema\n// ---------------------------------------------------------------------------\n\nexport function toJsonSchema(doc: SchemaDocument): JSONSchema7 {\n  const defsKeyword =\n    doc.rest.defsKeyword === \"definitions\" ? \"definitions\" : \"$defs\";\n  const defNameById = new Map<string, string>();\n  for (const def of doc.defs) defNameById.set(def.id, def.name);\n\n  const out = nodeToSchema(doc.root, defNameById, defsKeyword) as JSONSchema7;\n\n  if (doc.defs.length > 0) {\n    const bag: Record<string, JSONSchema7Definition> = {};\n    for (const def of doc.defs) {\n      setRecordValue(\n        bag,\n        def.name,\n        nodeToSchema(def.node, defNameById, defsKeyword),\n      );\n    }\n    setRecordValue(out as Record<string, unknown>, defsKeyword, bag);\n  }\n\n  // Replay the root key order (so $defs lands back where the source had it).\n  return applyKeyOrder(\n    out as Record<string, unknown>,\n    doc.root.order,\n  ) as JSONSchema7;\n}\n\n/**\n * Project a single node to JSON Schema (using the document's definition names for\n * any `$ref`s). The inverse of `nodeFromJson` — together they let a component read\n * and rewrite one node's JSON by id while the Document stays the source of truth.\n */\nexport function projectNode(\n  doc: SchemaDocument,\n  node: DocumentNode,\n): JSONSchema7Definition {\n  const defsKeyword =\n    doc.rest.defsKeyword === \"definitions\" ? \"definitions\" : \"$defs\";\n  const defNameById = new Map<string, string>();\n  for (const def of doc.defs) defNameById.set(def.id, def.name);\n  return nodeToSchema(node, defNameById, defsKeyword);\n}\n\n/** Convert a JSON Schema subtree into a Document node, resolving `$ref`s against\n *  the document's existing definitions (so refs survive the round-trip). */\nexport function nodeFromJson(\n  schema: JSONSchema7Definition,\n  doc: SchemaDocument,\n): DocumentNode {\n  const refMap: RefMap = new Map();\n  const defsKeyword =\n    doc.rest.defsKeyword === \"definitions\" ? \"definitions\" : \"$defs\";\n  const otherDefsKeyword = defsKeyword === \"$defs\" ? \"definitions\" : \"$defs\";\n  const hasOtherDefsKeyword = Object.prototype.hasOwnProperty.call(\n    doc.root.rest,\n    otherDefsKeyword,\n  );\n  for (const def of doc.defs) {\n    addDefinitionRefMapEntries(refMap, def, {\n      defsKeyword,\n      hasOtherDefsKeyword,\n    });\n  }\n  // Strip the document's primary defs keyword — definitions live at the document\n  // level, not on a node; this keeps a root-level edit (whose JSON still carries\n  // `$defs`) from duplicating them into the root node's `rest`.\n  return nodeFromSchema(schema, refMap, { stripKeyword: defsKeyword });\n}\n\nfunction addDefinitionRefMapEntries(\n  refMap: RefMap,\n  def: DefinitionEntry,\n  options: {\n    defsKeyword: \"$defs\" | \"definitions\";\n    hasOtherDefsKeyword: boolean;\n  },\n) {\n  for (const ref of definitionRefAliases(options.defsKeyword, def.name)) {\n    refMap.set(ref, def.id);\n  }\n  if (!options.hasOtherDefsKeyword) {\n    const otherKeyword =\n      options.defsKeyword === \"$defs\" ? \"definitions\" : \"$defs\";\n    for (const ref of definitionRefAliases(otherKeyword, def.name)) {\n      refMap.set(ref, def.id);\n    }\n  }\n}\n\nfunction nodeToSchema(\n  node: DocumentNode,\n  defNameById: Map<string, string>,\n  defsKeyword: DefinitionsKeyword,\n): JSONSchema7Definition {\n  if (node.booleanSchema !== undefined) {\n    return node.booleanSchema;\n  }\n\n  // Emit modeled keys first in a natural reading order ($ref, type, title, …),\n  // then any unmodeled keywords. Export is a projection, so it normalizes key\n  // order the way a formatter would — semantics are preserved, not byte layout.\n  const out: Record<string, unknown> = {};\n\n  if (node.ref) {\n    const name = defNameById.get(node.ref);\n    if (name) setRecordValue(out, \"$ref\", definitionRef(defsKeyword, name));\n  }\n\n  if (node.type !== undefined) setRecordValue(out, \"type\", node.type);\n  if (node.title !== undefined) setRecordValue(out, \"title\", node.title);\n  if (node.description !== undefined)\n    setRecordValue(out, \"description\", node.description);\n\n  if (node.enum) {\n    setRecordValue(\n      out,\n      \"enum\",\n      node.enum.map((entry) => entry.value),\n    );\n  }\n\n  if (node.properties) {\n    const properties: Record<string, JSONSchema7Definition> = {};\n    const required: string[] = [];\n    const seen = new Set<string>();\n    for (const entry of node.properties) {\n      const key = entry.key;\n      // Transient-invalid states live in the Document, not the projection:\n      // drop empty and duplicate keys at the boundary.\n      if ((entry.isTransient && !key) || seen.has(key)) continue;\n      seen.add(key);\n      setRecordValue(\n        properties,\n        key,\n        nodeToSchema(entry.node, defNameById, defsKeyword),\n      );\n      if (entry.required) required.push(key);\n    }\n    setRecordValue(out, \"properties\", properties);\n    // Emit `required` when it has entries, or when the source had an explicit\n    // (possibly empty) `required` key — so `required: []` round-trips faithfully.\n    const hadRequiredKey = node.order?.includes(\"required\") ?? false;\n    const projectedRequired = orderRequiredNames(\n      [...(node.extraRequired ?? []), ...required],\n      node.requiredOrder,\n    );\n    if (projectedRequired.length > 0 || hadRequiredKey) {\n      setRecordValue(out, \"required\", projectedRequired);\n    }\n  } else if (node.extraRequired?.length) {\n    setRecordValue(\n      out,\n      \"required\",\n      orderRequiredNames(node.extraRequired, node.requiredOrder),\n    );\n  }\n\n  if (node.items) {\n    setRecordValue(\n      out,\n      \"items\",\n      nodeToSchema(node.items, defNameById, defsKeyword),\n    );\n  }\n\n  for (const key of [\"anyOf\", \"oneOf\", \"allOf\"] as const) {\n    const value = node[key];\n    if (value) {\n      setRecordValue(\n        out,\n        key,\n        value.map((sub) => nodeToSchema(sub, defNameById, defsKeyword)),\n      );\n    }\n  }\n\n  // Trailing unmodeled keywords (const, default, format, pattern, x-*, …).\n  for (const [key, value] of Object.entries(node.rest)) {\n    if (hasOwn(out, key)) continue; // modeled field already won this key\n    setRecordValue(out, key, value);\n  }\n\n  return applyKeyOrder(out, node.order) as JSONSchema7;\n}\n\n/**\n * Reorder an object's keys to match a recorded source order. Keys present in\n * `order` come first (in that order); any keys not in it (newly added by edits)\n * are appended in their current order. Returns a new object.\n */\nfunction applyKeyOrder(\n  obj: Record<string, unknown>,\n  order: unknown,\n): Record<string, unknown> {\n  if (!Array.isArray(order)) return obj;\n  const result: Record<string, unknown> = {};\n  for (const key of order as string[]) {\n    if (hasOwn(obj, key)) setRecordValue(result, key, obj[key]);\n  }\n  for (const key of Object.keys(obj)) {\n    if (!hasOwn(result, key)) setRecordValue(result, key, obj[key]);\n  }\n  return result;\n}\n\nfunction hasOwn(record: Record<string, unknown>, key: string): boolean {\n  return Object.prototype.hasOwnProperty.call(record, key);\n}\n\nfunction setRecordValue<T>(record: Record<string, T>, key: string, value: T) {\n  Object.defineProperty(record, key, {\n    value,\n    enumerable: true,\n    configurable: true,\n    writable: true,\n  });\n}\n\nfunction orderRequiredNames(names: string[], sourceOrder: unknown): string[] {\n  const uniqueNames = [...new Set(names)];\n  if (!Array.isArray(sourceOrder)) return uniqueNames;\n\n  const remaining = new Set(uniqueNames);\n  const ordered: string[] = [];\n  for (const name of sourceOrder) {\n    if (typeof name !== \"string\" || !remaining.has(name)) continue;\n    ordered.push(name);\n    remaining.delete(name);\n  }\n  ordered.push(...remaining);\n  return ordered;\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/document/convert.ts"
    },
    {
      "path": "components/schema-editor/document/definition-operations.ts",
      "content": "import { mapPreserve } from \"@/components/schema-editor/document/array\";\nimport { createId } from \"@/components/schema-editor/document/id\";\nimport {\n  definitionRef,\n  definitionRefAliases,\n} from \"@/components/schema-editor/document/json-pointer\";\nimport { updateNode } from \"@/components/schema-editor/document/node-update\";\nimport {\n  createNode,\n  stripSchemaTypeSpecificRest,\n  updateEffectiveNodeShape,\n} from \"@/components/schema-editor/document/type-operations\";\nimport type {\n  DefinitionEntry,\n  DocumentNode,\n  SchemaDocument,\n} from \"@/components/schema-editor/document/types\";\n\nconst SCHEMA_VALUE_REST_KEYS = new Set([\n  \"additionalItems\",\n  \"additionalProperties\",\n  \"allOf\",\n  \"anyOf\",\n  \"contains\",\n  \"else\",\n  \"if\",\n  \"items\",\n  \"not\",\n  \"oneOf\",\n  \"prefixItems\",\n  \"propertyNames\",\n  \"then\",\n  \"unevaluatedItems\",\n  \"unevaluatedProperties\",\n]);\n\nconst SCHEMA_MAP_REST_KEYS = new Set([\n  \"$defs\",\n  \"definitions\",\n  \"dependentSchemas\",\n  \"dependencies\",\n  \"patternProperties\",\n  \"properties\",\n]);\n\nexport function addDefinition(\n  doc: SchemaDocument,\n  init: Partial<DefinitionEntry> = {},\n): { doc: SchemaDocument; defId: string } {\n  const baseName = init.name?.trim() || \"Definition\";\n  const entry: DefinitionEntry = {\n    id: init.id ?? createId(\"def\"),\n    name: uniqueDefinitionName(doc, baseName),\n    node: init.node ?? createNode(\"object\"),\n  };\n  return { doc: { ...doc, defs: [...doc.defs, entry] }, defId: entry.id };\n}\n\nexport function renameDefinition(\n  doc: SchemaDocument,\n  defId: string,\n  name: string,\n): SchemaDocument {\n  const currentDefinition = doc.defs.find(\n    (definition) => definition.id === defId,\n  );\n  if (!currentDefinition) return doc;\n\n  const nextName = name.trim();\n  if (!nextName) return doc;\n\n  const taken = new Set(\n    doc.defs\n      .filter((definition) => definition.id !== defId)\n      .map((definition) => definition.name),\n  );\n  let finalName = nextName;\n  if (taken.has(finalName)) {\n    let index = 2;\n    while (taken.has(`${nextName}${index}`)) index += 1;\n    finalName = `${nextName}${index}`;\n  }\n  const defs = mapPreserve(doc.defs, (definition) =>\n    definition.id === defId ? { ...definition, name: finalName } : definition,\n  );\n  if (defs === doc.defs) return doc;\n\n  return rewriteRawDefinitionRefs(\n    { ...doc, defs },\n    currentDefinition.name,\n    finalName,\n  );\n}\n\nexport function removeDefinition(\n  doc: SchemaDocument,\n  defId: string,\n): SchemaDocument {\n  const defs = doc.defs.filter((definition) => definition.id !== defId);\n  if (defs.length === doc.defs.length) return doc;\n  return { ...doc, defs };\n}\n\nexport function setRef(\n  doc: SchemaDocument,\n  id: string,\n  defId: string,\n): SchemaDocument {\n  return updateNode(doc, id, (node) => {\n    if (isTypeArrayNullable(node)) {\n      return {\n        ...node,\n        type: undefined,\n        properties: undefined,\n        items: undefined,\n        enum: undefined,\n        ref: undefined,\n        rest: stripSchemaTypeSpecificRest(node.rest),\n        order: undefined,\n        anyOf: [createRefNode(defId), createNode(\"null\")],\n      };\n    }\n\n    return updateEffectiveNodeShape(node, (effective) => ({\n      id: effective.id,\n      ref: defId,\n      title: effective.title,\n      description: effective.description,\n      rest: stripSchemaTypeSpecificRest(effective.rest),\n      order: effective.order,\n    }));\n  });\n}\n\nexport function setRefByName(\n  doc: SchemaDocument,\n  id: string,\n  name: string,\n): SchemaDocument {\n  const definition = doc.defs.find((def) => def.name === name);\n  return definition ? setRef(doc, id, definition.id) : doc;\n}\n\nfunction uniqueDefinitionName(doc: SchemaDocument, base: string): string {\n  const taken = new Set(doc.defs.map((definition) => definition.name));\n  if (!taken.has(base)) return base;\n  let index = 2;\n  while (taken.has(`${base}${index}`)) index += 1;\n  return `${base}${index}`;\n}\n\nfunction isTypeArrayNullable(node: DocumentNode): boolean {\n  return Array.isArray(node.type) && node.type.includes(\"null\");\n}\n\nfunction createRefNode(defId: string): DocumentNode {\n  return {\n    id: createId(),\n    ref: defId,\n    rest: {},\n  };\n}\n\nfunction rewriteRawDefinitionRefs(\n  doc: SchemaDocument,\n  oldName: string,\n  newName: string,\n): SchemaDocument {\n  if (oldName === newName) return doc;\n\n  const refs = getRewriteRefOptions(doc);\n  const root = rewriteRawDefinitionRefsInNode(doc.root, oldName, newName, refs);\n  const defs = mapPreserve(doc.defs, (definition) => {\n    const node = rewriteRawDefinitionRefsInNode(\n      definition.node,\n      oldName,\n      newName,\n      refs,\n    );\n    return node === definition.node ? definition : { ...definition, node };\n  });\n\n  if (root === doc.root && defs === doc.defs) return doc;\n  return { ...doc, root, defs };\n}\n\nfunction rewriteRawDefinitionRefsInNode(\n  node: DocumentNode,\n  oldName: string,\n  newName: string,\n  refs: RewriteRefOptions,\n): DocumentNode {\n  let next = node;\n\n  const rest = rewriteRawDefinitionRefsInRest(\n    node.rest,\n    oldName,\n    newName,\n    refs,\n  );\n  if (rest !== node.rest) next = { ...next, rest };\n\n  if (node.properties) {\n    const properties = mapPreserve(node.properties, (property) => {\n      const child = rewriteRawDefinitionRefsInNode(\n        property.node,\n        oldName,\n        newName,\n        refs,\n      );\n      return child === property.node ? property : { ...property, node: child };\n    });\n    if (properties !== node.properties) next = { ...next, properties };\n  }\n\n  if (node.items) {\n    const items = rewriteRawDefinitionRefsInNode(\n      node.items,\n      oldName,\n      newName,\n      refs,\n    );\n    if (items !== node.items) next = { ...next, items };\n  }\n\n  for (const key of [\"anyOf\", \"oneOf\", \"allOf\"] as const) {\n    const children = node[key];\n    if (!children) continue;\n    const mapped = mapPreserve(children, (child) =>\n      rewriteRawDefinitionRefsInNode(child, oldName, newName, refs),\n    );\n    if (mapped !== children) next = { ...next, [key]: mapped };\n  }\n\n  return next;\n}\n\nfunction rewriteRawDefinitionRefsInRest(\n  rest: Record<string, unknown>,\n  oldName: string,\n  newName: string,\n  refs: RewriteRefOptions,\n): Record<string, unknown> {\n  let next = rest;\n\n  for (const [key, value] of Object.entries(rest)) {\n    const rewritten = SCHEMA_MAP_REST_KEYS.has(key)\n      ? rewriteRefsInSchemaMap(value, oldName, newName, refs)\n      : SCHEMA_VALUE_REST_KEYS.has(key)\n        ? rewriteRefsInSchemaValue(value, oldName, newName, refs)\n        : value;\n\n    if (rewritten !== value) {\n      if (next === rest) next = { ...rest };\n      setRecordValue(next, key, rewritten);\n    }\n  }\n\n  return next;\n}\n\nfunction rewriteRefsInSchemaMap(\n  value: unknown,\n  oldName: string,\n  newName: string,\n  refs: RewriteRefOptions,\n): unknown {\n  if (!isPlainObject(value)) return value;\n\n  let next: Record<string, unknown> = value;\n  for (const [key, child] of Object.entries(value)) {\n    const rewritten = rewriteRefsInSchemaValue(child, oldName, newName, refs);\n    if (rewritten !== child) {\n      if (next === value) next = { ...value };\n      setRecordValue(next, key, rewritten);\n    }\n  }\n  return next;\n}\n\nfunction rewriteRefsInSchemaValue(\n  value: unknown,\n  oldName: string,\n  newName: string,\n  refs: RewriteRefOptions,\n): unknown {\n  if (Array.isArray(value)) {\n    return mapPreserve(value, (child) =>\n      rewriteRefsInSchemaValue(child, oldName, newName, refs),\n    );\n  }\n  if (!isPlainObject(value)) return value;\n\n  let next: Record<string, unknown> = value;\n  const rewrittenRef = rewriteDefinitionRef(value.$ref, oldName, newName, refs);\n  if (rewrittenRef !== value.$ref) {\n    next = { ...value, $ref: rewrittenRef };\n  }\n\n  for (const [key, child] of Object.entries(next)) {\n    let rewritten = child;\n    if (SCHEMA_MAP_REST_KEYS.has(key)) {\n      rewritten = rewriteRefsInSchemaMap(child, oldName, newName, refs);\n    } else if (SCHEMA_VALUE_REST_KEYS.has(key)) {\n      rewritten = rewriteRefsInSchemaValue(child, oldName, newName, refs);\n    }\n\n    if (rewritten !== child) {\n      if (next === value) next = { ...value };\n      setRecordValue(next, key, rewritten);\n    }\n  }\n\n  return next;\n}\n\nfunction rewriteDefinitionRef(\n  value: unknown,\n  oldName: string,\n  newName: string,\n  refs: RewriteRefOptions,\n): unknown {\n  if (\n    definitionRefAliases(refs.primaryKeyword, oldName).includes(value as string)\n  )\n    return definitionRef(refs.primaryKeyword, newName);\n  if (\n    refs.allowOtherKeywordAlias &&\n    definitionRefAliases(refs.otherKeyword, oldName).includes(value as string)\n  )\n    return definitionRef(refs.otherKeyword, newName);\n  return value;\n}\n\ninterface RewriteRefOptions {\n  primaryKeyword: \"$defs\" | \"definitions\";\n  otherKeyword: \"$defs\" | \"definitions\";\n  allowOtherKeywordAlias: boolean;\n}\n\nfunction getRewriteRefOptions(doc: SchemaDocument): RewriteRefOptions {\n  const primaryKeyword =\n    doc.rest.defsKeyword === \"definitions\" ? \"definitions\" : \"$defs\";\n  const otherKeyword = primaryKeyword === \"$defs\" ? \"definitions\" : \"$defs\";\n  return {\n    primaryKeyword,\n    otherKeyword,\n    allowOtherKeywordAlias: !Object.prototype.hasOwnProperty.call(\n      doc.root.rest,\n      otherKeyword,\n    ),\n  };\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n  return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction setRecordValue<T>(record: Record<string, T>, key: string, value: T) {\n  Object.defineProperty(record, key, {\n    value,\n    enumerable: true,\n    configurable: true,\n    writable: true,\n  });\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/document/definition-operations.ts"
    },
    {
      "path": "components/schema-editor/document/derive.ts",
      "content": "import type { JSONSchema7TypeName } from \"json-schema\";\n\nimport { definitionRefAliases } from \"@/components/schema-editor/document/json-pointer\";\nimport type {\n  DefinitionEntry,\n  DocumentNode,\n  SchemaDocument,\n  SchemaKind,\n} from \"./types\";\n\nconst SCHEMA_VALUE_REST_KEYS = new Set([\n  \"additionalItems\",\n  \"additionalProperties\",\n  \"allOf\",\n  \"anyOf\",\n  \"contains\",\n  \"else\",\n  \"if\",\n  \"items\",\n  \"not\",\n  \"oneOf\",\n  \"prefixItems\",\n  \"propertyNames\",\n  \"then\",\n  \"unevaluatedItems\",\n  \"unevaluatedProperties\",\n]);\n\nconst SCHEMA_MAP_REST_KEYS = new Set([\n  \"$defs\",\n  \"definitions\",\n  \"dependentSchemas\",\n  \"dependencies\",\n  \"patternProperties\",\n  \"properties\",\n]);\n\n/**\n * Pure projections of a node for rendering. NONE of these are stored — there is\n * one source of truth (the Document) and these are computed on each render. This\n * is what lets child components stay stateless the way extend's do, without a\n * second representation to keep in sync.\n */\n\n/** The effective UI \"kind\" of a node. */\nexport function getEffectiveKind(node: DocumentNode): SchemaKind {\n  if (node.ref) return \"ref\";\n  if (node.enum) return \"enum\";\n  if (node.anyOf || node.oneOf) return \"union\";\n\n  if (Array.isArray(node.type)) {\n    const real = node.type.filter((t) => t !== \"null\");\n    return real.length === 1 ? (real[0] as SchemaKind) : \"union\";\n  }\n  if (node.type) return node.type;\n  return \"any\";\n}\n\nexport function isNullable(node: DocumentNode): boolean {\n  if (Array.isArray(node.type)) return node.type.includes(\"null\");\n  if (node.type === \"null\") return true;\n  if (node.anyOf) return node.anyOf.some((sub) => sub.type === \"null\");\n  return false;\n}\n\n/** Base scalar type backing an enum (enums are `{ type, enum: [...] }`). */\nexport function getEnumBaseType(node: DocumentNode): JSONSchema7TypeName {\n  const type = Array.isArray(node.type)\n    ? node.type.find((t) => t !== \"null\")\n    : node.type;\n  return (type as JSONSchema7TypeName) ?? \"string\";\n}\n\n/** Resolve a `$ref` node to the definition it points at (by id). */\nexport function resolveRef(\n  doc: SchemaDocument,\n  node: DocumentNode,\n): DefinitionEntry | null {\n  if (!node.ref) return null;\n  return doc.defs.find((def) => def.id === node.ref) ?? null;\n}\n\n/** True when a `$ref` points at a definition that no longer exists. */\nexport function isDanglingRef(\n  doc: SchemaDocument,\n  node: DocumentNode,\n): boolean {\n  return Boolean(node.ref) && !resolveRef(doc, node);\n}\n\nexport function isDefinitionReferenced(\n  doc: SchemaDocument,\n  defId: string,\n  options: { exceptDefId?: string } = {},\n): boolean {\n  const referenced = doc.defs.find((definition) => definition.id === defId);\n  if (!referenced) return false;\n\n  const refs = getReferenceRefOptions(doc);\n\n  if (nodeReferencesDefinition(doc.root, referenced, refs)) return true;\n\n  for (const definition of doc.defs) {\n    if (definition.id === options.exceptDefId) continue;\n    if (nodeReferencesDefinition(definition.node, referenced, refs))\n      return true;\n  }\n\n  return false;\n}\n\nfunction nodeReferencesDefinition(\n  node: DocumentNode,\n  definition: DefinitionEntry,\n  refs: ReferenceRefOptions,\n): boolean {\n  if (node.ref === definition.id) return true;\n  if (restReferencesDefinition(node.rest, definition, refs)) return true;\n\n  if (node.properties) {\n    for (const property of node.properties) {\n      if (nodeReferencesDefinition(property.node, definition, refs))\n        return true;\n    }\n  }\n\n  if (node.items && nodeReferencesDefinition(node.items, definition, refs))\n    return true;\n\n  for (const key of [\"anyOf\", \"oneOf\", \"allOf\"] as const) {\n    const children = node[key];\n    if (!children) continue;\n    for (const child of children) {\n      if (nodeReferencesDefinition(child, definition, refs)) return true;\n    }\n  }\n\n  return false;\n}\n\nfunction restReferencesDefinition(\n  rest: Record<string, unknown>,\n  definition: DefinitionEntry,\n  refs: ReferenceRefOptions,\n): boolean {\n  for (const [key, value] of Object.entries(rest)) {\n    if (SCHEMA_MAP_REST_KEYS.has(key)) {\n      if (schemaMapReferencesDefinition(value, definition, refs)) return true;\n    } else if (SCHEMA_VALUE_REST_KEYS.has(key)) {\n      if (schemaReferencesDefinition(value, definition, refs)) return true;\n    }\n  }\n  return false;\n}\n\nfunction schemaMapReferencesDefinition(\n  value: unknown,\n  definition: DefinitionEntry,\n  refs: ReferenceRefOptions,\n): boolean {\n  if (!isPlainObject(value)) return false;\n  return Object.values(value).some((child) =>\n    schemaReferencesDefinition(child, definition, refs),\n  );\n}\n\nfunction schemaReferencesDefinition(\n  value: unknown,\n  definition: DefinitionEntry,\n  refs: ReferenceRefOptions,\n): boolean {\n  if (Array.isArray(value)) {\n    return value.some((child) =>\n      schemaReferencesDefinition(child, definition, refs),\n    );\n  }\n  if (!isPlainObject(value)) return false;\n\n  const ref = value.$ref;\n  if (\n    definitionRefAliases(refs.primaryKeyword, definition.name).includes(\n      ref as string,\n    )\n  )\n    return true;\n  if (\n    refs.allowOtherKeywordAlias &&\n    definitionRefAliases(refs.otherKeyword, definition.name).includes(\n      ref as string,\n    )\n  )\n    return true;\n\n  for (const [key, child] of Object.entries(value)) {\n    if (SCHEMA_MAP_REST_KEYS.has(key)) {\n      if (schemaMapReferencesDefinition(child, definition, refs)) return true;\n    } else if (SCHEMA_VALUE_REST_KEYS.has(key)) {\n      if (schemaReferencesDefinition(child, definition, refs)) return true;\n    }\n  }\n\n  return false;\n}\n\ninterface ReferenceRefOptions {\n  primaryKeyword: \"$defs\" | \"definitions\";\n  otherKeyword: \"$defs\" | \"definitions\";\n  allowOtherKeywordAlias: boolean;\n}\n\nfunction getReferenceRefOptions(doc: SchemaDocument): ReferenceRefOptions {\n  const primaryKeyword =\n    doc.rest.defsKeyword === \"definitions\" ? \"definitions\" : \"$defs\";\n  const otherKeyword = primaryKeyword === \"$defs\" ? \"definitions\" : \"$defs\";\n  return {\n    primaryKeyword,\n    otherKeyword,\n    allowOtherKeywordAlias: !Object.prototype.hasOwnProperty.call(\n      doc.root.rest,\n      otherKeyword,\n    ),\n  };\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n  return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/document/derive.ts"
    },
    {
      "path": "components/schema-editor/document/enum-operations.ts",
      "content": "import { mapPreserve } from \"@/components/schema-editor/document/array\";\nimport { getEffectiveDocNode } from \"@/components/schema-editor/document/node-selectors\";\nimport { updateNode } from \"@/components/schema-editor/document/node-update\";\nimport { getNode } from \"@/components/schema-editor/document/traversal\";\nimport {\n  createNode,\n  createEnumValue,\n  stripSchemaTypeSpecificRest,\n  updateEffectiveNodeShape,\n} from \"@/components/schema-editor/document/type-operations\";\nimport type {\n  DocumentNode,\n  EnumValue,\n  JsonValue,\n  SchemaDocument,\n} from \"@/components/schema-editor/document/types\";\n\nconst ENUM_DESCRIPTIONS_KEY = \"x-enumDescriptions\";\n\nexport function addEnumValue(\n  doc: SchemaDocument,\n  id: string,\n  value: JsonValue = \"\",\n): SchemaDocument {\n  return updateNode(doc, id, (node) => {\n    if (isTypeArrayNullable(node) && node.enum) {\n      const nextEnum = [...node.enum, { ...createEnumValue(), value }];\n      return createNullableEnumWrapper(\n        node,\n        nextEnum,\n        remapEnumDescriptions(node.rest, node.enum, nextEnum),\n      );\n    }\n\n    return updateEffectiveNodeShape(node, (effective) => {\n      const previousEnum = effective.enum ?? [];\n      const nextEnum = [...previousEnum, { ...createEnumValue(), value }];\n\n      return {\n        ...effective,\n        enum: nextEnum,\n        rest: remapEnumDescriptions(effective.rest, previousEnum, nextEnum),\n      };\n    });\n  });\n}\n\nexport function updateEnumValue(\n  doc: SchemaDocument,\n  id: string,\n  enumId: string,\n  patch: Partial<Omit<EnumValue, \"id\">>,\n): SchemaDocument {\n  return updateNode(doc, id, (node) => {\n    if (isTypeArrayNullable(node) && node.enum) {\n      const nextEnum = mapPreserve(node.enum, (value) =>\n        value.id === enumId ? { ...value, ...patch } : value,\n      );\n      return createNullableEnumWrapper(\n        node,\n        nextEnum,\n        remapEnumDescriptions(node.rest, node.enum, nextEnum),\n      );\n    }\n\n    return updateEffectiveNodeShape(node, (effective) => {\n      const previousEnum = effective.enum ?? [];\n      const nextEnum = mapPreserve(previousEnum, (value) =>\n        value.id === enumId ? { ...value, ...patch } : value,\n      );\n\n      return {\n        ...effective,\n        enum: nextEnum,\n        rest: remapEnumDescriptions(effective.rest, previousEnum, nextEnum),\n      };\n    });\n  });\n}\n\nexport function removeEnumValue(\n  doc: SchemaDocument,\n  id: string,\n  enumId: string,\n): SchemaDocument {\n  return updateNode(doc, id, (node) => {\n    if (isTypeArrayNullable(node) && node.enum) {\n      const nextEnum = node.enum.filter((value) => value.id !== enumId);\n      return createNullableEnumWrapper(\n        node,\n        nextEnum,\n        remapEnumDescriptions(node.rest, node.enum, nextEnum),\n      );\n    }\n\n    return updateEffectiveNodeShape(node, (effective) => {\n      const previousEnum = effective.enum ?? [];\n      const nextEnum = previousEnum.filter((value) => value.id !== enumId);\n\n      return {\n        ...effective,\n        enum: nextEnum,\n        rest: remapEnumDescriptions(effective.rest, previousEnum, nextEnum),\n      };\n    });\n  });\n}\n\nexport function setEnumValues(\n  doc: SchemaDocument,\n  id: string,\n  values: JsonValue[],\n): SchemaDocument {\n  return updateNode(doc, id, (node) => {\n    if (isTypeArrayNullable(node)) {\n      const nextEnum = buildEnumValues(values, node.enum);\n      return createNullableEnumWrapper(\n        node,\n        nextEnum,\n        remapEnumDescriptions(node.rest, node.enum, nextEnum),\n      );\n    }\n\n    return updateEffectiveNodeShape(node, (effective) => {\n      const nextEnum = buildEnumValues(values, effective.enum);\n      const rest = stripSchemaRestForEnum(\n        remapEnumDescriptions(effective.rest, effective.enum, nextEnum),\n      );\n      return {\n        ...effective,\n        type: \"string\",\n        enum: nextEnum,\n        rest,\n        booleanSchema: undefined,\n      };\n    });\n  });\n}\n\nexport function updateEnumValueAtIndex(\n  doc: SchemaDocument,\n  id: string,\n  index: number,\n  value: JsonValue,\n): SchemaDocument {\n  const node = getNode(doc, id);\n  if (!node) return doc;\n\n  const enumId = getEffectiveDocNode(node).enum?.[index]?.id;\n  return enumId ? updateEnumValue(doc, id, enumId, { value }) : doc;\n}\n\nexport function removeEnumValueAtIndex(\n  doc: SchemaDocument,\n  id: string,\n  index: number,\n): SchemaDocument {\n  const node = getNode(doc, id);\n  if (!node) return doc;\n\n  const enumId = getEffectiveDocNode(node).enum?.[index]?.id;\n  return enumId ? removeEnumValue(doc, id, enumId) : doc;\n}\n\nfunction buildEnumValues(\n  values: JsonValue[],\n  existing?: EnumValue[],\n): EnumValue[] {\n  return values.map((value, index) => ({\n    ...(existing?.[index] ?? createEnumValue()),\n    value,\n  }));\n}\n\nfunction isTypeArrayNullable(node: DocumentNode): boolean {\n  return Array.isArray(node.type) && node.type.includes(\"null\");\n}\n\nfunction createEnumNode(\n  values: JsonValue[],\n  existing?: EnumValue[],\n): DocumentNode {\n  return createEnumNodeFromEntries(buildEnumValues(values, existing));\n}\n\nfunction createEnumNodeFromEntries(enumEntries: EnumValue[]): DocumentNode {\n  const node = createNode(\"string\");\n  return {\n    ...node,\n    type: \"string\",\n    enum: enumEntries,\n  };\n}\n\nfunction createNullableEnumWrapper(\n  node: DocumentNode,\n  enumEntries: EnumValue[],\n  rest: Record<string, unknown> = node.rest,\n): DocumentNode {\n  return {\n    ...node,\n    type: undefined,\n    properties: undefined,\n    items: undefined,\n    enum: undefined,\n    ref: undefined,\n    rest: stripSchemaRestForEnum(rest),\n    order: undefined,\n    booleanSchema: undefined,\n    anyOf: [createEnumNodeFromEntries(enumEntries), { ...createNode(\"null\") }],\n  };\n}\n\nfunction stripSchemaRestForEnum(\n  rest: Record<string, unknown>,\n): Record<string, unknown> {\n  const descriptions = rest[ENUM_DESCRIPTIONS_KEY];\n  const next = { ...stripSchemaTypeSpecificRest(rest) };\n  if (isPlainRecord(descriptions)) {\n    next[ENUM_DESCRIPTIONS_KEY] = descriptions;\n  }\n  return next;\n}\n\nfunction remapEnumDescriptions(\n  rest: Record<string, unknown>,\n  previousEntries: EnumValue[] | undefined,\n  nextEntries: EnumValue[],\n): Record<string, unknown> {\n  const descriptions = rest[ENUM_DESCRIPTIONS_KEY];\n  if (!isPlainRecord(descriptions)) return rest;\n\n  const previousById = new Map(\n    previousEntries?.map((entry) => [entry.id, entry]) ?? [],\n  );\n  const nextDescriptions: Record<string, unknown> = {};\n  for (let index = 0; index < nextEntries.length; index += 1) {\n    const nextEntry = nextEntries[index];\n    const previous = previousById.get(nextEntry.id) ?? previousEntries?.[index];\n    if (!previous) continue;\n    const previousKey = enumDescriptionKey(previous.value);\n    if (!hasOwn(descriptions, previousKey)) continue;\n    nextDescriptions[enumDescriptionKey(nextEntry.value)] =\n      descriptions[previousKey];\n  }\n\n  const next = { ...rest };\n  if (Object.keys(nextDescriptions).length > 0) {\n    next[ENUM_DESCRIPTIONS_KEY] = nextDescriptions;\n  } else {\n    delete next[ENUM_DESCRIPTIONS_KEY];\n  }\n  return next;\n}\n\nfunction enumDescriptionKey(value: JsonValue): string {\n  return String(value);\n}\n\nfunction isPlainRecord(value: unknown): value is Record<string, unknown> {\n  return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction hasOwn(record: Record<string, unknown>, key: string): boolean {\n  return Object.prototype.hasOwnProperty.call(record, key);\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/document/enum-operations.ts"
    },
    {
      "path": "components/schema-editor/document/id.ts",
      "content": "import type { NodeId } from \"./types\";\n\n/**\n * Monotonic id minting. Ids are minted exactly once per node — at creation, or at\n * the import boundary when an external JSON Schema first becomes a Document — and\n * are stable for the node's lifetime thereafter.\n *\n * A module-level counter (rather than a random uuid) keeps ids short and stable\n * for snapshot tests; it is process-local, which is all the editor needs since\n * ids never have to be reconstructed from a serialized form (JSON Schema carries\n * none — that asymmetry is the point).\n */\nlet counter = 0;\n\nexport function createId(prefix = \"node\"): NodeId {\n  counter += 1;\n  return `${prefix}-${counter}`;\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/document/id.ts"
    },
    {
      "path": "components/schema-editor/document/json-node.ts",
      "content": "import type { JSONSchema7Definition } from \"json-schema\";\n\nimport {\n  nodeFromJson,\n  projectNode,\n} from \"@/components/schema-editor/document/convert\";\nimport { updateNode } from \"@/components/schema-editor/document/node-update\";\nimport { getNode } from \"@/components/schema-editor/document/traversal\";\nimport type { SchemaDocument } from \"@/components/schema-editor/document/types\";\n\nexport function getNodeJson(\n  doc: SchemaDocument,\n  id: string,\n): JSONSchema7Definition | null {\n  const node = getNode(doc, id);\n  return node ? projectNode(doc, node) : null;\n}\n\nexport function replaceNodeJson(\n  doc: SchemaDocument,\n  id: string,\n  jsonNode: JSONSchema7Definition,\n): SchemaDocument {\n  const converted = nodeFromJson(jsonNode, doc);\n  return updateNode(doc, id, (node) => ({\n    ...converted,\n    id: node.id,\n    order: node.order ?? converted.order,\n  }));\n}\n\nexport function updateNodeJson(\n  doc: SchemaDocument,\n  id: string,\n  transform: (json: JSONSchema7Definition) => JSONSchema7Definition,\n): SchemaDocument {\n  const json = getNodeJson(doc, id);\n  if (json === null) return doc;\n  return replaceNodeJson(doc, id, transform(json));\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/document/json-node.ts"
    },
    {
      "path": "components/schema-editor/document/json-pointer.ts",
      "content": "export type DefinitionsKeyword = \"$defs\" | \"definitions\";\n\nexport function escapeJsonPointerSegment(segment: string): string {\n  return segment.replace(/~/g, \"~0\").replace(/\\//g, \"~1\");\n}\n\nexport function unescapeJsonPointerSegment(segment: string): string {\n  return segment.replace(/~1/g, \"/\").replace(/~0/g, \"~\");\n}\n\nexport function decodeJsonPointerSegment(segment: string): string {\n  return unescapeJsonPointerSegment(decodeUriComponentSafe(segment));\n}\n\nexport function definitionRef(\n  keyword: DefinitionsKeyword,\n  name: string,\n): string {\n  return `#/${keyword}/${escapeJsonPointerSegment(name)}`;\n}\n\nexport function definitionRefAliases(\n  keyword: DefinitionsKeyword,\n  name: string,\n): string[] {\n  const pointerSegment = escapeJsonPointerSegment(name);\n  const encodedSegment = encodeURIComponent(pointerSegment);\n  const encodedNameSegment = encodeURIComponent(name);\n  const encodedNameSegmentWithTilde = encodedNameSegment.replace(/~/g, \"%7E\");\n  const lowerHexEncodedSegment = encodedSegment.replace(\n    /%[0-9A-F]{2}/g,\n    (escapeSequence) => escapeSequence.toLowerCase(),\n  );\n  const lowerHexEncodedNameSegment = encodedNameSegmentWithTilde.replace(\n    /%[0-9A-F]{2}/g,\n    (escapeSequence) => escapeSequence.toLowerCase(),\n  );\n  return [\n    ...new Set([\n      `#/${keyword}/${pointerSegment}`,\n      `#/${keyword}/${encodedSegment}`,\n      `#/${keyword}/${lowerHexEncodedSegment}`,\n      ...encodedOriginalNameAliases(keyword, encodedNameSegment),\n      ...encodedOriginalNameAliases(keyword, encodedNameSegmentWithTilde),\n      ...encodedOriginalNameAliases(keyword, lowerHexEncodedNameSegment),\n    ]),\n  ];\n}\n\nfunction encodedOriginalNameAliases(\n  keyword: DefinitionsKeyword,\n  encodedSegment: string,\n): string[] {\n  return encodedSegment.includes(\"%\") ? [`#/${keyword}/${encodedSegment}`] : [];\n}\n\nexport function definitionNameFromRef(ref: string): string {\n  const prefix = ref.startsWith(\"#/$defs/\")\n    ? \"#/$defs/\"\n    : ref.startsWith(\"#/definitions/\")\n      ? \"#/definitions/\"\n      : undefined;\n  return prefix ? decodeJsonPointerSegment(ref.slice(prefix.length)) : ref;\n}\n\nfunction decodeUriComponentSafe(value: string): string {\n  try {\n    return decodeURIComponent(value);\n  } catch {\n    return value;\n  }\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/document/json-pointer.ts"
    },
    {
      "path": "components/schema-editor/document/node-metadata.ts",
      "content": "import { mapPreserve } from \"@/components/schema-editor/document/array\";\nimport { updateNode } from \"@/components/schema-editor/document/node-update\";\nimport type {\n  DocumentNode,\n  SchemaDocument,\n} from \"@/components/schema-editor/document/types\";\n\nconst SCHEMA_VALUE_REST_KEYS = new Set([\n  \"additionalItems\",\n  \"additionalProperties\",\n  \"allOf\",\n  \"anyOf\",\n  \"contains\",\n  \"else\",\n  \"if\",\n  \"items\",\n  \"not\",\n  \"oneOf\",\n  \"prefixItems\",\n  \"propertyNames\",\n  \"then\",\n  \"unevaluatedItems\",\n  \"unevaluatedProperties\",\n]);\n\nconst SCHEMA_MAP_REST_KEYS = new Set([\n  \"$defs\",\n  \"definitions\",\n  \"dependentSchemas\",\n  \"dependencies\",\n  \"patternProperties\",\n  \"properties\",\n]);\n\nexport function setNodeDescription(\n  doc: SchemaDocument,\n  id: string,\n  description: string | undefined,\n): SchemaDocument {\n  return updateNode(doc, id, (node) => {\n    const nextDescription = description?.trim() ? description : undefined;\n    return node.description === nextDescription\n      ? node\n      : { ...node, description: nextDescription };\n  });\n}\n\nexport function setNodeTitle(\n  doc: SchemaDocument,\n  id: string,\n  title: string | undefined,\n): SchemaDocument {\n  return updateNode(doc, id, (node) => {\n    const nextTitle = title?.trim() ? title : undefined;\n    return node.title === nextTitle ? node : { ...node, title: nextTitle };\n  });\n}\n\nexport function stripDescriptions(doc: SchemaDocument): SchemaDocument {\n  const root = stripNodeDescription(doc.root);\n  const defs = mapPreserve(doc.defs, (definition) => {\n    const node = stripNodeDescription(definition.node);\n    return node === definition.node ? definition : { ...definition, node };\n  });\n  if (root === doc.root && defs === doc.defs) return doc;\n  return { ...doc, root, defs };\n}\n\nfunction stripNodeDescription(node: DocumentNode): DocumentNode {\n  let next =\n    node.description === undefined ? node : { ...node, description: undefined };\n\n  if (next.properties) {\n    const properties = mapPreserve(next.properties, (property) => {\n      const childNode = stripNodeDescription(property.node);\n      return childNode === property.node\n        ? property\n        : { ...property, node: childNode };\n    });\n    if (properties !== next.properties) next = { ...next, properties };\n  }\n\n  const rest = stripRestDescriptionKeywords(next.rest);\n  if (rest !== next.rest) next = { ...next, rest };\n\n  if (next.items) {\n    const items = stripNodeDescription(next.items);\n    if (items !== next.items) next = { ...next, items };\n  }\n\n  for (const key of [\"anyOf\", \"oneOf\", \"allOf\"] as const) {\n    const children = next[key];\n    if (!children) continue;\n    const mapped = mapPreserve(children, stripNodeDescription);\n    if (mapped !== children) next = { ...next, [key]: mapped };\n  }\n\n  return next;\n}\n\nfunction stripRestDescriptionKeywords(\n  rest: Record<string, unknown>,\n): Record<string, unknown> {\n  let next = rest;\n\n  for (const [key, value] of Object.entries(rest)) {\n    const stripped = SCHEMA_MAP_REST_KEYS.has(key)\n      ? stripSchemaMapDescriptions(value)\n      : SCHEMA_VALUE_REST_KEYS.has(key)\n        ? stripSchemaDescription(value)\n        : value;\n\n    if (stripped !== value) {\n      if (next === rest) next = { ...rest };\n      setRecordValue(next, key, stripped);\n    }\n  }\n\n  return next;\n}\n\nfunction stripSchemaMapDescriptions(value: unknown): unknown {\n  if (!isPlainObject(value)) return value;\n\n  let next: Record<string, unknown> = value;\n  for (const [key, child] of Object.entries(value)) {\n    const stripped = stripSchemaDescription(child);\n    if (stripped !== child) {\n      if (next === value) next = { ...value };\n      setRecordValue(next, key, stripped);\n    }\n  }\n\n  return next;\n}\n\nfunction stripSchemaDescription(value: unknown): unknown {\n  if (Array.isArray(value)) {\n    return mapPreserve(value, stripSchemaDescription);\n  }\n  if (!isPlainObject(value)) return value;\n\n  let next: Record<string, unknown> = value;\n  if (Object.prototype.hasOwnProperty.call(value, \"description\")) {\n    const { description: _description, ...rest } = value;\n    next = rest;\n  }\n\n  for (const [key, child] of Object.entries(next)) {\n    let stripped = child;\n    if (SCHEMA_MAP_REST_KEYS.has(key)) {\n      stripped = stripSchemaMapDescriptions(child);\n    } else if (SCHEMA_VALUE_REST_KEYS.has(key)) {\n      stripped = stripSchemaDescription(child);\n    }\n\n    if (stripped !== child) {\n      if (next === value) next = { ...value };\n      setRecordValue(next, key, stripped);\n    }\n  }\n\n  return next;\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n  return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction setRecordValue<T>(record: Record<string, T>, key: string, value: T) {\n  Object.defineProperty(record, key, {\n    value,\n    enumerable: true,\n    configurable: true,\n    writable: true,\n  });\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/document/node-metadata.ts"
    },
    {
      "path": "components/schema-editor/document/node-selectors.ts",
      "content": "import { getNode } from \"@/components/schema-editor/document/traversal\";\nimport type {\n  DocumentNode,\n  PropertyEntry,\n  SchemaDocument,\n} from \"@/components/schema-editor/document/types\";\n\nexport function getEffectiveDocNode(node: DocumentNode): DocumentNode {\n  if (node.anyOf) {\n    const nonNull = node.anyOf.find(\n      (branch) => branch.type !== \"null\" || branch.ref,\n    );\n    if (nonNull) return nonNull;\n  }\n  return node;\n}\n\nexport function getChildPropertyId(\n  doc: SchemaDocument,\n  parentId: string,\n  key: string,\n): string | undefined {\n  const parent = getNode(doc, parentId);\n  if (!parent) return undefined;\n  return getEffectiveDocNode(parent).properties?.find(\n    (entry) => entry.key === key,\n  )?.id;\n}\n\nexport function getChildNodeId(\n  doc: SchemaDocument,\n  parentId: string,\n  key: string,\n): string | undefined {\n  const parent = getNode(doc, parentId);\n  if (!parent) return undefined;\n  return getEffectiveDocNode(parent).properties?.find(\n    (entry) => entry.key === key,\n  )?.node.id;\n}\n\nexport function getItemsNodeId(\n  doc: SchemaDocument,\n  parentId: string,\n): string | undefined {\n  const parent = getNode(doc, parentId);\n  if (!parent) return undefined;\n  return getEffectiveDocNode(parent).items?.id;\n}\n\nexport function getOwnProperty(\n  doc: SchemaDocument,\n  parentId: string,\n  index: number,\n): PropertyEntry | null {\n  const parent = getNode(doc, parentId);\n  return parent\n    ? (getEffectiveDocNode(parent).properties?.[index] ?? null)\n    : null;\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/document/node-selectors.ts"
    },
    {
      "path": "components/schema-editor/document/node-update.ts",
      "content": "import { mapPreserve } from \"@/components/schema-editor/document/array\";\nimport type {\n  DocumentNode,\n  SchemaDocument,\n} from \"@/components/schema-editor/document/types\";\n\nexport function updateNode(\n  doc: SchemaDocument,\n  id: string,\n  fn: (node: DocumentNode) => DocumentNode,\n): SchemaDocument {\n  const root = replaceInNode(doc.root, id, fn);\n  const defs = mapPreserve(doc.defs, (definition) => {\n    const node = replaceInNode(definition.node, id, fn);\n    return node === definition.node ? definition : { ...definition, node };\n  });\n  if (root === doc.root && defs === doc.defs) return doc;\n  return { ...doc, root, defs };\n}\n\nfunction replaceInNode(\n  node: DocumentNode,\n  id: string,\n  fn: (node: DocumentNode) => DocumentNode,\n): DocumentNode {\n  if (node.id === id) return fn(node);\n\n  let next = node;\n\n  if (node.properties) {\n    const properties = mapPreserve(node.properties, (entry) => {\n      const child = replaceInNode(entry.node, id, fn);\n      return child === entry.node ? entry : { ...entry, node: child };\n    });\n    if (properties !== node.properties) next = { ...next, properties };\n  }\n\n  if (node.items) {\n    const items = replaceInNode(node.items, id, fn);\n    if (items !== node.items) next = { ...next, items };\n  }\n\n  for (const key of [\"anyOf\", \"oneOf\", \"allOf\"] as const) {\n    const list = node[key];\n    if (!list) continue;\n    const mapped = mapPreserve(list, (child) => replaceInNode(child, id, fn));\n    if (mapped !== list) next = { ...next, [key]: mapped };\n  }\n\n  return next;\n}\n\nexport function updateNodeRest(\n  doc: SchemaDocument,\n  id: string,\n  patch: Record<string, unknown>,\n): SchemaDocument {\n  return updateNode(doc, id, (node) => ({\n    ...node,\n    rest: { ...node.rest, ...patch },\n  }));\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/document/node-update.ts"
    },
    {
      "path": "components/schema-editor/document/property-operations.ts",
      "content": "import { mapPreserve } from \"@/components/schema-editor/document/array\";\nimport { createId } from \"@/components/schema-editor/document/id\";\nimport {\n  getEffectiveDocNode,\n  getOwnProperty,\n} from \"@/components/schema-editor/document/node-selectors\";\nimport { updateNode } from \"@/components/schema-editor/document/node-update\";\nimport {\n  childNodes,\n  getNode,\n} from \"@/components/schema-editor/document/traversal\";\nimport { createNode } from \"@/components/schema-editor/document/type-operations\";\nimport type {\n  DocumentNode,\n  PropertyEntry,\n  SchemaDocument,\n} from \"@/components/schema-editor/document/types\";\n\nfunction updateObjectProperties(\n  doc: SchemaDocument,\n  parentId: string,\n  fn: (properties: PropertyEntry[]) => PropertyEntry[],\n): SchemaDocument {\n  return updateNode(doc, parentId, (node) => {\n    if (isSingleNullableBranchContainer(node)) {\n      const branch = getEffectiveDocNode(node);\n      if (branch.type !== \"object\" && !branch.properties) return node;\n      return {\n        ...node,\n        anyOf: node.anyOf!.map((child) =>\n          child.id === branch.id\n            ? updateNodeProperties(child, fn(child.properties ?? []))\n            : child,\n        ),\n      };\n    }\n\n    if (node.type !== \"object\" && !node.properties) return node;\n    return updateNodeProperties(node, fn(node.properties ?? []));\n  });\n}\n\nfunction updateNodeProperties(\n  node: DocumentNode,\n  properties: PropertyEntry[],\n): DocumentNode {\n  return {\n    ...node,\n    properties,\n    requiredOrder: getRequiredOrder(node, properties),\n  };\n}\n\nfunction getRequiredOrder(\n  node: DocumentNode,\n  properties: PropertyEntry[],\n): string[] | undefined {\n  const requiredProperties = properties\n    .filter((property) => property.required && isProjectableProperty(property))\n    .map((property) => property.key);\n  const extraRequired = node.extraRequired ?? [];\n\n  if (requiredProperties.length === 0 && extraRequired.length === 0) {\n    return node.requiredOrder?.length ? [] : undefined;\n  }\n\n  const orderedExtraRequired = node.requiredOrder\n    ? node.requiredOrder.filter((name) => extraRequired.includes(name))\n    : extraRequired;\n\n  return [...orderedExtraRequired, ...requiredProperties];\n}\n\nexport function findOwningProperty(\n  doc: SchemaDocument,\n  propertyId: string,\n): { parentId: string; index: number } | null {\n  let result: { parentId: string; index: number } | null = null;\n  const visit = (node: DocumentNode) => {\n    if (result) return;\n    if (node.properties) {\n      const index = node.properties.findIndex(\n        (property) => property.id === propertyId,\n      );\n      if (index >= 0) {\n        result = { parentId: node.id, index };\n        return;\n      }\n    }\n    for (const child of childNodes(node)) visit(child);\n  };\n  visit(doc.root);\n  for (const definition of doc.defs) visit(definition.node);\n  return result;\n}\n\nexport function addProperty(\n  doc: SchemaDocument,\n  parentId: string,\n  init: Partial<PropertyEntry> = {},\n): SchemaDocument {\n  const entry: PropertyEntry = {\n    id: init.id ?? createId(\"prop\"),\n    key: init.key ?? \"\",\n    isTransient: init.isTransient ?? (init.key ? undefined : true),\n    required: init.required ?? false,\n    node: init.node ?? createNode(\"string\"),\n  };\n  return updateObjectProperties(doc, parentId, (properties) => [\n    ...properties,\n    entry,\n  ]);\n}\n\nexport function removeProperty(\n  doc: SchemaDocument,\n  propertyId: string,\n): SchemaDocument {\n  const owner = findOwningProperty(doc, propertyId);\n  if (!owner) return doc;\n  return updateObjectProperties(doc, owner.parentId, (properties) =>\n    properties.filter((property) => property.id !== propertyId),\n  );\n}\n\nexport function renameProperty(\n  doc: SchemaDocument,\n  propertyId: string,\n  key: string,\n): SchemaDocument {\n  return updateOwningEntry(doc, propertyId, (entry) =>\n    entry.key === key\n      ? entry\n      : { ...entry, key, isTransient: key === \"\" ? true : undefined },\n  );\n}\n\nexport function setRequired(\n  doc: SchemaDocument,\n  propertyId: string,\n  required: boolean,\n): SchemaDocument {\n  return updateOwningEntry(doc, propertyId, (entry) =>\n    entry.required === required ? entry : { ...entry, required },\n  );\n}\n\nfunction updateOwningEntry(\n  doc: SchemaDocument,\n  propertyId: string,\n  fn: (entry: PropertyEntry) => PropertyEntry,\n): SchemaDocument {\n  const owner = findOwningProperty(doc, propertyId);\n  if (!owner) return doc;\n  return updateObjectProperties(doc, owner.parentId, (properties) =>\n    mapPreserve(properties, (entry) =>\n      entry.id === propertyId ? fn(entry) : entry,\n    ),\n  );\n}\n\nexport function moveProperty(\n  doc: SchemaDocument,\n  propertyId: string,\n  targetParentId: string,\n  index: number,\n): SchemaDocument {\n  const owner = findOwningProperty(doc, propertyId);\n  if (!owner) return doc;\n  const moved = getOwnProperty(doc, owner.parentId, owner.index);\n  if (!moved) return doc;\n  const targetParent = getNode(doc, targetParentId);\n  if (!targetParent) return doc;\n  const targetEffectiveParent = getEffectiveDocNode(targetParent);\n  if (\n    targetEffectiveParent.type !== \"object\" &&\n    !targetEffectiveParent.properties\n  )\n    return doc;\n\n  if (isAncestor(doc, moved.node.id, targetParentId)) return doc;\n\n  let next = updateObjectProperties(doc, owner.parentId, (properties) =>\n    properties.filter((property) => property.id !== propertyId),\n  );\n  next = updateObjectProperties(next, targetParentId, (properties) => {\n    const clamped = Math.max(0, Math.min(index, properties.length));\n    const out = properties.slice();\n    out.splice(clamped, 0, moved);\n    return out;\n  });\n  return next;\n}\n\nfunction isSingleNullableBranchContainer(node: DocumentNode): boolean {\n  if (!node.anyOf) return false;\n  const nonNullBranches = node.anyOf.filter(\n    (branch) => branch.type !== \"null\" || branch.ref,\n  );\n  return (\n    nonNullBranches.length === 1 &&\n    node.anyOf.length === 2 &&\n    node.anyOf.some((branch) => branch.type === \"null\" && !branch.ref)\n  );\n}\n\nfunction isProjectableProperty(property: PropertyEntry): boolean {\n  return property.key !== \"\" || !property.isTransient;\n}\n\nfunction isAncestor(\n  doc: SchemaDocument,\n  nodeId: string,\n  maybeDescendantId: string,\n): boolean {\n  const node = getNode(doc, nodeId);\n  if (!node) return false;\n  if (node.id === maybeDescendantId) return true;\n  return childNodes(node).some((child) =>\n    isAncestor(doc, child.id, maybeDescendantId),\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/document/property-operations.ts"
    },
    {
      "path": "components/schema-editor/document/traversal.ts",
      "content": "import type {\n  DefinitionEntry,\n  DocumentNode,\n  SchemaDocument,\n} from \"@/components/schema-editor/document/types\";\n\nexport function childNodes(node: DocumentNode): DocumentNode[] {\n  const out: DocumentNode[] = [];\n  if (node.properties)\n    for (const property of node.properties) out.push(property.node);\n  if (node.items) out.push(node.items);\n  if (node.anyOf) out.push(...node.anyOf);\n  if (node.oneOf) out.push(...node.oneOf);\n  if (node.allOf) out.push(...node.allOf);\n  return out;\n}\n\nexport function getNode(doc: SchemaDocument, id: string): DocumentNode | null {\n  return findInNode(doc.root, id) ?? findInDefinitions(doc.defs, id);\n}\n\nfunction findInDefinitions(\n  definitions: DefinitionEntry[],\n  id: string,\n): DocumentNode | null {\n  for (const definition of definitions) {\n    const found = findInNode(definition.node, id);\n    if (found) return found;\n  }\n  return null;\n}\n\nfunction findInNode(node: DocumentNode, id: string): DocumentNode | null {\n  if (node.id === id) return node;\n  for (const child of childNodes(node)) {\n    const found = findInNode(child, id);\n    if (found) return found;\n  }\n  return null;\n}\n\nexport function findNodeByPath(\n  doc: SchemaDocument,\n  path: string | readonly string[],\n): string | null {\n  const segments =\n    typeof path === \"string\" ? path.split(\".\").filter(Boolean) : path;\n  let node: DocumentNode | undefined = doc.root;\n  for (const segment of segments) {\n    node = unwrapContainer(doc, node);\n    const entry = node?.properties?.find(\n      (property) => property.key === segment,\n    );\n    if (!entry) return null;\n    node = entry.node;\n  }\n  return node?.id ?? null;\n}\n\nfunction unwrapContainer(\n  doc: SchemaDocument,\n  node: DocumentNode | undefined,\n): DocumentNode | undefined {\n  let current = node;\n  const visited = new Set<string>();\n  while (current) {\n    current = getEffectiveContainerNode(current);\n    if (visited.has(current.id)) return undefined;\n    visited.add(current.id);\n    if (current.ref) {\n      current = doc.defs.find(\n        (definition) => definition.id === current!.ref,\n      )?.node;\n      continue;\n    }\n    if (current.items && !current.properties) {\n      current = current.items;\n      continue;\n    }\n    break;\n  }\n  return current;\n}\n\nfunction getEffectiveContainerNode(node: DocumentNode): DocumentNode {\n  if (node.anyOf) {\n    const nonNull = node.anyOf.find(\n      (branch) => branch.type !== \"null\" || branch.ref,\n    );\n    if (nonNull) return nonNull;\n  }\n  return node;\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/document/traversal.ts"
    },
    {
      "path": "components/schema-editor/document/type-operations.ts",
      "content": "import type { JSONSchema7TypeName } from \"json-schema\";\n\nimport { mapPreserve } from \"@/components/schema-editor/document/array\";\nimport { createId } from \"@/components/schema-editor/document/id\";\nimport { updateNode } from \"@/components/schema-editor/document/node-update\";\nimport type {\n  DocumentNode,\n  EnumValue,\n  SchemaDocument,\n} from \"@/components/schema-editor/document/types\";\n\nexport type SchemaEditorType =\n  | JSONSchema7TypeName\n  | \"enum\"\n  | \"date\"\n  | \"time\"\n  | \"datetime\";\n\nconst STRING_REST_KEYS = new Set([\n  \"contentEncoding\",\n  \"contentMediaType\",\n  \"contentSchema\",\n  \"format\",\n  \"maxLength\",\n  \"minLength\",\n  \"pattern\",\n]);\n\nconst NUMBER_REST_KEYS = new Set([\n  \"exclusiveMaximum\",\n  \"exclusiveMinimum\",\n  \"maximum\",\n  \"minimum\",\n  \"multipleOf\",\n]);\n\nconst ARRAY_REST_KEYS = new Set([\n  \"additionalItems\",\n  \"contains\",\n  \"items\",\n  \"maxContains\",\n  \"maxItems\",\n  \"minContains\",\n  \"minItems\",\n  \"prefixItems\",\n  \"unevaluatedItems\",\n  \"uniqueItems\",\n]);\n\nconst OBJECT_REST_KEYS = new Set([\n  \"additionalProperties\",\n  \"dependentRequired\",\n  \"dependentSchemas\",\n  \"dependencies\",\n  \"maxProperties\",\n  \"minProperties\",\n  \"patternProperties\",\n  \"propertyNames\",\n  \"unevaluatedProperties\",\n]);\n\nconst ENUM_REST_KEYS = new Set([\"x-enumDescriptions\"]);\n\nconst ENUM_ALLOWED_REST_KEYS = new Set([\n  ...STRING_REST_KEYS,\n  ...ENUM_REST_KEYS,\n]);\n\nconst TYPE_SPECIFIC_REST_KEYS = new Set([\n  ...STRING_REST_KEYS,\n  ...NUMBER_REST_KEYS,\n  ...ARRAY_REST_KEYS,\n  ...OBJECT_REST_KEYS,\n  ...ENUM_REST_KEYS,\n]);\n\nexport function setNodeType(\n  doc: SchemaDocument,\n  id: string,\n  type: JSONSchema7TypeName | \"enum\",\n): SchemaDocument {\n  return updateNode(doc, id, (node) => normalizeNodeForType(node, type));\n}\n\nexport function setNodeEditorType(\n  doc: SchemaDocument,\n  id: string,\n  type: SchemaEditorType,\n): SchemaDocument {\n  return updateNode(doc, id, (node) =>\n    updateEffectiveNodeShape(node, (effective) =>\n      normalizeNodeForEditorType(effective, type),\n    ),\n  );\n}\n\nexport function normalizeNodeForType(\n  node: DocumentNode,\n  type: JSONSchema7TypeName | \"enum\",\n): DocumentNode {\n  const nullable = isNodeNullable(node);\n  const base: DocumentNode = {\n    ...node,\n    rest: stripSchemaRestForType(node.rest, type),\n    ref: undefined,\n    anyOf: undefined,\n    oneOf: undefined,\n    allOf: undefined,\n    properties: undefined,\n    extraRequired: undefined,\n    requiredOrder: undefined,\n    items: undefined,\n    enum: undefined,\n    booleanSchema: undefined,\n  };\n\n  if (type === \"enum\") {\n    base.type = \"string\";\n    base.enum = node.enum?.length ? node.enum : [createEnumValue()];\n  } else if (type === \"object\") {\n    base.type = \"object\";\n    base.extraRequired = node.extraRequired;\n    base.requiredOrder = node.requiredOrder;\n    base.properties = node.properties?.length\n      ? node.properties\n      : [\n          {\n            id: createId(\"prop\"),\n            key: \"\",\n            // Starter row the user has yet to name: mark it transient so the\n            // empty key is dropped at the projection boundary (matching\n            // `addProperty`) instead of leaking as a property named \"\".\n            isTransient: true,\n            required: false,\n            node: createNode(\"string\"),\n          },\n        ];\n  } else if (type === \"array\") {\n    base.type = \"array\";\n    base.items = node.items ?? createNode(\"string\");\n  } else {\n    base.type = type;\n  }\n\n  return nullable ? setNodeNullable(base, true) : base;\n}\n\nfunction normalizeNodeForEditorType(\n  node: DocumentNode,\n  type: SchemaEditorType,\n): DocumentNode {\n  const format =\n    type === \"date\"\n      ? \"date\"\n      : type === \"time\"\n        ? \"time\"\n        : type === \"datetime\"\n          ? \"date-time\"\n          : undefined;\n  const schemaType: JSONSchema7TypeName | \"enum\" = format\n    ? \"string\"\n    : (type as JSONSchema7TypeName | \"enum\");\n  const normalized = normalizeNodeForType(node, schemaType);\n\n  return {\n    ...normalized,\n    rest: format\n      ? { ...stripSchemaFormat(normalized.rest), format }\n      : stripSchemaFormat(normalized.rest),\n  };\n}\n\nexport function stripSchemaFormat(\n  rest: Record<string, unknown>,\n): Record<string, unknown> {\n  if (!Object.prototype.hasOwnProperty.call(rest, \"format\")) return rest;\n  const { format: _format, ...withoutFormat } = rest;\n  return withoutFormat;\n}\n\nexport function stripSchemaTypeSpecificRest(\n  rest: Record<string, unknown>,\n): Record<string, unknown> {\n  return filterSchemaRest(rest, undefined);\n}\n\nfunction stripSchemaRestForType(\n  rest: Record<string, unknown>,\n  type: JSONSchema7TypeName | \"enum\",\n): Record<string, unknown> {\n  return filterSchemaRest(rest, getRestKeysForType(type));\n}\n\nfunction getRestKeysForType(\n  type: JSONSchema7TypeName | \"enum\",\n): Set<string> | undefined {\n  if (type === \"string\") return STRING_REST_KEYS;\n  if (type === \"enum\") return ENUM_ALLOWED_REST_KEYS;\n  if (type === \"number\" || type === \"integer\") return NUMBER_REST_KEYS;\n  if (type === \"array\") return ARRAY_REST_KEYS;\n  if (type === \"object\") return OBJECT_REST_KEYS;\n  return undefined;\n}\n\nfunction filterSchemaRest(\n  rest: Record<string, unknown>,\n  allowedTypeSpecificKeys: Set<string> | undefined,\n): Record<string, unknown> {\n  let next = rest;\n\n  for (const key of Object.keys(rest)) {\n    if (\n      TYPE_SPECIFIC_REST_KEYS.has(key) &&\n      !allowedTypeSpecificKeys?.has(key)\n    ) {\n      if (next === rest) next = { ...rest };\n      delete next[key];\n    }\n  }\n\n  return next;\n}\n\nexport function setNullable(\n  doc: SchemaDocument,\n  id: string,\n  nullable: boolean,\n): SchemaDocument {\n  return updateNode(doc, id, (node) => setNodeNullable(node, nullable));\n}\n\nfunction setNodeNullable(node: DocumentNode, nullable: boolean): DocumentNode {\n  if (node.anyOf) {\n    return setAnyOfNodeNullable(node, nullable);\n  }\n\n  const current = node.type;\n  const names = Array.isArray(current)\n    ? current.filter((type) => type !== \"null\")\n    : current\n      ? [current]\n      : [];\n\n  if (nullable) {\n    if (node.enum) return wrapNodeInNullableAnyOf(node);\n    if (names.length === 0) {\n      return node.ref ? wrapNodeInNullableAnyOf(node) : node;\n    }\n    return { ...node, type: [...names, \"null\"] };\n  }\n\n  if (names.length <= 1) return { ...node, type: names[0] };\n  return { ...node, type: names };\n}\n\nfunction isNodeNullable(node: DocumentNode): boolean {\n  if (Array.isArray(node.type)) return node.type.includes(\"null\");\n  if (node.anyOf) return node.anyOf.some((branch) => branch.type === \"null\");\n  return node.type === \"null\";\n}\n\nfunction setAnyOfNodeNullable(\n  node: DocumentNode,\n  nullable: boolean,\n): DocumentNode {\n  const branches = node.anyOf ?? [];\n  const nonNullBranches = branches.filter(\n    (branch) => branch.type !== \"null\" || branch.ref,\n  );\n\n  if (nullable) {\n    if (nonNullBranches.length !== branches.length) return node;\n    return { ...node, anyOf: [...branches, createNode(\"null\")] };\n  }\n\n  if (nonNullBranches.length === branches.length) return node;\n  if (nonNullBranches.length !== 1) {\n    return { ...node, anyOf: nonNullBranches };\n  }\n\n  return mergeNullableWrapperIntoBranch(node, nonNullBranches[0]);\n}\n\nfunction wrapNodeInNullableAnyOf(node: DocumentNode): DocumentNode {\n  return {\n    id: node.id,\n    title: node.title,\n    description: node.description,\n    rest: node.rest,\n    order: node.order,\n    anyOf: [cloneNodeAsAnyOfBranch(node), createNode(\"null\")],\n  };\n}\n\nfunction cloneNodeAsAnyOfBranch(node: DocumentNode): DocumentNode {\n  return {\n    ...node,\n    id: createId(),\n    type: nonNullType(node.type),\n    title: undefined,\n    description: undefined,\n    rest: {},\n    order: undefined,\n  };\n}\n\nfunction nonNullType(type: DocumentNode[\"type\"]): DocumentNode[\"type\"] {\n  if (!Array.isArray(type)) return type;\n  const types = type.filter((entry) => entry !== \"null\");\n  if (types.length === 0) return undefined;\n  return types.length === 1 ? types[0] : types;\n}\n\nfunction mergeNullableWrapperIntoBranch(\n  wrapper: DocumentNode,\n  branch: DocumentNode,\n): DocumentNode {\n  return {\n    ...branch,\n    id: wrapper.id,\n    title: wrapper.title ?? branch.title,\n    description: wrapper.description ?? branch.description,\n    rest: { ...branch.rest, ...wrapper.rest },\n    order: wrapper.order ?? branch.order,\n  };\n}\n\nexport function createNode(\n  type: JSONSchema7TypeName | \"enum\" = \"string\",\n): DocumentNode {\n  return normalizeNodeForType({ id: createId(), rest: {} }, type);\n}\n\nexport function createEnumValue(): EnumValue {\n  return { id: createId(\"enum\"), value: \"\" };\n}\n\nexport function updateEffectiveNodeShape(\n  node: DocumentNode,\n  fn: (node: DocumentNode) => DocumentNode,\n): DocumentNode {\n  if (node.anyOf) {\n    return {\n      ...node,\n      anyOf: mapPreserve(node.anyOf, (branch) =>\n        branch.type === \"null\" && !branch.ref ? branch : fn(branch),\n      ),\n    };\n  }\n  return fn(node);\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/document/type-operations.ts"
    },
    {
      "path": "components/schema-editor/document/types.ts",
      "content": "import type { JSONSchema7TypeName } from \"json-schema\";\n\n/**\n * Stable, intrinsic node identity.\n *\n * Born when a node is created, stable across renames / reorders / retypes, dies\n * with the node. NEVER derived from a mutable key or a positional path — that is\n * the whole reason this Document model exists instead of editing JSON Schema in\n * place.\n */\nexport type NodeId = string;\nexport type PropertyId = string;\n\n/** A JSON Schema literal (enum entries, const, default, …). */\nexport type JsonValue =\n  | string\n  | number\n  | boolean\n  | null\n  | JsonValue[]\n  | { [key: string]: JsonValue };\n\n/**\n * The editor's source of truth: a Document — a total, identity-bearing,\n * order-explicit representation of a JSON Schema.\n *\n * Design contract (see ./DESIGN.md):\n *\n *  1. Identity is intrinsic. Every node carries an `id` field; we never key a\n *     node by its property name or its path.\n *\n *  2. Order is explicit. Children JSON Schema keys by name (`properties`, `$defs`)\n *     are stored as ORDERED arrays of entries, so reordering is `arrayMove` and\n *     never depends on JS object key-order.\n *\n *  3. Losslessness by construction. Every keyword we don't model structurally is\n *     carried verbatim in `rest` and projected straight back out. Editing a node\n *     spreads it (`{ ...node, description }`), so unknown keywords ride along.\n *\n *  4. References are by id, not by name. A `$ref` resolves to a Definition's\n *     `id` on import, so renaming a definition can never break a reference; the\n *     `#/$defs/Name` pointer is re-projected from the def's current name on export.\n *\n *  5. The Document can hold transient-invalid states (empty/duplicate keys while\n *     typing). Cleanup (dropping empty keys, etc.) happens at the EXPORT boundary,\n *     never in the model — which is what lets the component be fully controlled.\n */\nexport interface DocumentNode {\n  id: NodeId;\n\n  /**\n   * Mirrors JSON Schema `type`. A single name is the common case; an array\n   * encodes a type-union such as `[\"string\", \"null\"]` (nullable). Absent = \"any\".\n   */\n  type?: JSONSchema7TypeName | JSONSchema7TypeName[];\n\n  title?: string;\n  description?: string;\n\n  /** object: ordered, identity-bearing property entries. */\n  properties?: PropertyEntry[];\n\n  /** Required names from the source that are not modeled as property entries. */\n  extraRequired?: string[];\n\n  /** Source order for `required[]`, used to preserve external required names. */\n  requiredOrder?: string[];\n\n  /** array: the item schema. (Tuple `items` arrays are carried in `rest` for v1.) */\n  items?: DocumentNode;\n\n  /** enum: ordered, identity-bearing values. Base type comes from `type`. */\n  enum?: EnumValue[];\n\n  /** $ref: id of the Definition this node points at, resolved on import. */\n  ref?: NodeId;\n\n  /** Composition keywords, recursively modeled. */\n  anyOf?: DocumentNode[];\n  oneOf?: DocumentNode[];\n  allOf?: DocumentNode[];\n\n  /**\n   * Every keyword we don't model structurally — `const`, `default`, `format`,\n   * `pattern`, `minLength`, `examples`, `additionalProperties`, `x-*`, … — kept\n   * verbatim and projected straight back out. This is what makes the round-trip\n   * lossless.\n   */\n  rest: Record<string, unknown>;\n\n  /**\n   * Internal: source key order, replayed on export for byte-faithful round-trips.\n   * Kept off `rest` so a real keyword named `__order` can't collide with it.\n   */\n  order?: string[];\n\n  /**\n   * Internal: a boolean schema (`true` / `false`) has no structure to model; its\n   * literal value is carried here.\n   */\n  booleanSchema?: boolean;\n}\n\n/**\n * A named child of an object. The `key` is mutable and freely editable; the\n * `required` flag lives on the parent/child EDGE here (in JSON Schema it lives in\n * the parent's `required[]` array, which we distribute onto entries on import and\n * rebuild on export).\n */\nexport interface PropertyEntry {\n  id: PropertyId;\n  key: string;\n  /** Empty key created by the editor while typing; omitted from projection. */\n  isTransient?: boolean;\n  required: boolean;\n  node: DocumentNode;\n}\n\n/** A single enum option. `id` keeps the row stable while its value is edited. */\nexport interface EnumValue {\n  id: NodeId;\n  value: JsonValue;\n}\n\n/** A named definition under `$defs`. `name` is mutable; references point at `id`. */\nexport interface DefinitionEntry {\n  id: NodeId;\n  name: string;\n  node: DocumentNode;\n}\n\n/** The whole editor document — the single source of truth held in one useState. */\nexport interface SchemaDocument {\n  root: DocumentNode;\n  /** Named definitions ($defs / legacy definitions), ordered. */\n  defs: DefinitionEntry[];\n  /**\n   * Document-level round-trip metadata we don't surface structurally — which defs\n   * keyword the source used, top-level `$schema`/`$id`, etc. Carried verbatim.\n   */\n  rest: Record<string, unknown>;\n}\n\n/**\n * The effective \"kind\" of a node for the UI. Derived (see ./derive.ts), never\n * stored — there is exactly one source of truth and this is a projection of it.\n */\nexport type SchemaKind =\n  | JSONSchema7TypeName // string | number | integer | boolean | object | array | null\n  | \"enum\"\n  | \"ref\"\n  | \"union\"\n  | \"any\";\n",
      "type": "registry:component",
      "target": "@components/schema-editor/document/types.ts"
    },
    {
      "path": "components/schema-editor/document/view-model.ts",
      "content": "import type { JSONSchema7Definition } from \"json-schema\";\n\nimport { projectNode } from \"@/components/schema-editor/document/convert\";\nimport {\n  getEffectiveKind,\n  isNullable,\n  resolveRef,\n} from \"@/components/schema-editor/document/derive\";\nimport type { SchemaEditorType } from \"@/components/schema-editor/document/type-operations\";\nimport type {\n  DocumentNode,\n  EnumValue,\n  PropertyEntry,\n  SchemaDocument,\n} from \"@/components/schema-editor/document/types\";\n\nexport interface DocumentDefinitionView {\n  definitionId: string;\n  definitionName: string;\n  schema: JSONSchema7Definition;\n}\n\nexport interface DocumentPropertyView {\n  propertyId: string;\n  propertyName: string;\n  isRequired: boolean;\n  nodeView: DocumentNodeView;\n}\n\nexport interface DocumentNodeView {\n  nodeId: string;\n  docNode: DocumentNode;\n  effectiveNode: DocumentNode;\n  type: SchemaEditorType | \"$ref\" | \"any\" | \"union\" | \"null\";\n  title?: string;\n  description?: string;\n  refName?: string;\n  isNullable: boolean;\n  properties: DocumentPropertyView[];\n  items?: DocumentNodeView;\n  enumEntries: EnumValue[];\n}\n\nexport interface SchemaDocumentView {\n  root: DocumentNodeView;\n  definitions: DocumentDefinitionView[];\n}\n\nexport function getSchemaDocumentView(doc: SchemaDocument): SchemaDocumentView {\n  return {\n    root: getDocumentNodeView(doc, doc.root),\n    definitions: doc.defs.map((definition) => ({\n      definitionId: definition.id,\n      definitionName: definition.name,\n      schema: projectNode(doc, definition.node),\n    })),\n  };\n}\n\nexport function getDocumentNodeView(\n  doc: SchemaDocument,\n  node: DocumentNode,\n): DocumentNodeView {\n  const effectiveNode = getViewEffectiveNode(node);\n  return {\n    nodeId: node.id,\n    docNode: node,\n    effectiveNode,\n    type: getDocumentEditorType(doc, effectiveNode),\n    title: node.title,\n    description: node.description,\n    refName: resolveRef(doc, effectiveNode)?.name,\n    isNullable: isNullable(node),\n    properties: (effectiveNode.properties ?? []).map((property) =>\n      getDocumentPropertyView(doc, property),\n    ),\n    items: effectiveNode.items\n      ? getDocumentNodeView(doc, effectiveNode.items)\n      : undefined,\n    enumEntries: effectiveNode.enum ?? [],\n  };\n}\n\nfunction getDocumentPropertyView(\n  doc: SchemaDocument,\n  property: PropertyEntry,\n): DocumentPropertyView {\n  return {\n    propertyId: property.id,\n    propertyName: property.key,\n    isRequired: property.required,\n    nodeView: getDocumentNodeView(doc, property.node),\n  };\n}\n\nfunction getViewEffectiveNode(node: DocumentNode): DocumentNode {\n  if (node.anyOf) {\n    return (\n      node.anyOf.find((branch) => branch.type !== \"null\" || branch.ref) ?? node\n    );\n  }\n  return node;\n}\n\nfunction getDocumentEditorType(\n  doc: SchemaDocument,\n  node: DocumentNode,\n): DocumentNodeView[\"type\"] {\n  if (node.ref) return \"$ref\";\n  if (node.enum) return \"enum\";\n\n  const kind = getEffectiveKind(node);\n  if (kind === \"ref\") return \"$ref\";\n  if (kind !== \"string\") return kind;\n\n  const format = node.rest.format;\n  if (format === \"date\") return \"date\";\n  if (format === \"time\") return \"time\";\n  if (format === \"date-time\") return \"datetime\";\n\n  return resolveRef(doc, node) ? \"$ref\" : \"string\";\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/document/view-model.ts"
    },
    {
      "path": "components/schema-editor/draft/draft-node-edits.ts",
      "content": "import type { JSONSchema7Definition } from \"json-schema\";\n\nimport type { ExtendedJSONSchema7 } from \"@/components/schema-editor/lib/json-schema-types\";\nimport { getEffectiveNode } from \"@/components/schema-editor/lib/json-schema-utils\";\n\nimport { formatTitle } from \"../schema-title\";\n\nexport function getEffectiveType(node: ExtendedJSONSchema7): {\n  type: string;\n  isNullable: boolean;\n} {\n  if (node.anyOf && Array.isArray(node.anyOf)) {\n    const nonNull = node.anyOf.find(\n      (branch: JSONSchema7Definition) =>\n        typeof branch === \"object\" &&\n        (branch.type !== \"null\" || branch.$ref || branch.enum),\n    );\n    const isNullable = node.anyOf.some(\n      (branch: JSONSchema7Definition) =>\n        typeof branch === \"object\" && branch.type === \"null\",\n    );\n    if (nonNull && typeof nonNull === \"object\") {\n      if (nonNull.$ref) return { type: \"$ref\", isNullable };\n      if (nonNull.enum) return { type: \"enum\", isNullable };\n      if (nonNull.type === \"string\" && nonNull.format === \"date\") {\n        return { type: \"date\", isNullable };\n      }\n      if (nonNull.type === \"string\" && nonNull.format === \"time\") {\n        return { type: \"time\", isNullable };\n      }\n      if (nonNull.type === \"string\" && nonNull.format === \"date-time\") {\n        return { type: \"datetime\", isNullable };\n      }\n      return { type: nonNull.type?.toString() || \"string\", isNullable };\n    }\n    return { type: \"string\", isNullable };\n  }\n  // A nullable scalar can also be encoded as a type union `[\"string\", \"null\"]`\n  // (the standard JSON Schema form, and what the document-model builder emits).\n  // Treat that `null` member as nullability, not as part of the type name.\n  const isTypeArrayNullable =\n    Array.isArray(node.type) && node.type.includes(\"null\");\n  const effectiveTypeName = isTypeArrayNullable\n    ? (node.type as string[]).find((member) => member !== \"null\")\n    : node.type;\n\n  if (node.enum) return { type: \"enum\", isNullable: isTypeArrayNullable };\n  if (node.$ref) return { type: \"$ref\", isNullable: isTypeArrayNullable };\n  if (effectiveTypeName === \"string\" && node.format === \"date\") {\n    return { type: \"date\", isNullable: isTypeArrayNullable };\n  }\n  if (effectiveTypeName === \"string\" && node.format === \"time\") {\n    return { type: \"time\", isNullable: isTypeArrayNullable };\n  }\n  if (effectiveTypeName === \"string\" && node.format === \"date-time\") {\n    return { type: \"datetime\", isNullable: isTypeArrayNullable };\n  }\n  return {\n    type: effectiveTypeName?.toString() || \"string\",\n    isNullable: isTypeArrayNullable,\n  };\n}\n\nexport function defaultSchemaForType(type: string): ExtendedJSONSchema7 {\n  switch (type) {\n    case \"string\":\n      return { type: \"string\" };\n    case \"number\":\n      return { type: \"number\" };\n    case \"integer\":\n      return { type: \"integer\" };\n    case \"boolean\":\n      return { type: \"boolean\" };\n    case \"object\":\n      return { type: \"object\", properties: {}, required: [] };\n    case \"array\":\n      return { type: \"array\", items: { type: \"string\" } };\n    case \"$ref\":\n      return {} as ExtendedJSONSchema7;\n    case \"enum\":\n      return { enum: [], type: \"string\" };\n    case \"date\":\n      return { type: \"string\", format: \"date\" };\n    case \"time\":\n      return { type: \"string\", format: \"time\" };\n    case \"datetime\":\n      return { type: \"string\", format: \"date-time\" };\n    default:\n      return { type: \"string\" };\n  }\n}\n\nexport function updateType(\n  newType: string,\n  nullable: boolean,\n  oldNode: ExtendedJSONSchema7,\n): ExtendedJSONSchema7 {\n  const effectiveOldNode = getEffectiveNode(oldNode);\n  const metadata: Partial<ExtendedJSONSchema7> = {};\n  if (oldNode.title) metadata.title = oldNode.title;\n  if (oldNode.description) metadata.description = oldNode.description;\n\n  let baseSchema = defaultSchemaForType(newType);\n\n  if (newType === \"array\") {\n    baseSchema = {\n      type: \"array\",\n      items:\n        effectiveOldNode.type === \"object\"\n          ? effectiveOldNode\n          : { type: \"string\" },\n    };\n  } else if (newType === \"enum\") {\n    baseSchema = {\n      type: \"string\",\n      enum: effectiveOldNode.enum || [],\n    };\n  }\n\n  if (nullable) {\n    return {\n      anyOf: [baseSchema, { type: \"null\" }],\n      ...metadata,\n    } as ExtendedJSONSchema7;\n  }\n\n  return { ...baseSchema, ...metadata };\n}\n\nexport function setNullable(\n  node: ExtendedJSONSchema7,\n  nullable: boolean,\n): ExtendedJSONSchema7 {\n  const { title, description, ...rest } = node;\n\n  if (nullable) {\n    if (node.anyOf && Array.isArray(node.anyOf)) {\n      const nonNullBranch = node.anyOf.find(\n        (branch: JSONSchema7Definition) =>\n          typeof branch === \"object\" && (branch.type !== \"null\" || branch.$ref),\n      );\n      const nonNullObject =\n        typeof nonNullBranch === \"object\" ? nonNullBranch : {};\n\n      return {\n        anyOf: [{ ...nonNullObject }, { type: \"null\" }],\n        ...(title ? { title } : {}),\n        ...(description ? { description } : {}),\n      };\n    }\n\n    // Strip any pre-existing `null` from a type union so the wrapped branch\n    // isn't doubly-nullable (e.g. `[\"string\",\"null\"]` -> `\"string\"`).\n    return {\n      anyOf: [stripNullType({ ...rest }), { type: \"null\" }],\n      ...(title ? { title } : {}),\n      ...(description ? { description } : {}),\n    };\n  }\n\n  if (node.anyOf && Array.isArray(node.anyOf)) {\n    const nonNullBranch = node.anyOf.find(\n      (branch: JSONSchema7Definition) =>\n        typeof branch === \"object\" && (branch.type !== \"null\" || branch.$ref),\n    );\n    const nonNullObject =\n      typeof nonNullBranch === \"object\" ? nonNullBranch : {};\n\n    return {\n      ...nonNullObject,\n      ...(title ? { title } : {}),\n      ...(description ? { description } : {}),\n    };\n  }\n\n  // Plain node: also handle the type-union nullable form `[\"string\",\"null\"]`,\n  // which carries no `anyOf` — drop the `null` member to make it non-nullable.\n  return stripNullType(node);\n}\n\n/** Remove a `null` member from a `type` union, collapsing to a single name. */\nfunction stripNullType(node: ExtendedJSONSchema7): ExtendedJSONSchema7 {\n  if (!Array.isArray(node.type)) return node;\n  const nonNull = node.type.filter((member) => member !== \"null\");\n  return {\n    ...node,\n    type: (nonNull.length === 0\n      ? undefined\n      : nonNull.length === 1\n        ? nonNull[0]\n        : nonNull) as ExtendedJSONSchema7[\"type\"],\n  };\n}\n\nexport function updateEffectiveNode(\n  node: ExtendedJSONSchema7,\n  updatedEffectiveNode: ExtendedJSONSchema7,\n): ExtendedJSONSchema7 {\n  if (node.anyOf && Array.isArray(node.anyOf)) {\n    const anyOf = node.anyOf.map((branch: JSONSchema7Definition) =>\n      typeof branch === \"object\" && branch.type === \"null\"\n        ? branch\n        : updatedEffectiveNode,\n    );\n    return { ...node, anyOf };\n  }\n  return updatedEffectiveNode;\n}\n\nexport function updateSchemaProperty(\n  schemaNode: ExtendedJSONSchema7,\n  propertyKey: string,\n  newPropertyName: string,\n  updatedProperty: ExtendedJSONSchema7,\n): ExtendedJSONSchema7 {\n  // Refuse to rename onto an existing sibling: merging would drop the renamed\n  // property's schema and leave a duplicate in `required`. Callers validate\n  // name collisions upstream, so this is a defensive no-op.\n  if (\n    newPropertyName !== propertyKey &&\n    schemaNode.properties &&\n    Object.prototype.hasOwnProperty.call(schemaNode.properties, newPropertyName)\n  ) {\n    return schemaNode;\n  }\n\n  const effectiveNode = getEffectiveNode(updatedProperty);\n  const cleanProperty = {\n    ...effectiveNode,\n    title: formatTitle(newPropertyName),\n    ...(Array.isArray(effectiveNode.enum) && { type: \"string\" }),\n  } as ExtendedJSONSchema7;\n  const finalProperty = updateEffectiveNode(updatedProperty, cleanProperty);\n\n  const oldProperties = schemaNode.properties || {};\n  const newProperties: Record<string, ExtendedJSONSchema7> = {};\n  let found = false;\n\n  Object.keys(oldProperties).forEach((key) => {\n    if (key === propertyKey && newPropertyName !== propertyKey) {\n      setRecordValue(newProperties, newPropertyName, finalProperty);\n      found = true;\n    } else if (isJSONSchema(oldProperties[key])) {\n      setRecordValue(\n        newProperties,\n        key,\n        oldProperties[key] as ExtendedJSONSchema7,\n      );\n    }\n  });\n\n  if (!found && newPropertyName !== propertyKey) {\n    setRecordValue(newProperties, newPropertyName, finalProperty);\n    delete newProperties[propertyKey];\n  }\n\n  if (newPropertyName !== propertyKey) {\n    const required = Array.isArray(schemaNode.required)\n      ? schemaNode.required.map((key: string) =>\n          key === propertyKey ? newPropertyName : key,\n        )\n      : [];\n\n    return {\n      ...schemaNode,\n      properties: newProperties,\n      required,\n    };\n  }\n\n  return {\n    ...schemaNode,\n    properties: {\n      ...newProperties,\n      [propertyKey]: finalProperty,\n    },\n  };\n}\n\nfunction isJSONSchema(\n  value: JSONSchema7Definition,\n): value is ExtendedJSONSchema7 {\n  return typeof value === \"object\" && value !== null;\n}\n\nfunction setRecordValue<T>(record: Record<string, T>, key: string, value: T) {\n  Object.defineProperty(record, key, {\n    value,\n    enumerable: true,\n    configurable: true,\n    writable: true,\n  });\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/draft/draft-node-edits.ts"
    },
    {
      "path": "components/schema-editor/enum-creation-dialog.tsx",
      "content": "import * as React from \"react\";\nimport { useState } from \"react\";\nimport { PlusIcon, X } from \"lucide-react\";\n\nimport { Button } from \"@/components/ui/button\";\nimport {\n  Dialog,\n  DialogContent,\n  DialogDescription,\n  DialogFooter,\n  DialogHeader,\n  DialogTitle,\n} from \"@/components/ui/dialog\";\nimport { Input } from \"@/components/ui/input\";\nimport { Label } from \"@/components/ui/label\";\n\ninterface EnumCreationDialogProps {\n  isOpen: boolean;\n  onClose: () => void;\n  onConfirm: (enumValues: string[]) => void;\n  onCancel: () => void;\n}\n\ninterface EnumCreationDialogContentProps {\n  onClose: () => void;\n  onConfirm: (enumValues: string[]) => void;\n  onCancel: () => void;\n}\n\nfunction EnumCreationDialogContent({\n  onClose,\n  onConfirm,\n  onCancel,\n}: EnumCreationDialogContentProps) {\n  const [enumValues, setEnumValues] = useState<string[]>([]); // Start with no values\n  const [newValue, setNewValue] = useState(\"\");\n  const enumInputRefs = React.useRef<Array<HTMLInputElement | null>>([]);\n  const pendingFocusIndexRef = React.useRef<number | null>(null);\n\n  const setEnumInputRef = React.useCallback(\n    (index: number, input: HTMLInputElement | null) => {\n      enumInputRefs.current[index] = input;\n      if (input && pendingFocusIndexRef.current === index) {\n        pendingFocusIndexRef.current = null;\n        input.focus();\n      }\n    },\n    [],\n  );\n\n  const handleAddValue = () => {\n    if (newValue.trim()) {\n      setEnumValues((prev) => [...prev, newValue.trim()]);\n      setNewValue(\"\");\n    }\n  };\n\n  const handleAddFromEmpty = () => {\n    pendingFocusIndexRef.current = enumValues.length;\n    setEnumValues((prev) => [...prev, \"\"]);\n  };\n\n  const handleRemoveValue = (index: number) => {\n    setEnumValues((prev) => prev.filter((_, i) => i !== index));\n  };\n\n  const handleEditValue = (index: number, value: string) => {\n    setEnumValues((prev) => prev.map((val, i) => (i === index ? value : val)));\n  };\n\n  const handleConfirm = () => {\n    // Filter out empty values\n    const validValues = enumValues.filter((val) => val.trim() !== \"\");\n    if (validValues.length > 0) {\n      onConfirm(validValues);\n      onClose();\n    }\n  };\n\n  const handleCancel = () => {\n    onCancel();\n    onClose();\n  };\n\n  const validValuesCount = enumValues.filter((val) => val.trim() !== \"\").length;\n\n  return (\n    <DialogContent className=\"max-h-[85vh] overflow-y-auto sm:max-w-lg\">\n      <DialogHeader>\n        <DialogTitle>Update Field</DialogTitle>\n        <DialogDescription>\n          Define the allowed values for this field.\n        </DialogDescription>\n      </DialogHeader>\n\n      <div className=\"space-y-4\">\n        <div>\n          <Label className=\"mb-2\">Enabled Options</Label>\n\n          {enumValues.length > 0 && (\n            <div className=\"mb-4 space-y-2\">\n              {enumValues.map((value, index) => (\n                <div key={index} className=\"flex items-center gap-2\">\n                  <Input\n                    value={value}\n                    ref={(input) => {\n                      setEnumInputRef(index, input);\n                    }}\n                    onChange={(e) => handleEditValue(index, e.target.value)}\n                    onKeyDown={(e) => {\n                      if (e.key === \"Enter\" && value.trim()) {\n                        if (index === enumValues.length - 1) {\n                          handleAddFromEmpty();\n                        } else {\n                          enumInputRefs.current[index + 1]?.focus();\n                        }\n                      }\n                    }}\n                    className=\"flex-1\"\n                    placeholder={`Value ${index + 1}`}\n                  />\n                  <Button\n                    type=\"button\"\n                    variant=\"ghost\"\n                    size=\"sm\"\n                    className=\"h-8 w-8 p-1\"\n                    onClick={() => handleRemoveValue(index)}\n                    disabled={enumValues.length === 1}\n                  >\n                    <X className=\"h-4 w-4\" />\n                  </Button>\n                </div>\n              ))}\n            </div>\n          )}\n\n          <div className=\"flex items-center gap-2\">\n            <Input\n              value={newValue}\n              onChange={(e) => setNewValue(e.target.value)}\n              onKeyDown={(e) => {\n                if (e.key === \"Enter\" && newValue.trim()) {\n                  handleAddValue();\n                }\n              }}\n              className=\"flex-1\"\n              placeholder=\"Add another value\"\n            />\n            <Button\n              type=\"button\"\n              variant=\"outline\"\n              size=\"sm\"\n              onClick={handleAddValue}\n              disabled={!newValue.trim()}\n            >\n              <PlusIcon className=\"mr-1 h-4 w-4\" />\n              Add\n            </Button>\n          </div>\n\n          <p className=\"text-muted-foreground mt-2 text-xs\">\n            {validValuesCount} value{validValuesCount !== 1 ? \"s\" : \"\"} defined.\n          </p>\n        </div>\n      </div>\n\n      <DialogFooter>\n        <Button type=\"button\" variant=\"outline\" onClick={handleCancel}>\n          Cancel\n        </Button>\n        <Button\n          type=\"button\"\n          variant=\"default\"\n          onClick={handleConfirm}\n          disabled={validValuesCount === 0}\n        >\n          Update Field ({validValuesCount} value\n          {validValuesCount !== 1 ? \"s\" : \"\"})\n        </Button>\n      </DialogFooter>\n    </DialogContent>\n  );\n}\n\nexport function EnumCreationDialog({\n  isOpen,\n  onClose,\n  onConfirm,\n  onCancel,\n}: EnumCreationDialogProps) {\n  const handleOpenChange = (open: boolean) => {\n    if (!open) {\n      onCancel();\n      onClose();\n    }\n  };\n\n  return (\n    <Dialog open={isOpen} onOpenChange={handleOpenChange}>\n      {isOpen ? (\n        <EnumCreationDialogContent\n          key=\"enum-creation-dialog\"\n          onClose={onClose}\n          onConfirm={onConfirm}\n          onCancel={onCancel}\n        />\n      ) : null}\n    </Dialog>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/enum-creation-dialog.tsx"
    },
    {
      "path": "components/schema-editor/lib/configure-ajv.ts",
      "content": "import type Ajv from \"ajv\";\nimport ajvErrors from \"ajv-errors\";\nimport addFormats from \"ajv-formats\";\n\ntype AjvFormatsInstance = Parameters<typeof addFormats>[0];\ntype AjvErrorsInstance = Parameters<typeof ajvErrors>[0];\n\nexport function addJsonSchemaFormats(ajv: Ajv) {\n  addFormats(ajv as unknown as AjvFormatsInstance);\n}\n\nexport function addJsonSchemaErrors(ajv: Ajv) {\n  ajvErrors(ajv as unknown as AjvErrorsInstance);\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/lib/configure-ajv.ts"
    },
    {
      "path": "components/schema-editor/lib/json-schema-types.ts",
      "content": "import type { JSONSchema7 } from \"json-schema\";\n\n/**\n * The schema editor works with standard JSON Schema (Draft 7). This is a plain\n * alias for `JSONSchema7` — no custom extensions — kept under a single shared\n * name so the editor's many imports stay stable.\n */\nexport type ExtendedJSONSchema7 = JSONSchema7;\n",
      "type": "registry:component",
      "target": "@components/schema-editor/lib/json-schema-types.ts"
    },
    {
      "path": "components/schema-editor/lib/json-schema-utils.ts",
      "content": "import type { JSONSchema7Definition } from \"json-schema\";\nimport type { ExtendedJSONSchema7 } from \"@/components/schema-editor/lib/json-schema-types\";\n\n// Pydantic reserved names that actually break model creation\nconst PYDANTIC_RESERVED = [\n  \"__root__\",\n  \"model_config\",\n  \"model_post_init\",\n  \"model_validate\",\n  \"model_dump\",\n];\n\n// Generic name validator for Pydantic compatible field and definition names\nexport function validateName(\n  name: string,\n  existingNames: string[] = [],\n  currentName?: string,\n  entityType: string = \"name\",\n): string | null {\n  if (!/^[a-zA-Z_][a-zA-Z0-9_]{0,63}$/.test(name)) {\n    return \"Name must start with a letter or underscore, contain only letters, numbers, or underscores, and be at most 64 characters long\";\n  }\n\n  if (PYDANTIC_RESERVED.includes(name)) {\n    return `\"${name}\" is a Pydantic reserved name`;\n  }\n\n  const namesToCheck = currentName\n    ? existingNames.filter((n) => n.toLowerCase() !== currentName.toLowerCase())\n    : existingNames;\n\n  if (namesToCheck.some((n) => n.toLowerCase() === name.toLowerCase())) {\n    return `A ${entityType} with the name \"${name}\" already exists (names are case-insensitive)`;\n  }\n\n  return null;\n}\n\n/**\n * Unwrap a nullable `anyOf` node (e.g. `{ anyOf: [<schema>, { type: \"null\" }] }`)\n * down to its underlying non-null schema so it can be inspected directly.\n */\nexport function getEffectiveNode(\n  node: ExtendedJSONSchema7,\n): ExtendedJSONSchema7 {\n  if (node.anyOf && Array.isArray(node.anyOf)) {\n    const nonNull = node.anyOf.find(\n      (b: JSONSchema7Definition) =>\n        typeof b === \"object\" && (b.type !== \"null\" || b.$ref),\n    );\n    return nonNull && typeof nonNull === \"object\" ? nonNull : node;\n  }\n  return node;\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/lib/json-schema-utils.ts"
    },
    {
      "path": "components/schema-editor/node-dialog.tsx",
      "content": "import * as React from \"react\";\n\nimport type { ExtendedJSONSchema7 } from \"@/components/schema-editor/lib/json-schema-types\";\nimport { PropertyForm } from \"@/components/schema-editor/property-form/property-form\";\nimport type {\n  PropertyDraft,\n  PropertyFormMode,\n  PropertyFormSchemaContext,\n} from \"@/components/schema-editor/property-form/types\";\nimport {\n  Dialog,\n  DialogContent,\n  DialogDescription,\n  DialogHeader,\n  DialogTitle,\n} from \"@/components/ui/dialog\";\n\ninterface NodeDialogProps {\n  isOpen: boolean;\n  onClose: () => void;\n  onChange: (newNode: ExtendedJSONSchema7) => void;\n  onNameChange?: (newName: string, updatedNode?: ExtendedJSONSchema7) => void;\n  onDelete?: () => void;\n  node: ExtendedJSONSchema7;\n  name: string;\n  mode: PropertyFormMode;\n  siblingNames: string[];\n  formContext: Omit<PropertyFormSchemaContext, \"siblingNames\" | \"originalName\">;\n}\n\nexport function NodeDialog({\n  isOpen,\n  onClose,\n  onChange,\n  onNameChange,\n  onDelete,\n  node,\n  name,\n  mode,\n  siblingNames,\n  formContext,\n}: NodeDialogProps) {\n  const handleCommit = async (next: PropertyDraft) => {\n    if (next.name !== name && onNameChange) {\n      onNameChange(next.name, next.schemaNode);\n    } else {\n      onChange(next.schemaNode);\n    }\n\n    onClose();\n  };\n\n  return (\n    <Dialog open={isOpen} onOpenChange={onClose}>\n      <DialogContent className=\"max-h-[90vh] gap-2 overflow-y-auto p-0 sm:max-w-xl\">\n        <DialogHeader className=\"px-4 pt-4\">\n          <DialogTitle>\n            {mode === \"readOnly\" ? \"View Property\" : \"Edit Property\"}\n          </DialogTitle>\n          <DialogDescription>\n            {mode === \"readOnly\"\n              ? \"View property name, type, description, and other characteristics.\"\n              : \"Modify property name, type, description, and other characteristics.\"}\n          </DialogDescription>\n        </DialogHeader>\n\n        <PropertyForm\n          propertyDraft={{ name, schemaNode: node }}\n          schemaContext={{\n            ...formContext,\n            siblingNames,\n            originalName: name,\n          }}\n          onCommitPropertyDraft={handleCommit}\n          onCancel={onClose}\n          onDelete={onDelete}\n          submitLabel=\"Save\"\n          mode={mode}\n        />\n      </DialogContent>\n    </Dialog>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/node-dialog.tsx"
    },
    {
      "path": "components/schema-editor/object-template-type-section.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport type { SchemaTypeMenuTrailingContent } from \"@/components/schema-editor/primitives/schema-type-menu\";\n\nconst LazyObjectTemplateSubmenu = React.lazy(() =>\n  import(\n    \"@/components/schema-editor/optional/object-templates/object-template-menu\"\n  ).then((module) => ({\n    default: module.ObjectTemplateSubmenu,\n  })),\n);\n\nexport function createObjectTemplateTypeTrailingContent({\n  onSelectTemplate,\n}: {\n  onSelectTemplate: (templateName: string) => void;\n}): SchemaTypeMenuTrailingContent {\n  function ObjectTemplateTypeTrailingContent({\n    editable,\n  }: {\n    editable: boolean;\n  }) {\n    return (\n      <React.Suspense fallback={null}>\n        <LazyObjectTemplateSubmenu\n          onSelectTemplate={(templateName) => {\n            if (!editable) return;\n            onSelectTemplate(templateName);\n          }}\n        />\n      </React.Suspense>\n    );\n  }\n\n  ObjectTemplateTypeTrailingContent.displayName =\n    \"ObjectTemplateTypeTrailingContent\";\n  return ObjectTemplateTypeTrailingContent;\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/object-template-type-section.tsx"
    },
    {
      "path": "components/schema-editor/optional/import-export/import-export-menu-items.tsx",
      "content": "\"use client\";\n\nimport { CloudUpload, Copy, Download } from \"lucide-react\";\nimport { toast } from \"sonner\";\n\nimport type { ExtendedJSONSchema7 } from \"@/components/schema-editor/lib/json-schema-types\";\nimport { DropdownMenuItem } from \"@/components/ui/dropdown-menu\";\n\nexport function ImportExportMenuItems({\n  node,\n  onReplaceRoot,\n}: {\n  node: ExtendedJSONSchema7;\n  onReplaceRoot: (node: ExtendedJSONSchema7) => void;\n}) {\n  const handleDownloadSchema = () => {\n    try {\n      const schemaBlob = new Blob([JSON.stringify(node, null, 2)], {\n        type: \"application/json\",\n      });\n      const url = URL.createObjectURL(schemaBlob);\n      const link = document.createElement(\"a\");\n      link.href = url;\n      link.download = node.title\n        ? `${node.title.toLowerCase().replace(/\\s+/g, \"-\")}.json`\n        : \"schema.json\";\n\n      document.body.appendChild(link);\n      link.click();\n      document.body.removeChild(link);\n      URL.revokeObjectURL(url);\n\n      toast.success(\"Download Started\", {\n        description: \"Your schema has been downloaded successfully.\",\n      });\n    } catch (error) {\n      console.error(\"Error downloading schema:\", error);\n      toast.error(\"Download Failed\", {\n        description: \"There was an error downloading your schema.\",\n      });\n    }\n  };\n\n  const handleUploadSchema = () => {\n    try {\n      const fileInput = document.createElement(\"input\");\n      fileInput.type = \"file\";\n      fileInput.accept = \".json\";\n      fileInput.style.display = \"none\";\n\n      fileInput.addEventListener(\"change\", (event) => {\n        const target = event.target as HTMLInputElement;\n        if (target.files && target.files.length > 0) {\n          const file = target.files[0];\n          const reader = new FileReader();\n\n          reader.onload = async (loadEvent) => {\n            try {\n              const content = loadEvent.target?.result as string;\n              onReplaceRoot(JSON.parse(content));\n              toast.success(\"Schema Uploaded\", {\n                description:\n                  \"Your schema has been uploaded and applied successfully.\",\n              });\n            } catch (error) {\n              console.error(\"Error parsing uploaded schema:\", error);\n              toast.error(\"Upload Failed\", {\n                description: \"The uploaded file is not a valid JSON schema.\",\n              });\n            }\n          };\n\n          reader.readAsText(file);\n        }\n\n        document.body.removeChild(fileInput);\n      });\n\n      document.body.appendChild(fileInput);\n      fileInput.click();\n    } catch (error) {\n      console.error(\"Error uploading schema:\", error);\n      toast.error(\"Upload Failed\", {\n        description: \"There was an error uploading your schema.\",\n      });\n    }\n  };\n\n  const handleCopy = () => {\n    try {\n      const formattedSchema = JSON.stringify(node, null, 2);\n      navigator.clipboard\n        .writeText(formattedSchema)\n        .then(() => {\n          toast.success(\"Copied to Clipboard\", {\n            description: \"Your schema has been copied to the clipboard.\",\n          });\n        })\n        .catch((error) => {\n          console.error(\"Error copying to clipboard:\", error);\n          const textArea = document.createElement(\"textarea\");\n          textArea.value = formattedSchema;\n          textArea.style.position = \"fixed\";\n          textArea.style.left = \"-999999px\";\n          textArea.style.top = \"-999999px\";\n          document.body.appendChild(textArea);\n          textArea.focus();\n          textArea.select();\n\n          try {\n            document.execCommand(\"copy\");\n            textArea.remove();\n            toast.success(\"Copied to Clipboard\", {\n              description: \"Your schema has been copied to the clipboard.\",\n            });\n          } catch (copyError) {\n            console.error(\"Unable to copy schema\", copyError);\n            toast.error(\"Copy Failed\", {\n              description:\n                \"There was an error copying your schema to the clipboard.\",\n            });\n            textArea.remove();\n          }\n        });\n    } catch (error) {\n      console.error(\"Error preparing schema for copy:\", error);\n      toast.error(\"Copy Failed\", {\n        description: \"There was an error preparing your schema for copying.\",\n      });\n    }\n  };\n\n  return (\n    <>\n      <DropdownMenuItem onClick={handleDownloadSchema}>\n        <Download className=\"mr-2 h-4 w-4\" />\n        Download\n      </DropdownMenuItem>\n      <DropdownMenuItem onClick={handleUploadSchema}>\n        <CloudUpload className=\"mr-2 h-4 w-4\" />\n        Upload\n      </DropdownMenuItem>\n      <DropdownMenuItem onClick={handleCopy}>\n        <Copy className=\"mr-2 h-4 w-4\" />\n        Copy to clipboard\n      </DropdownMenuItem>\n    </>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/optional/import-export/import-export-menu-items.tsx"
    },
    {
      "path": "components/schema-editor/optional/json-mode/json-mode-editor.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport type { ExtendedJSONSchema7 } from \"@/components/schema-editor/schema-builder-types\";\nimport { Button } from \"@/components/ui/button\";\n\ntype JsonModeDraftState =\n  | { status: \"dirty-valid\"; text: string; parsed: ExtendedJSONSchema7 }\n  | { status: \"dirty-invalid\"; text: string; error: string };\n\ntype JsonModeState = { status: \"synced\"; text: string } | JsonModeDraftState;\n\nexport function JsonModeEditor({\n  schema,\n  readOnly,\n  replaceSchema,\n}: {\n  schema: ExtendedJSONSchema7;\n  readOnly: boolean;\n  replaceSchema: (schema: ExtendedJSONSchema7) => void | Promise<void>;\n}) {\n  const schemaText = React.useMemo(\n    () => JSON.stringify(schema, null, 2),\n    [schema],\n  );\n  const [jsonDraft, setJsonDraft] = React.useState<JsonModeDraftState | null>(\n    null,\n  );\n  const jsonState = React.useMemo<JsonModeState>(\n    () =>\n      jsonDraft ?? {\n        status: \"synced\",\n        text: schemaText,\n      },\n    [jsonDraft, schemaText],\n  );\n\n  const handleJsonChange = React.useCallback((text: string) => {\n    try {\n      const parsed = JSON.parse(text) as ExtendedJSONSchema7;\n      setJsonDraft({ status: \"dirty-valid\", text, parsed });\n    } catch (error) {\n      setJsonDraft({\n        status: \"dirty-invalid\",\n        text,\n        error: error instanceof Error ? error.message : \"Invalid JSON\",\n      });\n    }\n  }, []);\n\n  const applyJson = React.useCallback(() => {\n    if (readOnly || jsonState.status !== \"dirty-valid\") return;\n    void replaceSchema(jsonState.parsed);\n    setJsonDraft(null);\n  }, [jsonState, readOnly, replaceSchema]);\n\n  const discardJson = React.useCallback(() => {\n    setJsonDraft(null);\n  }, []);\n\n  return (\n    <div className=\"flex min-h-[420px] flex-col gap-3\">\n      <textarea\n        spellCheck={false}\n        readOnly={readOnly}\n        className=\"bg-muted/30 focus-visible:ring-ring min-h-0 flex-1 resize-none overflow-auto rounded-lg border p-3 font-mono text-xs outline-none focus-visible:ring-2\"\n        value={jsonState.text}\n        onChange={(event) => handleJsonChange(event.target.value)}\n      />\n      <div className=\"flex items-center justify-between gap-3\">\n        <p className=\"text-destructive min-h-5 text-sm\">\n          {jsonState.status === \"dirty-invalid\" ? jsonState.error : null}\n        </p>\n        {!readOnly && (\n          <div className=\"flex items-center gap-2\">\n            <Button\n              type=\"button\"\n              size=\"sm\"\n              variant=\"outline\"\n              onClick={discardJson}\n              disabled={jsonState.status === \"synced\"}\n            >\n              Discard\n            </Button>\n            <Button\n              type=\"button\"\n              size=\"sm\"\n              onClick={applyJson}\n              disabled={jsonState.status !== \"dirty-valid\"}\n            >\n              Apply\n            </Button>\n          </div>\n        )}\n      </div>\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/optional/json-mode/json-mode-editor.tsx"
    },
    {
      "path": "components/schema-editor/optional/object-templates/object-template-menu.tsx",
      "content": "import { Shapes } from \"lucide-react\";\n\nimport {\n  DropdownMenuItem,\n  DropdownMenuPortal,\n  DropdownMenuSub,\n  DropdownMenuSubContent,\n  DropdownMenuSubTrigger,\n} from \"@/components/ui/dropdown-menu\";\nimport { getTemplateIcon } from \"@/components/schema-editor/type-icons\";\n\nimport { templateObjects } from \"./template-objects\";\n\nexport function ObjectTemplateSubmenu({\n  onSelectTemplate,\n}: {\n  onSelectTemplate: (name: string) => void;\n}) {\n  return (\n    <DropdownMenuSub>\n      <DropdownMenuSubTrigger>\n        <Shapes className=\"mr-4 h-4 w-4\" />\n        object template\n      </DropdownMenuSubTrigger>\n      <DropdownMenuPortal>\n        <DropdownMenuSubContent>\n          {Object.entries(templateObjects).map(([name]) => (\n            <DropdownMenuItem\n              key={name}\n              className=\"flex items-center gap-2\"\n              onClick={() => onSelectTemplate(name)}\n            >\n              {getTemplateIcon(name)}\n              {name}\n            </DropdownMenuItem>\n          ))}\n        </DropdownMenuSubContent>\n      </DropdownMenuPortal>\n    </DropdownMenuSub>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/optional/object-templates/object-template-menu.tsx"
    },
    {
      "path": "components/schema-editor/optional/object-templates/object-template-reference.ts",
      "content": "import { nodeFromJson } from \"@/components/schema-editor/document/convert\";\nimport {\n  addDefinition,\n  setRef,\n} from \"@/components/schema-editor/document/definition-operations\";\nimport type { SchemaDocument } from \"@/components/schema-editor/document/types\";\n\nimport {\n  objectTemplateDependencies,\n  templateObjects,\n} from \"./template-objects\";\n\nexport function applyObjectTemplateReferenceToDocument(\n  doc: SchemaDocument,\n  nodeId: string,\n  templateName: string,\n): SchemaDocument {\n  const next = addObjectTemplateDefinitionsToDocument(doc, templateName);\n  const targetDefinition = next.defs.find(\n    (definition) => definition.name === templateName,\n  );\n  return targetDefinition ? setRef(next, nodeId, targetDefinition.id) : next;\n}\n\nexport function addObjectTemplateDefinitionsToDocument(\n  doc: SchemaDocument,\n  templateName: string,\n): SchemaDocument {\n  const template = templateObjects[templateName];\n  if (!template) return doc;\n\n  // Dependencies must be added BEFORE the template that references them, so\n  // `nodeFromJson` can resolve the template's `$ref`s to a real definition id.\n  // Otherwise the reference is kept as a raw, unlinked `$ref` in `rest` and the\n  // field renders as \"any\" in the editor (the exported JSON stays correct, but\n  // the in-editor model is broken).\n  const defsToAdd = [\n    ...(objectTemplateDependencies[templateName] ?? []),\n    templateName,\n  ];\n\n  let next = doc;\n  for (const defName of defsToAdd) {\n    if (next.defs.some((definition) => definition.name === defName)) continue;\n\n    const templateNode = templateObjects[defName];\n    if (!templateNode) continue;\n\n    next = addDefinition(next, {\n      name: defName,\n      node: nodeFromJson(templateNode, next),\n    }).doc;\n  }\n\n  return next;\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/optional/object-templates/object-template-reference.ts"
    },
    {
      "path": "components/schema-editor/optional/object-templates/template-objects.ts",
      "content": "import type { ExtendedJSONSchema7 } from \"@/components/schema-editor/lib/json-schema-types\";\n\nexport const objectTemplateDependencies: Record<string, string[]> = {\n  Company: [\"Address\"],\n};\n\nexport const templateObjects: Record<string, ExtendedJSONSchema7> = {\n  Address: {\n    description:\n      \"A normalized postal address (Schema.org / UBL / FHIR compatible).\",\n    properties: {\n      street_address: {\n        description:\n          \"Full address line: house number, street name, apt/suite, building, floor, company, ...\",\n        examples: [\"221B Baker Street, Flat B, 10th Floor, Acme Inc.\"],\n        title: \"Street Address\",\n        type: \"string\",\n      },\n      city: {\n        examples: [\"London\"],\n        title: \"City\",\n        type: \"string\",\n      },\n      region: {\n        anyOf: [\n          {\n            type: \"string\",\n          },\n          {\n            type: \"null\",\n          },\n        ],\n        default: null,\n        description: \"State / province / region.\",\n        examples: [\"Greater London\"],\n        title: \"Region\",\n      },\n      postal_code: {\n        anyOf: [\n          {\n            type: \"string\",\n          },\n          {\n            type: \"null\",\n          },\n        ],\n        default: null,\n        description: \"ZIP / postal code.\",\n        examples: [\"NW1 6XE\"],\n        title: \"Postal Code\",\n      },\n      country: {\n        examples: [\"GB\"],\n        maxLength: 2,\n        minLength: 2,\n        pattern: \"^[A-Z]{2}$\",\n        title: \"Country\",\n        type: \"string\",\n      },\n    },\n    required: [\"street_address\", \"city\", \"country\"],\n    title: \"Address\",\n    type: \"object\",\n  },\n  Price: {\n    description:\n      \"Monetary value tied to a specific ISO-4217 currency.\\n\\n`amount` is stored in the major unit of the currency (e.g. dollars, euros).\",\n    properties: {\n      amount: {\n        anyOf: [\n          {\n            type: \"number\",\n          },\n          {\n            type: \"string\",\n          },\n        ],\n        examples: [\"19.99\"],\n        title: \"Amount\",\n      },\n      currency: {\n        examples: [\"USD\"],\n        maxLength: 3,\n        minLength: 3,\n        pattern: \"^[A-Z]{3}$\",\n        title: \"Currency\",\n        type: \"string\",\n      },\n    },\n    required: [\"amount\", \"currency\"],\n    title: \"Price\",\n    type: \"object\",\n  },\n  Person: {\n    description: \"A natural person with contact information.\",\n    properties: {\n      first_name: {\n        examples: [\"Ada\"],\n        title: \"First Name\",\n        type: \"string\",\n      },\n      last_name: {\n        examples: [\"Lovelace\"],\n        title: \"Last Name\",\n        type: \"string\",\n      },\n      middle_name: {\n        anyOf: [\n          {\n            type: \"string\",\n          },\n          {\n            type: \"null\",\n          },\n        ],\n        default: null,\n        examples: [\"King\"],\n        title: \"Middle Name\",\n      },\n      email: {\n        anyOf: [\n          {\n            pattern: \"^[^@\\\\s]+@[^@\\\\s]+\\\\.[^@\\\\s]+$\",\n            type: \"string\",\n          },\n          {\n            type: \"null\",\n          },\n        ],\n        default: null,\n        examples: [\"ada@example.com\"],\n        title: \"Email\",\n      },\n      phone: {\n        anyOf: [\n          {\n            pattern: \"^\\\\+?[0-9 .\\\\-()]{7,25}$\",\n            type: \"string\",\n          },\n          {\n            type: \"null\",\n          },\n        ],\n        default: null,\n        examples: [\"+1-555-0100\"],\n        title: \"Phone\",\n      },\n    },\n    required: [\"first_name\", \"last_name\"],\n    title: \"Person\",\n    type: \"object\",\n  },\n  Company: {\n    $defs: {\n      Address: {\n        description:\n          \"A normalized postal address (Schema.org / UBL / FHIR compatible).\",\n        properties: {\n          street_address: {\n            description:\n              \"Full address line: house number, street name, apt/suite, building, floor, company, ...\",\n            examples: [\"221B Baker Street, Flat B, 10th Floor, Acme Inc.\"],\n            title: \"Street Address\",\n            type: \"string\",\n          },\n          city: {\n            examples: [\"London\"],\n            title: \"City\",\n            type: \"string\",\n          },\n          region: {\n            anyOf: [\n              {\n                type: \"string\",\n              },\n              {\n                type: \"null\",\n              },\n            ],\n            default: null,\n            description: \"State / province / region.\",\n            examples: [\"Greater London\"],\n            title: \"Region\",\n          },\n          postal_code: {\n            anyOf: [\n              {\n                type: \"string\",\n              },\n              {\n                type: \"null\",\n              },\n            ],\n            default: null,\n            description: \"ZIP / postal code.\",\n            examples: [\"NW1 6XE\"],\n            title: \"Postal Code\",\n          },\n          country: {\n            examples: [\"GB\"],\n            maxLength: 2,\n            minLength: 2,\n            pattern: \"^[A-Z]{2}$\",\n            title: \"Country\",\n            type: \"string\",\n          },\n        },\n        required: [\"street_address\", \"city\", \"country\"],\n        title: \"Address\",\n        type: \"object\",\n      },\n    },\n    description: \"A legal entity or organization.\",\n    properties: {\n      name: {\n        description: \"Registered legal name.\",\n        examples: [\"Example Corp.\"],\n        title: \"Name\",\n        type: \"string\",\n      },\n      address: {\n        $ref: \"#/$defs/Address\",\n      },\n      phone: {\n        anyOf: [\n          {\n            pattern: \"^\\\\+?[0-9 .\\\\-()]{7,25}$\",\n            type: \"string\",\n          },\n          {\n            type: \"null\",\n          },\n        ],\n        default: null,\n        examples: [\"+1 415 555 0199\"],\n        title: \"Phone\",\n      },\n      email: {\n        anyOf: [\n          {\n            pattern: \"^[^@\\\\s]+@[^@\\\\s]+\\\\.[^@\\\\s]+$\",\n            type: \"string\",\n          },\n          {\n            type: \"null\",\n          },\n        ],\n        default: null,\n        examples: [\"info@example.com\"],\n        title: \"Email\",\n      },\n      website: {\n        anyOf: [\n          {\n            type: \"string\",\n          },\n          {\n            type: \"null\",\n          },\n        ],\n        default: null,\n        description: \"Company website URL.\",\n        examples: [\"https://example.com\"],\n        title: \"Website\",\n      },\n    },\n    required: [\"name\", \"address\"],\n    title: \"Company\",\n    type: \"object\",\n  },\n  Event: {\n    description:\n      \"An occurrence at a certain date and place, lasting from a start time to an end time.\",\n    properties: {\n      title: {\n        examples: [\"Board Meeting\"],\n        title: \"Title\",\n        type: \"string\",\n      },\n      date: {\n        examples: [\"2025-08-15\"],\n        format: \"date\",\n        title: \"Date\",\n        type: \"string\",\n      },\n      start_time: {\n        examples: [\"09:00\"],\n        format: \"time\",\n        title: \"Start Time\",\n        type: \"string\",\n      },\n      end_time: {\n        examples: [\"11:30\"],\n        format: \"time\",\n        title: \"End Time\",\n        type: \"string\",\n      },\n      description: {\n        examples: [\"Quarterly results call for the company\"],\n        title: \"Description\",\n        type: \"string\",\n      },\n    },\n    required: [\"title\", \"date\", \"start_time\", \"end_time\", \"description\"],\n    title: \"Event\",\n    type: \"object\",\n  },\n};\n",
      "type": "registry:component",
      "target": "@components/schema-editor/optional/object-templates/template-objects.ts"
    },
    {
      "path": "components/schema-editor/primitives/schema-add-input-model.ts",
      "content": "export interface SchemaAddInputModel {\n  error?: string | null;\n  focusAfterSubmit?: boolean;\n  inputLabel: string;\n  placeholder: string;\n  submitLabel: string;\n  value: string;\n  onChange: (value: string) => void;\n  onSubmit: () => void;\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/primitives/schema-add-input-model.ts"
    },
    {
      "path": "components/schema-editor/primitives/schema-add-row.tsx",
      "content": "\"use client\";\n\nimport { AlertCircle, PlusIcon } from \"lucide-react\";\n\nimport { cn } from \"@/lib/utils\";\nimport { Button } from \"@/components/ui/button\";\nimport { Input } from \"@/components/ui/input\";\nimport type { SchemaAddInputModel } from \"@/components/schema-editor/primitives/schema-add-input-model\";\n\ninterface SchemaAddRowProps extends SchemaAddInputModel {\n  className?: string;\n  disabled: boolean;\n}\n\nexport function SchemaAddRow({\n  className,\n  disabled,\n  error,\n  inputLabel,\n  placeholder,\n  submitLabel,\n  value,\n  onChange,\n  onSubmit,\n}: SchemaAddRowProps) {\n  const isSubmitDisabled = disabled || !value.trim() || Boolean(error);\n\n  return (\n    <div\n      data-slot=\"schema-add-row\"\n      className={cn(\"flex flex-col gap-1\", className)}\n    >\n      <div className=\"flex items-center gap-3\">\n        <Input\n          aria-label={inputLabel}\n          placeholder={placeholder}\n          disabled={disabled}\n          value={value}\n          onChange={(event) => onChange(event.target.value)}\n          onKeyDown={(event) => {\n            if (event.key === \"Enter\") {\n              event.preventDefault();\n              event.stopPropagation();\n              if (!isSubmitDisabled) onSubmit();\n            }\n          }}\n          className={`h-8 w-40 ${error ? \"border-destructive\" : \"\"}`}\n        />\n        <Button\n          type=\"button\"\n          variant=\"outline\"\n          size=\"sm\"\n          disabled={isSubmitDisabled}\n          className={isSubmitDisabled ? \"cursor-not-allowed\" : \"\"}\n          onClick={onSubmit}\n        >\n          <PlusIcon className=\"h-4 w-4\" />\n          <span>{submitLabel}</span>\n        </Button>\n      </div>\n\n      {error && (\n        <p className=\"text-destructive flex items-center gap-1 text-xs\">\n          <AlertCircle className=\"h-3 w-3\" /> {error}\n        </p>\n      )}\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/primitives/schema-add-row.tsx"
    },
    {
      "path": "components/schema-editor/primitives/schema-chip-add-row.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { PlusIcon } from \"lucide-react\";\n\nimport { Button } from \"@/components/ui/button\";\nimport { Input } from \"@/components/ui/input\";\nimport type { SchemaAddInputModel } from \"@/components/schema-editor/primitives/schema-add-input-model\";\n\nexport interface SchemaChipAddRowProps {\n  addInput: SchemaAddInputModel;\n  editable: boolean;\n}\n\nexport function SchemaChipAddRow({\n  addInput,\n  editable,\n}: SchemaChipAddRowProps) {\n  const addInputRef = React.useRef<HTMLInputElement>(null);\n\n  const submitAddRow = () => {\n    if (!addInput.value.trim()) return;\n    addInput.onSubmit();\n    if (addInput.focusAfterSubmit) {\n      addInputRef.current?.focus();\n    }\n  };\n\n  return (\n    <div className=\"flex items-center gap-2\">\n      <Input\n        aria-label={addInput.inputLabel}\n        ref={addInputRef}\n        disabled={!editable}\n        placeholder={addInput.placeholder}\n        value={addInput.value}\n        onChange={(event) => addInput.onChange(event.target.value)}\n        onKeyDown={(event) => {\n          if (event.key === \"Enter\") {\n            event.preventDefault();\n            event.stopPropagation();\n            submitAddRow();\n          }\n        }}\n        className=\"w-40\"\n      />\n      <Button\n        disabled={!editable || !addInput.value.trim()}\n        type=\"button\"\n        variant=\"outline\"\n        size=\"sm\"\n        onClick={submitAddRow}\n      >\n        <PlusIcon className=\"mr-1 h-4 w-4\" />\n        {addInput.submitLabel}\n      </Button>\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/primitives/schema-chip-add-row.tsx"
    },
    {
      "path": "components/schema-editor/primitives/schema-chip-list.tsx",
      "content": "\"use client\";\n\nimport { X } from \"lucide-react\";\n\nimport { Button } from \"@/components/ui/button\";\n\nexport interface SchemaChipItem {\n  id: string;\n  inputLabel: string;\n  removeLabel: string;\n  value: string;\n}\n\nexport interface SchemaChipListProps {\n  editable: boolean;\n  items: SchemaChipItem[];\n  onRemove: (id: string) => void;\n  onReplace: (id: string, value: string) => void;\n}\n\nexport function SchemaChipList({\n  editable,\n  items,\n  onRemove,\n  onReplace,\n}: SchemaChipListProps) {\n  return (\n    <div data-slot=\"schema-chip-list\" className=\"space-y-2\">\n      {items.length > 0 ? (\n        <div className=\"mb-2 flex flex-wrap gap-2\">\n          {items.map((item) => {\n            return (\n              <div\n                key={item.id}\n                data-slot=\"schema-chip\"\n                className=\"border-border bg-muted flex items-center gap-1 rounded-md border px-1 shadow-none\"\n              >\n                <input\n                  aria-label={item.inputLabel}\n                  data-slot=\"schema-chip-input\"\n                  disabled={!editable}\n                  value={item.value}\n                  onChange={(event: React.ChangeEvent<HTMLInputElement>) => {\n                    onReplace(item.id, event.target.value);\n                  }}\n                  onKeyDown={(event: React.KeyboardEvent<HTMLInputElement>) => {\n                    if (event.key === \"Enter\") {\n                      event.stopPropagation();\n                    }\n                  }}\n                  className=\"h-6 w-24 min-w-0 rounded-[inherit] border-0 bg-transparent px-1 text-sm leading-6 shadow-none outline-none focus-visible:ring-0 focus-visible:ring-offset-0 disabled:opacity-64\"\n                />\n                <Button\n                  type=\"button\"\n                  disabled={!editable}\n                  variant=\"ghost\"\n                  size=\"icon\"\n                  className=\"h-6 w-6 rounded-sm border-0 bg-transparent p-0 shadow-none hover:bg-transparent data-pressed:bg-transparent\"\n                  aria-label={item.removeLabel}\n                  onClick={() => onRemove(item.id)}\n                >\n                  <X className=\"h-3 w-3\" />\n                </Button>\n              </div>\n            );\n          })}\n        </div>\n      ) : null}\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/primitives/schema-chip-list.tsx"
    },
    {
      "path": "components/schema-editor/primitives/schema-field-row.tsx",
      "content": "\"use client\";\n\nimport type * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\nimport {\n  SchemaRowGrip,\n  type SchemaRowGripMode,\n} from \"@/components/schema-editor/primitives/schema-row-grip\";\n\ninterface SchemaFieldRowProps {\n  id?: string;\n  grip: SchemaRowGripMode;\n  name: React.ReactNode;\n  description: React.ReactNode;\n  actions?: React.ReactNode;\n  type: React.ReactNode;\n  children?: React.ReactNode;\n  className?: string;\n  bodyClassName?: string;\n}\n\nexport function SchemaFieldRow({\n  id,\n  grip,\n  name,\n  description,\n  actions,\n  type,\n  children,\n  className,\n  bodyClassName,\n}: SchemaFieldRowProps) {\n  return (\n    <div\n      id={id}\n      data-slot=\"schema-field-row\"\n      className={cn(\"group/row\", className)}\n    >\n      <div className=\"hover:bg-accent flex min-h-12 flex-col items-start justify-between py-0 sm:flex-row sm:items-center\">\n        <SchemaRowGrip mode={grip} />\n        <div className=\"flex min-w-0 flex-1 items-center space-x-2\">\n          {name}\n          <div className=\"flex min-w-0 flex-1 items-center gap-1\">\n            {description}\n          </div>\n        </div>\n        <div className=\"flex items-center gap-2 pr-1\">\n          {actions}\n          {type}\n        </div>\n      </div>\n      {children ? <div className={bodyClassName}>{children}</div> : null}\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/primitives/schema-field-row.tsx"
    },
    {
      "path": "components/schema-editor/primitives/schema-inline-description.tsx",
      "content": "\"use client\";\n\nimport {\n  Tooltip,\n  TooltipContent,\n  TooltipTrigger,\n} from \"@/components/ui/tooltip\";\nimport { SchemaInlineText } from \"@/components/schema-editor/primitives/schema-inline-text\";\n\ninterface SchemaInlineDescriptionProps {\n  ariaLabel: string;\n  editable: boolean;\n  value: string;\n  placeholder?: string;\n  onOpenDetails?: () => void;\n  onCommit: (value: string) => void;\n}\n\nexport function SchemaInlineDescription({\n  ariaLabel,\n  editable,\n  value,\n  placeholder = \"Add description\",\n  onOpenDetails,\n  onCommit,\n}: SchemaInlineDescriptionProps) {\n  const description = (\n    <SchemaInlineText\n      ariaLabel={ariaLabel}\n      editable={editable}\n      value={value}\n      placeholder={placeholder}\n      className=\"text-muted-foreground placeholder:text-muted-foreground/70 hover:bg-accent hover:text-foreground focus:text-foreground m-0 h-6 min-w-[140px] flex-1 cursor-text rounded-sm border-none bg-transparent px-1 !text-xs leading-6 shadow-none outline-none focus-visible:ring-0\"\n      readOnlyClassName=\"flex h-6 min-w-[140px] flex-1 items-center truncate rounded-sm px-1 !text-xs text-muted-foreground\"\n      onOpenReadOnly={onOpenDetails}\n      onCommit={onCommit}\n    />\n  );\n\n  if (editable || !value) return description;\n\n  return (\n    <Tooltip>\n      <TooltipTrigger asChild>{description}</TooltipTrigger>\n      <TooltipContent className=\"max-w-xs\">\n        <div className=\"text-muted-foreground mb-1 text-xs\">Description:</div>\n        <div className=\"text-xs\">{value}</div>\n      </TooltipContent>\n    </Tooltip>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/primitives/schema-inline-description.tsx"
    },
    {
      "path": "components/schema-editor/primitives/schema-inline-name.tsx",
      "content": "\"use client\";\n\nimport { EyeIcon } from \"lucide-react\";\n\nimport { Button } from \"@/components/ui/button\";\nimport { SchemaInlineText } from \"@/components/schema-editor/primitives/schema-inline-text\";\n\ninterface SchemaInlineNameProps {\n  ariaLabel: string;\n  editable: boolean;\n  value: string;\n  canRename?: boolean;\n  reference?: {\n    label: string;\n    onReveal: () => void;\n  };\n  validate?: (value: string) => string | null;\n  onCommit?: (value: string) => void;\n}\n\nexport function SchemaInlineName({\n  ariaLabel,\n  editable,\n  value,\n  canRename = true,\n  reference,\n  validate = () => null,\n  onCommit,\n}: SchemaInlineNameProps) {\n  const canEditName = editable && canRename;\n\n  return (\n    <span className=\"flex min-w-0 items-center\">\n      <SchemaInlineText\n        ariaLabel={ariaLabel}\n        editable={canEditName}\n        value={value}\n        className=\"text-foreground m-0 h-6 w-36 rounded-sm border-none bg-transparent px-1 text-sm font-medium shadow-none outline-none focus-visible:ring-0\"\n        readOnlyClassName=\"mr-1 truncate text-sm font-medium whitespace-nowrap text-foreground\"\n        validate={validate}\n        onCommit={(nextValue) => {\n          if (nextValue) onCommit?.(nextValue);\n        }}\n      />\n      {reference && (\n        <Button\n          type=\"button\"\n          variant=\"ghost\"\n          size=\"icon\"\n          className=\"h-4 w-4 p-0\"\n          aria-label={`Show ${reference.label} definition`}\n          onClick={reference.onReveal}\n        >\n          <EyeIcon className=\"text-muted-foreground h-4 w-4\" />\n        </Button>\n      )}\n    </span>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/primitives/schema-inline-name.tsx"
    },
    {
      "path": "components/schema-editor/primitives/schema-inline-text.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { AlertCircle } from \"lucide-react\";\n\nimport { cn } from \"@/lib/utils\";\nimport { useKeyedMountEffect } from \"@/hooks/use-keyed-mount-effect\";\nimport { joinEffectKey } from \"@/lib/effect-key\";\n\ninterface SchemaInlineTextProps {\n  ariaLabel: string;\n  editable: boolean;\n  value: string;\n  className?: string;\n  errorClassName?: string;\n  placeholder?: string;\n  readOnlyClassName?: string;\n  trimOnCommit?: boolean;\n  validate?: (value: string) => string | null;\n  onCommit: (value: string) => void;\n  onOpenReadOnly?: () => void;\n}\n\nexport function SchemaInlineText({\n  ariaLabel,\n  editable,\n  value,\n  className,\n  errorClassName,\n  placeholder,\n  readOnlyClassName,\n  trimOnCommit = true,\n  validate = () => null,\n  onCommit,\n  onOpenReadOnly,\n}: SchemaInlineTextProps) {\n  const isFocusedRef = React.useRef(false);\n  const draftValueRef = React.useRef(value);\n  const [inputResetVersion, setInputResetVersion] = React.useState(0);\n  const [error, setError] = React.useState<string | null>(null);\n\n  useKeyedMountEffect(joinEffectKey([value]), () => {\n    if (isFocusedRef.current || draftValueRef.current === value) return;\n    draftValueRef.current = value;\n    setInputResetVersion((version) => version + 1);\n  });\n\n  const normalizeValue = (nextValue: string) =>\n    trimOnCommit ? nextValue.trim() : nextValue;\n\n  const commitValue = (input: HTMLInputElement) => {\n    const nextValue = normalizeValue(input.value);\n    const nextError = validate(nextValue);\n    if (nextError) {\n      setError(nextError);\n      return false;\n    }\n\n    setError(null);\n    isFocusedRef.current = false;\n    draftValueRef.current = nextValue;\n    input.value = nextValue;\n    if (nextValue !== normalizeValue(value)) {\n      onCommit(nextValue);\n    }\n    return true;\n  };\n\n  if (!editable) {\n    return (\n      <span className={readOnlyClassName} onClick={onOpenReadOnly}>\n        {value || (\n          <span className=\"text-muted-foreground/70\">{placeholder}</span>\n        )}\n      </span>\n    );\n  }\n\n  return (\n    <span className=\"relative flex min-w-0 items-center\">\n      <input\n        key={inputResetVersion}\n        aria-label={ariaLabel}\n        aria-invalid={Boolean(error)}\n        className={className}\n        data-slot=\"schema-inline-input\"\n        placeholder={placeholder}\n        defaultValue={value}\n        onFocus={() => {\n          isFocusedRef.current = true;\n        }}\n        onChange={(event) => {\n          const nextValue = event.target.value;\n          draftValueRef.current = nextValue;\n          setError(nextValue ? validate(normalizeValue(nextValue)) : null);\n        }}\n        onBlur={(event) => {\n          commitValue(event.currentTarget);\n        }}\n        onKeyDown={(event) => {\n          if (event.key === \"Enter\") {\n            event.preventDefault();\n            event.stopPropagation();\n            if (commitValue(event.currentTarget)) event.currentTarget.blur();\n          } else if (event.key === \"Escape\") {\n            event.preventDefault();\n            event.stopPropagation();\n            draftValueRef.current = value;\n            event.currentTarget.value = value;\n            setError(null);\n            isFocusedRef.current = false;\n            event.currentTarget.blur();\n          }\n        }}\n      />\n      {error && (\n        <p\n          className={cn(\n            \"bg-background text-destructive absolute top-7 left-1 z-10 flex min-w-56 items-center gap-1 rounded-sm border px-2 py-1 text-xs shadow-sm\",\n            errorClassName,\n          )}\n        >\n          <AlertCircle className=\"h-3 w-3\" /> {error}\n        </p>\n      )}\n    </span>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/primitives/schema-inline-text.tsx"
    },
    {
      "path": "components/schema-editor/primitives/schema-order.ts",
      "content": "export function moveOrderedItem<T>({\n  items,\n  sourceIndex,\n  targetIndex,\n}: {\n  items: readonly T[];\n  sourceIndex: number;\n  targetIndex: number;\n}): T[] {\n  const nextItems = items.slice();\n  if (sourceIndex < 0 || sourceIndex >= nextItems.length) return nextItems;\n\n  const [movedItem] = nextItems.splice(sourceIndex, 1);\n  const clampedTargetIndex = Math.max(\n    0,\n    Math.min(targetIndex, nextItems.length),\n  );\n  nextItems.splice(clampedTargetIndex, 0, movedItem);\n  return nextItems;\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/primitives/schema-order.ts"
    },
    {
      "path": "components/schema-editor/primitives/schema-row-actions.tsx",
      "content": "\"use client\";\n\nimport { Eye, Pencil, Trash2 } from \"lucide-react\";\n\nimport { Button } from \"@/components/ui/button\";\nimport {\n  Tooltip,\n  TooltipContent,\n  TooltipTrigger,\n} from \"@/components/ui/tooltip\";\n\ninterface SchemaRowActionsProps {\n  canDelete: boolean;\n  deleteLabel?: string;\n  details?: {\n    label: string;\n    mode: \"edit\" | \"view\";\n    onOpen: () => void;\n  };\n  editable: boolean;\n  onDelete?: () => void;\n}\n\nexport function SchemaRowActions({\n  canDelete,\n  deleteLabel = \"Delete field\",\n  details,\n  editable,\n  onDelete,\n}: SchemaRowActionsProps) {\n  return (\n    <>\n      {editable && canDelete && (\n        <Button\n          type=\"button\"\n          variant=\"ghost\"\n          size=\"iconSm\"\n          className=\"m-0 shrink-0 p-0\"\n          aria-label={deleteLabel}\n          onClick={onDelete}\n        >\n          <Trash2 className=\"text-primary-foreground group-hover/row:text-muted-foreground size-4\" />\n        </Button>\n      )}\n\n      {details && (\n        <Tooltip>\n          <TooltipTrigger asChild>\n            <Button\n              type=\"button\"\n              variant=\"ghost\"\n              size=\"iconSm\"\n              className=\"m-0 shrink-0 p-0\"\n              aria-label={details.label}\n              onClick={details.onOpen}\n            >\n              {details.mode === \"edit\" ? (\n                <Pencil className=\"text-muted-foreground size-4 opacity-0 group-hover/row:opacity-100\" />\n              ) : (\n                <Eye className=\"text-muted-foreground size-4 opacity-0 group-hover/row:opacity-100\" />\n              )}\n            </Button>\n          </TooltipTrigger>\n          <TooltipContent className=\"max-w-xs\">\n            <p>{details.label}</p>\n          </TooltipContent>\n        </Tooltip>\n      )}\n    </>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/primitives/schema-row-actions.tsx"
    },
    {
      "path": "components/schema-editor/primitives/schema-row-drag.ts",
      "content": "import type * as React from \"react\";\n\nexport interface SchemaRowDragItem {\n  id: string;\n  label: string;\n}\n\nexport type SchemaRowDropPlacement = \"before\" | \"after\";\nexport type SchemaRowDropIndicator = SchemaRowDropPlacement | null;\n\ninterface SchemaRowDropTargetRect {\n  height: number;\n  top: number;\n}\n\nconst ROW_DRAG_FORMAT = \"text/plain\";\nconst DROP_CLASSES = [\n  \"border-t-2\",\n  \"border-b-2\",\n  \"border-grey-700\",\n  \"border-dashed\",\n] as const;\n\nexport function getSchemaRowDropPlacement({\n  clientY,\n  targetRect,\n}: {\n  clientY: number;\n  targetRect: SchemaRowDropTargetRect;\n}): SchemaRowDropPlacement {\n  return clientY < targetRect.top + targetRect.height / 2 ? \"before\" : \"after\";\n}\n\nexport function getSchemaRowDropClasses(\n  indicator: SchemaRowDropIndicator,\n): string[] {\n  if (!indicator) return [];\n  return [\n    indicator === \"before\" ? \"border-t-2\" : \"border-b-2\",\n    \"border-grey-700\",\n    \"border-dashed\",\n  ];\n}\n\nexport function clearSchemaRowDropClasses(element: HTMLElement) {\n  element.classList.remove(...DROP_CLASSES);\n}\n\nexport function applySchemaRowDropClasses(\n  element: HTMLElement,\n  indicator: SchemaRowDropIndicator,\n) {\n  // Native dragover fires continuously; keep the drop indicator out of React state.\n  clearSchemaRowDropClasses(element);\n  const classes = getSchemaRowDropClasses(indicator);\n  if (classes.length) element.classList.add(...classes);\n}\n\nexport function getSchemaRowDropTargetIndex({\n  placement,\n  rowIds,\n  sourceRowId,\n  targetRowId,\n}: {\n  placement: SchemaRowDropPlacement;\n  rowIds: string[];\n  sourceRowId: string;\n  targetRowId: string;\n}) {\n  const sourceIndex = rowIds.indexOf(sourceRowId);\n  if (sourceIndex < 0 || sourceRowId === targetRowId) return -1;\n\n  const remainingRowIds = rowIds.filter((rowId) => rowId !== sourceRowId);\n  const targetIndex = remainingRowIds.indexOf(targetRowId);\n  if (targetIndex < 0) return -1;\n\n  return placement === \"after\" ? targetIndex + 1 : targetIndex;\n}\n\nexport function beginSchemaRowDrag({\n  event,\n  item,\n  draggedRowIdRef,\n}: {\n  event: React.DragEvent<HTMLElement>;\n  item: SchemaRowDragItem;\n  draggedRowIdRef: React.RefObject<string | null>;\n}) {\n  event.stopPropagation();\n  event.dataTransfer.setData(ROW_DRAG_FORMAT, item.id);\n  event.dataTransfer.effectAllowed = \"move\";\n  draggedRowIdRef.current = item.id;\n\n  const dragElement = createSchemaRowDragPreview({\n    sourceElement: event.currentTarget,\n    label: item.label,\n  });\n  event.dataTransfer.setDragImage(dragElement, 10, 10);\n  removeSchemaRowDragPreviewAfterFrame(dragElement);\n}\n\nexport function updateSchemaRowDragTarget({\n  event,\n  rowIds,\n  targetRowId,\n  draggedRowIdRef,\n}: {\n  event: React.DragEvent<HTMLElement>;\n  rowIds: string[];\n  targetRowId: string;\n  draggedRowIdRef: React.RefObject<string | null>;\n}) {\n  event.preventDefault();\n  const indicator =\n    draggedRowIdRef.current &&\n    draggedRowIdRef.current !== targetRowId &&\n    rowIds.includes(draggedRowIdRef.current) &&\n    rowIds.includes(targetRowId)\n      ? getSchemaRowDropPlacement({\n          clientY: event.clientY,\n          targetRect: event.currentTarget.getBoundingClientRect(),\n        })\n      : null;\n\n  event.dataTransfer.dropEffect = \"move\";\n  applySchemaRowDropClasses(event.currentTarget, indicator);\n}\n\nexport function leaveSchemaRowDragTarget(\n  event: Pick<\n    React.DragEvent<HTMLElement>,\n    \"currentTarget\" | \"stopPropagation\"\n  >,\n) {\n  event.stopPropagation();\n  clearSchemaRowDropClasses(event.currentTarget);\n}\n\nexport function resolveSchemaRowDrop({\n  event,\n  rowIds,\n  targetRowId,\n  draggedRowIdRef,\n}: {\n  event: React.DragEvent<HTMLElement>;\n  rowIds: string[];\n  targetRowId: string;\n  draggedRowIdRef: React.RefObject<string | null>;\n}): {\n  placement: SchemaRowDropPlacement;\n  sourceRowId: string;\n  targetRowId: string;\n  targetIndex: number;\n} | null {\n  event.stopPropagation();\n  event.preventDefault();\n  const sourceRowId = event.dataTransfer.getData(ROW_DRAG_FORMAT);\n  clearSchemaRowDropClasses(event.currentTarget);\n\n  if (\n    !sourceRowId ||\n    sourceRowId === targetRowId ||\n    draggedRowIdRef.current !== sourceRowId ||\n    !rowIds.includes(sourceRowId) ||\n    !rowIds.includes(targetRowId)\n  ) {\n    return null;\n  }\n\n  const placement = getSchemaRowDropPlacement({\n    clientY: event.clientY,\n    targetRect: event.currentTarget.getBoundingClientRect(),\n  });\n  const targetIndex = getSchemaRowDropTargetIndex({\n    placement,\n    rowIds,\n    sourceRowId,\n    targetRowId,\n  });\n  if (targetIndex < 0) return null;\n\n  return {\n    placement,\n    sourceRowId,\n    targetRowId,\n    targetIndex,\n  };\n}\n\nfunction createSchemaRowDragPreview({\n  sourceElement,\n  label,\n}: {\n  sourceElement: HTMLElement;\n  label: string;\n}) {\n  const dragElement = document.createElement(\"div\");\n  const rect = sourceElement.getBoundingClientRect();\n\n  Object.assign(dragElement.style, {\n    width: `${rect.width}px`,\n    padding: \"8px\",\n    border: \"1px solid var(--ring)\",\n    borderRadius: \"4px\",\n    backgroundColor: \"var(--background)\",\n    boxShadow: \"0 2px 5px rgba(0,0,0,0.1)\",\n    opacity: \"0.8\",\n    position: \"fixed\",\n    zIndex: \"9999\",\n    pointerEvents: \"none\",\n  });\n  dragElement.textContent = label;\n  document.body.appendChild(dragElement);\n\n  return dragElement;\n}\n\nfunction removeSchemaRowDragPreviewAfterFrame(dragElement: HTMLElement) {\n  window.requestAnimationFrame(() => {\n    dragElement.remove();\n  });\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/primitives/schema-row-drag.ts"
    },
    {
      "path": "components/schema-editor/primitives/schema-row-grip.tsx",
      "content": "\"use client\";\n\nimport { GripVertical } from \"lucide-react\";\n\nimport { cn } from \"@/lib/utils\";\n\nexport type SchemaRowGripMode = \"drag\" | \"static\" | \"empty\";\n\ninterface SchemaRowGripProps {\n  mode: SchemaRowGripMode;\n  className?: string;\n}\n\nexport function SchemaRowGrip({ mode, className }: SchemaRowGripProps) {\n  if (mode === \"empty\") {\n    return <div className={cn(\"h-12 w-6 px-1 py-4\", className)} />;\n  }\n\n  return (\n    <GripVertical\n      aria-hidden=\"true\"\n      className={cn(\n        \"h-12 w-6 px-1 py-4\",\n        mode === \"drag\"\n          ? \"group-hover/row:text-muted-foreground cursor-grab text-transparent\"\n          : \"text-muted-foreground\",\n        className,\n      )}\n    />\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/primitives/schema-row-grip.tsx"
    },
    {
      "path": "components/schema-editor/primitives/schema-type-menu.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { ChevronDown } from \"lucide-react\";\n\nimport { Button } from \"@/components/ui/button\";\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuPortal,\n  DropdownMenuSub,\n  DropdownMenuSubContent,\n  DropdownMenuSubTrigger,\n  DropdownMenuTrigger,\n} from \"@/components/ui/dropdown-menu\";\n\nexport type SchemaTypeMenuVariant = \"row\" | \"form\";\n\nexport interface SchemaTypeMenuValue {\n  id: string;\n  label: string;\n  icon: React.ReactNode;\n}\n\nexport interface SchemaTypeMenuItem {\n  id: string;\n  label: string;\n  icon?: React.ReactNode;\n  closeOnSelect?: boolean;\n  onSelect: () => void;\n}\n\nexport type SchemaTypeMenuSection =\n  | {\n      id: string;\n      kind: \"items\";\n      items: SchemaTypeMenuItem[];\n    }\n  | {\n      id: string;\n      kind: \"submenu\";\n      label: string;\n      icon?: React.ReactNode;\n      items: SchemaTypeMenuItem[];\n    };\n\nexport type SchemaTypeMenuTrailingContent = (context: {\n  editable: boolean;\n}) => React.ReactNode;\n\ninterface SchemaTypeMenuProps {\n  ariaLabel?: string;\n  editable: boolean;\n  sections: SchemaTypeMenuSection[];\n  trailingContent?: SchemaTypeMenuTrailingContent;\n  value: SchemaTypeMenuValue;\n  variant: SchemaTypeMenuVariant;\n}\n\nexport function SchemaTypeMenu({\n  ariaLabel,\n  editable,\n  sections,\n  trailingContent,\n  value,\n  variant,\n}: SchemaTypeMenuProps) {\n  const runMenuAction = (event: Event, item: SchemaTypeMenuItem) => {\n    if (!editable) {\n      event.preventDefault();\n      return;\n    }\n    if (item.closeOnSelect === false) {\n      event.preventDefault();\n    }\n    item.onSelect();\n  };\n\n  if (!editable && variant === \"row\") {\n    return <div className=\"ml-4 w-40 text-xs\">{value.label || \"string\"}</div>;\n  }\n\n  return (\n    <DropdownMenu>\n      <DropdownMenuTrigger asChild>\n        <Button\n          aria-label={ariaLabel ?? (variant === \"row\" ? value.id : undefined)}\n          disabled={!editable}\n          variant={variant === \"row\" ? \"ghost\" : \"outline\"}\n          className={\n            variant === \"row\"\n              ? \"text-muted-foreground w-40 justify-between pr-1 pl-2 text-xs font-normal\"\n              : \"mt-2 w-full justify-between pr-1 pl-2\"\n          }\n        >\n          <div\n            className={\n              variant === \"row\"\n                ? \"flex min-w-0 items-center gap-2\"\n                : \"flex items-center gap-2\"\n            }\n          >\n            {value.icon}\n            <span className={variant === \"row\" ? \"truncate\" : \"\"}>\n              {value.label}\n            </span>\n          </div>\n          <ChevronDown\n            className={\n              variant === \"row\"\n                ? \"text-muted-foreground mr-1! h-4 w-4 shrink-0 opacity-0 group-hover/row:opacity-100\"\n                : \"mr-1! h-4 w-4\"\n            }\n          />\n        </Button>\n      </DropdownMenuTrigger>\n      <DropdownMenuContent className={variant === \"form\" ? \"w-full\" : \"\"}>\n        {sections.map((section) => {\n          if (section.kind === \"submenu\") {\n            return (\n              <DropdownMenuSub key={section.id}>\n                <DropdownMenuSubTrigger>\n                  {section.icon}\n                  {section.label}\n                </DropdownMenuSubTrigger>\n                <DropdownMenuPortal>\n                  <DropdownMenuSubContent>\n                    {section.items.map((item) => (\n                      <SchemaTypeMenuDropdownItem\n                        key={item.id}\n                        editable={editable}\n                        item={item}\n                        onSelect={runMenuAction}\n                      />\n                    ))}\n                  </DropdownMenuSubContent>\n                </DropdownMenuPortal>\n              </DropdownMenuSub>\n            );\n          }\n\n          return section.items.map((item) => (\n            <SchemaTypeMenuDropdownItem\n              key={item.id}\n              editable={editable}\n              item={item}\n              onSelect={runMenuAction}\n            />\n          ));\n        })}\n        {trailingContent?.({ editable })}\n      </DropdownMenuContent>\n    </DropdownMenu>\n  );\n}\n\nfunction SchemaTypeMenuDropdownItem({\n  editable,\n  item,\n  onSelect,\n}: {\n  editable: boolean;\n  item: SchemaTypeMenuItem;\n  onSelect: (event: Event, item: SchemaTypeMenuItem) => void;\n}) {\n  return (\n    <DropdownMenuItem\n      disabled={!editable}\n      onSelect={(event) => onSelect(event, item)}\n    >\n      {item.icon}\n      {item.label}\n    </DropdownMenuItem>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/primitives/schema-type-menu.tsx"
    },
    {
      "path": "components/schema-editor/primitives/schema-type-options.ts",
      "content": "\"use client\";\n\nimport type * as React from \"react\";\n\nimport {\n  getTemplateIcon,\n  getTypeIcon,\n} from \"@/components/schema-editor/type-icons\";\n\nexport type SchemaTypeOptionId =\n  | \"string\"\n  | \"number\"\n  | \"integer\"\n  | \"boolean\"\n  | \"enum\"\n  | \"object\"\n  | \"array\"\n  | \"date\"\n  | \"time\"\n  | \"datetime\";\n\nexport interface SchemaTypeOption {\n  id: SchemaTypeOptionId;\n  label: string;\n  icon: React.ReactNode;\n}\n\nconst schemaTypeOptionLabels: Array<[SchemaTypeOptionId, string]> = [\n  [\"string\", \"string\"],\n  [\"number\", \"number\"],\n  [\"integer\", \"integer\"],\n  [\"boolean\", \"true/false\"],\n  [\"enum\", \"multiple choice\"],\n  [\"object\", \"object\"],\n  [\"array\", \"list\"],\n  [\"date\", \"date\"],\n  [\"time\", \"time\"],\n  [\"datetime\", \"timestamp\"],\n];\n\nexport const schemaTypeOptions: SchemaTypeOption[] = schemaTypeOptionLabels.map(\n  ([id, label]) => ({\n    id,\n    label,\n    icon: getTypeIcon(id),\n  }),\n);\n\nexport function schemaTypeLabel(type: string, refName?: string) {\n  if (type === \"$ref\" && refName) return refName;\n  if (type === \"boolean\") return \"true/false\";\n  if (type === \"enum\") return \"multiple choice\";\n  if (type === \"array\") return \"list\";\n  if (type === \"datetime\") return \"timestamp\";\n  return type || \"Select type\";\n}\n\nexport function schemaTypeIcon(\n  type: string,\n  refName?: string,\n): React.ReactNode {\n  if (type === \"$ref\" && refName) return getTemplateIcon(refName);\n  return getTypeIcon(type);\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/primitives/schema-type-options.ts"
    },
    {
      "path": "components/schema-editor/property-form/fields/array-items-field.tsx",
      "content": "\"use client\";\n\nimport type * as React from \"react\";\n\nimport { Label } from \"@/components/ui/label\";\n\nexport function ArrayItemsField({ children }: { children: React.ReactNode }) {\n  return (\n    <div className=\"space-y-3 rounded-md border p-3\">\n      <Label className=\"text-muted-foreground text-xs\">List item type</Label>\n      {children}\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/property-form/fields/array-items-field.tsx"
    },
    {
      "path": "components/schema-editor/property-form/fields/description-field.tsx",
      "content": "\"use client\";\n\nimport { FormItem } from \"@/components/ui/form\";\nimport { Label } from \"@/components/ui/label\";\nimport { Textarea } from \"@/components/ui/textarea\";\n\nexport function DescriptionField({\n  value,\n  disabled,\n  onChange,\n}: {\n  value: string;\n  disabled: boolean;\n  onChange: (description: string) => void;\n}) {\n  return (\n    <FormItem className=\"group\">\n      <Label htmlFor=\"description\">Description</Label>\n      <Textarea\n        id=\"description\"\n        value={value}\n        onChange={(event) => onChange(event.target.value)}\n        disabled={disabled}\n        className={disabled ? \"disabled:opacity-100\" : \"\"}\n      />\n    </FormItem>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/property-form/fields/description-field.tsx"
    },
    {
      "path": "components/schema-editor/property-form/fields/enum-value-identity.ts",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport type { JSONSchema7Type } from \"json-schema\";\n\nexport function useEnumValueIdentity({\n  resetKey,\n  values,\n}: {\n  resetKey: string;\n  values: JSONSchema7Type[];\n}) {\n  const resetKeyRef = React.useRef(resetKey);\n  const nextIdRef = React.useRef(0);\n  const idsRef = React.useRef<string[]>([]);\n\n  if (resetKeyRef.current !== resetKey) {\n    resetKeyRef.current = resetKey;\n    nextIdRef.current = 0;\n    idsRef.current = [];\n  }\n\n  while (idsRef.current.length < values.length) {\n    idsRef.current.push(createEnumValueId(nextIdRef.current));\n    nextIdRef.current += 1;\n  }\n\n  if (idsRef.current.length > values.length) {\n    idsRef.current.length = values.length;\n  }\n\n  return {\n    ids: idsRef.current,\n    removeId: (id: string) => {\n      const index = idsRef.current.indexOf(id);\n      if (index >= 0) {\n        idsRef.current.splice(index, 1);\n      }\n    },\n  };\n}\n\nfunction createEnumValueId(sequence: number) {\n  return `enum-value-${sequence}`;\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/property-form/fields/enum-value-identity.ts"
    },
    {
      "path": "components/schema-editor/property-form/fields/enum-values-field.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport type { JSONSchema7Type } from \"json-schema\";\n\nimport { Label } from \"@/components/ui/label\";\nimport { SchemaChipAddRow } from \"@/components/schema-editor/primitives/schema-chip-add-row\";\nimport { SchemaChipList } from \"@/components/schema-editor/primitives/schema-chip-list\";\nimport {\n  formatEnumValueInput,\n  parseEnumValueInput,\n} from \"@/components/schema-editor/property-form/model/enum-values\";\n\nimport { useEnumValueIdentity } from \"./enum-value-identity\";\n\nexport function EnumValuesField({\n  values,\n  resetKey,\n  disabled,\n  onChange,\n}: {\n  values: JSONSchema7Type[];\n  resetKey: string;\n  disabled: boolean;\n  onChange: (values: JSONSchema7Type[]) => void;\n}) {\n  const [nextValueState, setNextValueState] = React.useState({\n    resetKey,\n    value: \"\",\n  });\n  const nextValue =\n    nextValueState.resetKey === resetKey ? nextValueState.value : \"\";\n  const setNextValue = React.useCallback(\n    (value: string) => setNextValueState({ resetKey, value }),\n    [resetKey],\n  );\n  const valueIdentity = useEnumValueIdentity({ resetKey, values });\n\n  const items = values.map((value, index) => {\n    const inputValue = formatEnumValueInput(value);\n    return {\n      id: valueIdentity.ids[index],\n      inputLabel: `Option ${index + 1}: ${inputValue || \"empty\"}`,\n      removeLabel: `Remove option ${inputValue}`,\n      value: inputValue,\n    };\n  });\n\n  const indexFromId = (id: string) => valueIdentity.ids.indexOf(id);\n  const addInput = {\n    inputLabel: \"Add new value\",\n    placeholder: \"Add new value\",\n    submitLabel: \"Add\",\n    value: nextValue,\n    onChange: setNextValue,\n    onSubmit: () => {\n      onChange([...values, parseEnumValueInput(nextValue)]);\n      setNextValueState({ resetKey, value: \"\" });\n    },\n  };\n\n  return (\n    <div className=\"space-y-2\">\n      <Label className=\"text-muted-foreground text-xs\">Enabled options</Label>\n      <SchemaChipList\n        editable={!disabled}\n        items={items}\n        onRemove={(id) => {\n          const index = indexFromId(id);\n          valueIdentity.removeId(id);\n          onChange(values.filter((_value, current) => current !== index));\n        }}\n        onReplace={(id, value) => {\n          const index = indexFromId(id);\n          const nextValues = values.slice();\n          nextValues[index] = parseEnumValueInput(value);\n          onChange(nextValues);\n        }}\n      />\n      <SchemaChipAddRow addInput={addInput} editable={!disabled} />\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/property-form/fields/enum-values-field.tsx"
    },
    {
      "path": "components/schema-editor/property-form/fields/name-field.tsx",
      "content": "\"use client\";\n\nimport { AlertCircle } from \"lucide-react\";\n\nimport type { FieldValidation } from \"@/components/schema-editor/property-form/types\";\nimport { Input } from \"@/components/ui/input\";\nimport { Label } from \"@/components/ui/label\";\n\nexport function NameField({\n  value,\n  disabled,\n  validation,\n  onChange,\n}: {\n  value: string;\n  disabled: boolean;\n  validation: FieldValidation;\n  onChange: (name: string) => void;\n}) {\n  const message =\n    validation.status === \"invalid\" ? validation.message : undefined;\n\n  return (\n    <div className=\"grid gap-2\">\n      <Label htmlFor=\"name\">Name</Label>\n      <Input\n        id=\"name\"\n        disabled={disabled}\n        value={value}\n        onChange={(event) => onChange(event.target.value)}\n        className={`${message ? \"border-destructive\" : \"\"} ${disabled ? \"disabled:opacity-100\" : \"\"}`}\n        placeholder=\"e.g. first_name or firstName\"\n        required\n        aria-invalid={Boolean(message)}\n      />\n      {message && (\n        <p className=\"text-destructive mt-1 flex items-center gap-1 text-sm font-medium\">\n          <AlertCircle className=\"h-3 w-3\" />\n          {message}\n        </p>\n      )}\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/property-form/fields/name-field.tsx"
    },
    {
      "path": "components/schema-editor/property-form/fields/nullable-field.tsx",
      "content": "\"use client\";\n\nimport { FormItem } from \"@/components/ui/form\";\nimport { Label } from \"@/components/ui/label\";\nimport { Switch } from \"@/components/ui/switch\";\nimport {\n  Tooltip,\n  TooltipContent,\n  TooltipTrigger,\n} from \"@/components/ui/tooltip\";\n\nexport function NullableField({\n  checked,\n  disabled,\n  onChange,\n}: {\n  checked: boolean;\n  disabled: boolean;\n  onChange: (checked: boolean) => void;\n}) {\n  return (\n    <FormItem className=\"flex flex-row items-center space-y-0 space-x-2\">\n      <Switch\n        id=\"nullable\"\n        disabled={disabled}\n        checked={checked}\n        onCheckedChange={onChange}\n        className={disabled ? \"disabled:opacity-100\" : \"\"}\n      />\n      <Tooltip>\n        <TooltipTrigger asChild>\n          <Label htmlFor=\"nullable\" className=\"cursor-pointer\">\n            Nullable\n          </Label>\n        </TooltipTrigger>\n        <TooltipContent className=\"max-w-xs\">\n          <p>\n            Nullable fields allow <code>null</code> as a value (the type is\n            widened to include <code>null</code>).\n          </p>\n        </TooltipContent>\n      </Tooltip>\n    </FormItem>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/property-form/fields/nullable-field.tsx"
    },
    {
      "path": "components/schema-editor/property-form/fields/object-properties-drag.ts",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport {\n  beginSchemaRowDrag,\n  leaveSchemaRowDragTarget,\n  resolveSchemaRowDrop,\n  updateSchemaRowDragTarget,\n} from \"@/components/schema-editor/primitives/schema-row-drag\";\n\nimport type { ObjectPropertyRowModel } from \"@/components/schema-editor/property-form/model/object-properties-view\";\n\nexport function useObjectPropertiesRowDrag({\n  rows,\n  editable,\n}: {\n  rows: ObjectPropertyRowModel[];\n  editable: boolean;\n}) {\n  const draggedRowIdRef = React.useRef<string | null>(null);\n  const rowIds = rows.map((row) => row.id);\n\n  const getRowDragProps = (row: ObjectPropertyRowModel) => {\n    if (!editable) {\n      return {\n        draggable: false,\n        onDragStart: noopDragHandler,\n        onDragOver: noopDragHandler,\n        onDragLeave: noopDragHandler,\n        onDrop: noopDragHandler,\n      };\n    }\n\n    return {\n      draggable: true,\n      onDragStart: (event: React.DragEvent<HTMLDivElement>) => {\n        beginSchemaRowDrag({\n          event,\n          item: {\n            id: row.id,\n            label: row.name,\n          },\n          draggedRowIdRef,\n        });\n      },\n      onDragOver: (event: React.DragEvent<HTMLDivElement>) => {\n        updateSchemaRowDragTarget({\n          event,\n          rowIds,\n          targetRowId: row.id,\n          draggedRowIdRef,\n        });\n      },\n      onDragLeave: leaveSchemaRowDragTarget,\n      onDrop: (event: React.DragEvent<HTMLDivElement>) => {\n        const move = resolveSchemaRowDrop({\n          event,\n          rowIds,\n          targetRowId: row.id,\n          draggedRowIdRef,\n        });\n        if (!move) return;\n\n        const sourceRow = rows.find(\n          (candidate) => candidate.id === move.sourceRowId,\n        );\n        sourceRow?.reorder.move(move.targetIndex);\n      },\n    };\n  };\n\n  return {\n    rowIds,\n    getRowDragProps,\n  };\n}\n\nfunction noopDragHandler() {}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/property-form/fields/object-properties-drag.ts"
    },
    {
      "path": "components/schema-editor/property-form/fields/object-properties-field.tsx",
      "content": "\"use client\";\n\nimport type * as React from \"react\";\n\nimport { SchemaAddRow } from \"@/components/schema-editor/primitives/schema-add-row\";\nimport type { PropertyObjectPropertiesFieldModel } from \"@/components/schema-editor/property-form/model/object-properties-view\";\nimport type { PropertySchemaPlan } from \"@/components/schema-editor/property-form/types\";\n\nimport { ObjectPropertyRows } from \"./object-property-row\";\n\nexport function ObjectPropertiesField({\n  model,\n  renderPlan,\n}: {\n  model: PropertyObjectPropertiesFieldModel;\n  renderPlan: (plan: PropertySchemaPlan) => React.ReactNode;\n}) {\n  return (\n    <>\n      <ObjectPropertyRows model={model} renderPlan={renderPlan} />\n\n      <SchemaAddRow\n        {...model.addInput}\n        className=\"border-border ml-4 border-l pl-4\"\n        disabled={!model.editable}\n      />\n    </>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/property-form/fields/object-properties-field.tsx"
    },
    {
      "path": "components/schema-editor/property-form/fields/object-properties-model.ts",
      "content": "\"use client\";\n\nimport type { PropertyObjectPropertiesFieldModel } from \"@/components/schema-editor/property-form/model/object-properties-view\";\nimport type { PropertyObjectPropertiesPlan } from \"@/components/schema-editor/property-form/types\";\n\nimport { createObjectPropertyAddInput } from \"./object-property-add-input\";\nimport { createObjectPropertyOperations } from \"./object-properties-operations\";\nimport { createObjectPropertyRows } from \"./object-properties-rows\";\nimport { useObjectPropertiesState } from \"./object-properties-state\";\n\nexport function useObjectPropertiesModel(\n  plan: PropertyObjectPropertiesPlan,\n): PropertyObjectPropertiesFieldModel {\n  const state = useObjectPropertiesState(plan);\n  const operations = createObjectPropertyOperations({\n    plan,\n    state,\n  });\n  const rows = createObjectPropertyRows({ operations, plan, state });\n\n  return {\n    addInput: createObjectPropertyAddInput({\n      onSubmit: operations.addProperty,\n      state: {\n        propertyNames: state.propertyNames,\n        value: state.addInputValue,\n        setValue: state.setAddInputValue,\n      },\n    }),\n    editable: plan.editable,\n    rows,\n  };\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/property-form/fields/object-properties-model.ts"
    },
    {
      "path": "components/schema-editor/property-form/fields/object-properties-operations.ts",
      "content": "import type { ExtendedJSONSchema7 } from \"@/components/schema-editor/lib/json-schema-types\";\nimport { moveOrderedItem } from \"@/components/schema-editor/primitives/schema-order\";\nimport {\n  createObjectPropertySchema,\n  moveObjectProperty,\n  removeObjectProperty,\n  renameObjectProperty,\n  replaceObjectProperty,\n} from \"@/components/schema-editor/property-form/model/object-property-edits\";\nimport type { PropertyObjectPropertiesPlan } from \"@/components/schema-editor/property-form/types\";\n\nimport type { ObjectPropertiesState } from \"./object-properties-state\";\n\nexport interface ObjectPropertyOperations {\n  addProperty: (propertyName: string) => void;\n  moveProperty: (input: {\n    propertyName: string;\n    sourceIndex: number;\n    targetIndex: number;\n  }) => void;\n  removeProperty: (propertyName: string) => void;\n  renameProperty: (oldName: string, newName: string) => void;\n  replacePropertySchemaNode: (\n    propertyName: string,\n    propertySchema: ExtendedJSONSchema7,\n  ) => void;\n}\n\nexport function createObjectPropertyOperations({\n  plan,\n  state,\n}: {\n  plan: PropertyObjectPropertiesPlan;\n  state: ObjectPropertiesState;\n}): ObjectPropertyOperations {\n  const replacePropertySchemaNode = (\n    propertyName: string,\n    propertySchema: ExtendedJSONSchema7,\n  ) => {\n    plan.onChange(\n      replaceObjectProperty({\n        schemaNode: plan.schemaNode,\n        propertyName,\n        propertySchema,\n      }),\n    );\n  };\n\n  return {\n    addProperty: (propertyName) => {\n      state.rowIdentity.preserveAddRowForLocalPropertyNames([\n        ...state.propertyNames,\n        propertyName,\n      ]);\n      state.rowIdentity.addRowId(propertyName);\n      replacePropertySchemaNode(\n        propertyName,\n        createObjectPropertySchema(propertyName),\n      );\n      state.setAddInputValue(\"\");\n    },\n    moveProperty: ({ propertyName, sourceIndex, targetIndex }) => {\n      state.rowIdentity.preserveAddRowForLocalPropertyNames(\n        moveOrderedItem({\n          items: state.propertyNames,\n          sourceIndex,\n          targetIndex,\n        }),\n      );\n      plan.onChange(\n        moveObjectProperty({\n          schemaNode: plan.schemaNode,\n          propertyName,\n          targetIndex,\n        }),\n      );\n    },\n    removeProperty: (propertyName) => {\n      state.rowIdentity.preserveAddRowForLocalPropertyNames(\n        state.propertyNames.filter((name) => name !== propertyName),\n      );\n      state.rowIdentity.removeRowId(propertyName);\n      plan.onChange(\n        removeObjectProperty({\n          schemaNode: plan.schemaNode,\n          propertyName,\n        }),\n      );\n    },\n    renameProperty: (oldName, newName) => {\n      state.rowIdentity.preserveAddRowForLocalPropertyNames(\n        state.propertyNames.map((propertyName) =>\n          propertyName === oldName ? newName : propertyName,\n        ),\n      );\n      state.rowIdentity.renameRowId(oldName, newName);\n      plan.onChange(\n        renameObjectProperty({\n          schemaNode: plan.schemaNode,\n          oldName,\n          newName,\n        }),\n      );\n    },\n    replacePropertySchemaNode,\n  };\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/property-form/fields/object-properties-operations.ts"
    },
    {
      "path": "components/schema-editor/property-form/fields/object-properties-plan-field.tsx",
      "content": "\"use client\";\n\nimport type * as React from \"react\";\n\nimport { ObjectPropertiesField } from \"@/components/schema-editor/property-form/fields/object-properties-field\";\nimport { useObjectPropertiesModel } from \"@/components/schema-editor/property-form/fields/object-properties-model\";\nimport type {\n  PropertyObjectPropertiesPlan,\n  PropertySchemaPlan,\n} from \"@/components/schema-editor/property-form/types\";\n\nexport function ObjectPropertiesPlanField({\n  plan,\n  renderPlan,\n}: {\n  plan: PropertyObjectPropertiesPlan;\n  renderPlan: (plan: PropertySchemaPlan) => React.ReactNode;\n}) {\n  const objectProperties = useObjectPropertiesModel(plan);\n  return (\n    <ObjectPropertiesField model={objectProperties} renderPlan={renderPlan} />\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/property-form/fields/object-properties-plan-field.tsx"
    },
    {
      "path": "components/schema-editor/property-form/fields/object-properties-rows.ts",
      "content": "import type { ExtendedJSONSchema7 } from \"@/components/schema-editor/lib/json-schema-types\";\nimport { isSchemaNode } from \"@/components/schema-editor/property-form/model/object-property-selectors\";\nimport type { ObjectPropertyRowModel } from \"@/components/schema-editor/property-form/model/object-properties-view\";\nimport type { PropertyObjectPropertiesPlan } from \"@/components/schema-editor/property-form/types\";\nimport { validatePropertyFormName } from \"@/components/schema-editor/property-form/validation\";\n\nimport { createObjectPropertyRowSchemaPlan } from \"./object-property-row-schema-plan\";\nimport type { ObjectPropertyOperations } from \"./object-properties-operations\";\nimport type { ObjectPropertiesState } from \"./object-properties-state\";\nimport { createPropertyTypeFieldWithObjectTemplates } from \"./property-object-template-type-field\";\n\nexport function createObjectPropertyRows({\n  operations,\n  plan,\n  state,\n}: {\n  operations: ObjectPropertyOperations;\n  plan: PropertyObjectPropertiesPlan;\n  state: ObjectPropertiesState;\n}): ObjectPropertyRowModel[] {\n  return state.propertyNames.flatMap((name, index) => {\n    const propertySchema = plan.schemaNode.properties?.[name];\n    if (!isSchemaNode(propertySchema)) return [];\n\n    const id = state.rowIdentity.getRowId(name);\n    const replaceSchemaNode = (nextSchemaNode: ExtendedJSONSchema7) => {\n      operations.replacePropertySchemaNode(name, nextSchemaNode);\n    };\n    const rowSchemaContext = {\n      ...plan.schemaContext,\n      siblingNames: state.propertyNames,\n      originalName: name,\n      fieldPath: [\n        plan.schemaContext.fieldPath ?? plan.schemaContext.originalName,\n        id,\n      ].join(\".\"),\n      resetKey: [\n        plan.schemaContext.resetKey ??\n          plan.schemaContext.fieldPath ??\n          plan.schemaContext.originalName,\n        id,\n      ].join(\".\"),\n    };\n\n    return [\n      {\n        id,\n        schemaPlan: createObjectPropertyRowSchemaPlan({\n          access: plan.access,\n          editable: plan.editable,\n          mode: plan.mode,\n          schemaNode: propertySchema,\n          schemaContext: rowSchemaContext,\n          onChange: replaceSchemaNode,\n        }),\n        name,\n        nameField: {\n          ariaLabel: `Field name ${name}`,\n          value: name,\n          editable: plan.editable,\n          validate: (value: string) =>\n            validatePropertyFormName({\n              name: value,\n              siblingNames: state.propertyNames,\n              originalName: name,\n            }),\n          onCommit: (nextName: string) => {\n            operations.renameProperty(name, nextName);\n          },\n        },\n        descriptionField: {\n          ariaLabel: `Description for ${name}`,\n          value: propertySchema.description || \"\",\n          editable: plan.editable,\n          onCommit: (description: string) => {\n            replaceSchemaNode({\n              ...propertySchema,\n              description: description || undefined,\n            });\n          },\n        },\n        reorder: {\n          move: (targetIndex: number) => {\n            operations.moveProperty({\n              propertyName: name,\n              sourceIndex: index,\n              targetIndex,\n            });\n          },\n        },\n        typeField: createPropertyTypeFieldWithObjectTemplates({\n          schemaNode: propertySchema,\n          schemaContext: rowSchemaContext,\n          editable: plan.editable && plan.access.type,\n          onChange: replaceSchemaNode,\n        }),\n        deleteAction: {\n          label: `Remove field ${name}`,\n          onDelete: () => operations.removeProperty(name),\n        },\n      },\n    ];\n  });\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/property-form/fields/object-properties-rows.ts"
    },
    {
      "path": "components/schema-editor/property-form/fields/object-properties-state.ts",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { getObjectPropertyNames } from \"@/components/schema-editor/property-form/model/object-property-selectors\";\nimport type { PropertyObjectPropertiesPlan } from \"@/components/schema-editor/property-form/types\";\n\nimport {\n  type ObjectPropertyRowIdentity,\n  useObjectPropertyRowIdentity,\n} from \"./object-property-row-identity\";\n\nexport interface ObjectPropertiesState {\n  addInputValue: string;\n  propertyNames: string[];\n  rowIdentity: ObjectPropertyRowIdentity;\n  setAddInputValue: (value: string) => void;\n}\n\nexport function useObjectPropertiesState(\n  plan: PropertyObjectPropertiesPlan,\n): ObjectPropertiesState {\n  const [addInputValue, setAddInputValue] = React.useState(\"\");\n  const propertyNames = getObjectPropertyNames(plan.schemaNode);\n  const resetAddInputValue = React.useCallback(() => {\n    setAddInputValue(\"\");\n  }, []);\n  const rowIdentity = useObjectPropertyRowIdentity({\n    onExternalPropertyNamesChange: resetAddInputValue,\n    propertyNames,\n    resetKey: plan.schemaContext.resetKey ?? plan.schemaContext.originalName,\n  });\n\n  return {\n    addInputValue,\n    propertyNames,\n    rowIdentity,\n    setAddInputValue,\n  };\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/property-form/fields/object-properties-state.ts"
    },
    {
      "path": "components/schema-editor/property-form/fields/object-property-add-input.ts",
      "content": "import type { SchemaAddInputModel } from \"@/components/schema-editor/primitives/schema-add-input-model\";\nimport { validatePropertyFormName } from \"@/components/schema-editor/property-form/validation\";\n\nexport interface ObjectPropertyAddInputState {\n  propertyNames: string[];\n  value: string;\n  setValue: (value: string) => void;\n}\n\nexport function createObjectPropertyAddInput({\n  onSubmit,\n  state,\n}: {\n  onSubmit: (propertyName: string) => void;\n  state: ObjectPropertyAddInputState;\n}): SchemaAddInputModel {\n  const trimmedValue = state.value.trim();\n  const error = trimmedValue\n    ? validatePropertyFormName({\n        name: trimmedValue,\n        siblingNames: state.propertyNames,\n        originalName: \"\",\n      })\n    : null;\n\n  return {\n    error,\n    inputLabel: \"New object field\",\n    placeholder: \"New property name\",\n    submitLabel: \"Add\",\n    value: state.value,\n    onChange: state.setValue,\n    onSubmit: () => {\n      if (!trimmedValue || error) return;\n      onSubmit(trimmedValue);\n    },\n  };\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/property-form/fields/object-property-add-input.ts"
    },
    {
      "path": "components/schema-editor/property-form/fields/object-property-row-identity.ts",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { useKeyedMountEffect } from \"@/hooks/use-keyed-mount-effect\";\nimport { joinEffectKey } from \"@/lib/effect-key\";\n\nexport interface ObjectPropertyRowIdentity {\n  addRowId: (propertyName: string) => void;\n  getRowId: (propertyName: string) => string;\n  preserveAddRowForLocalPropertyNames: (propertyNames: string[]) => void;\n  removeRowId: (propertyName: string) => void;\n  renameRowId: (oldName: string, name: string) => void;\n}\n\nexport function useObjectPropertyRowIdentity({\n  onExternalPropertyNamesChange,\n  propertyNames,\n  resetKey,\n}: {\n  onExternalPropertyNamesChange: () => void;\n  propertyNames: string[];\n  resetKey: string;\n}): ObjectPropertyRowIdentity {\n  const propertyNamesKey = getPropertyNamesKey(propertyNames);\n  const localPropertyNamesKeyRef = React.useRef<string | null>(null);\n  const [rowIdsByName, setRowIdsByName] = React.useState(() =>\n    createRowIdsByName(propertyNames),\n  );\n  const nextRowIdRef = React.useRef(propertyNames.length);\n\n  useKeyedMountEffect(\n    joinEffectKey([onExternalPropertyNamesChange, propertyNamesKey, resetKey]),\n    () => {\n      if (localPropertyNamesKeyRef.current === propertyNamesKey) {\n        localPropertyNamesKeyRef.current = null;\n        return;\n      }\n      onExternalPropertyNamesChange();\n    },\n  );\n\n  const createRowId = React.useCallback(() => {\n    const rowId = `draft-property-${nextRowIdRef.current}`;\n    nextRowIdRef.current += 1;\n    return rowId;\n  }, []);\n\n  return {\n    addRowId: (propertyName) => {\n      setRowIdsByName((current) => {\n        const next = { ...current };\n        setRecordValue(next, propertyName, createRowId());\n        return next;\n      });\n    },\n    getRowId: (propertyName) =>\n      rowIdsByName[propertyName] ?? `external-property-${propertyName}`,\n    preserveAddRowForLocalPropertyNames: (nextPropertyNames) => {\n      localPropertyNamesKeyRef.current = getPropertyNamesKey(nextPropertyNames);\n    },\n    removeRowId: (propertyName) => {\n      setRowIdsByName((current) => {\n        const next = { ...current };\n        delete next[propertyName];\n        return next;\n      });\n    },\n    renameRowId: (oldName, name) => {\n      setRowIdsByName((current) => {\n        const rowId = current[oldName] ?? createRowId();\n        const next = { ...current };\n        delete next[oldName];\n        setRecordValue(next, name, rowId);\n        return next;\n      });\n    },\n  };\n}\n\nfunction getPropertyNamesKey(propertyNames: string[]) {\n  return propertyNames.join(\"\\0\");\n}\n\nfunction createRowIdsByName(propertyNames: string[]) {\n  const rowIdsByName: Record<string, string> = {};\n  propertyNames.forEach((propertyName, index) => {\n    setRecordValue(rowIdsByName, propertyName, `draft-property-${index}`);\n  });\n  return rowIdsByName;\n}\n\nfunction setRecordValue<T>(record: Record<string, T>, key: string, value: T) {\n  Object.defineProperty(record, key, {\n    value,\n    enumerable: true,\n    configurable: true,\n    writable: true,\n  });\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/property-form/fields/object-property-row-identity.ts"
    },
    {
      "path": "components/schema-editor/property-form/fields/object-property-row-schema-plan.ts",
      "content": "import type { ExtendedJSONSchema7 } from \"@/components/schema-editor/lib/json-schema-types\";\nimport { createPropertySchemaPlan } from \"@/components/schema-editor/property-form/model/property-schema-plan\";\nimport type {\n  PropertyFormMode,\n  PropertyFormSchemaContext,\n  PropertySchemaPlanAccess,\n  PropertySchemaPlan,\n} from \"@/components/schema-editor/property-form/types\";\n\nexport function createObjectPropertyRowSchemaPlan({\n  access,\n  editable,\n  mode,\n  schemaContext,\n  schemaNode,\n  onChange,\n}: {\n  access: PropertySchemaPlanAccess;\n  editable: boolean;\n  mode: PropertyFormMode;\n  schemaContext: PropertyFormSchemaContext;\n  schemaNode: ExtendedJSONSchema7;\n  onChange: (schemaNode: ExtendedJSONSchema7) => void;\n}): PropertySchemaPlan {\n  return createPropertySchemaPlan({\n    schemaNode,\n    schemaContext,\n    mode,\n    access,\n    editable,\n    showTypeSelector: false,\n    onChange,\n  });\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/property-form/fields/object-property-row-schema-plan.ts"
    },
    {
      "path": "components/schema-editor/property-form/fields/object-property-row.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\nimport { SchemaFieldRow } from \"@/components/schema-editor/primitives/schema-field-row\";\nimport { SchemaInlineDescription } from \"@/components/schema-editor/primitives/schema-inline-description\";\nimport { SchemaInlineName } from \"@/components/schema-editor/primitives/schema-inline-name\";\nimport { SchemaRowActions } from \"@/components/schema-editor/primitives/schema-row-actions\";\nimport type {\n  ObjectPropertyRowModel,\n  PropertyObjectPropertiesFieldModel,\n} from \"@/components/schema-editor/property-form/model/object-properties-view\";\nimport type { PropertySchemaPlan } from \"@/components/schema-editor/property-form/types\";\n\nimport { useObjectPropertiesRowDrag } from \"./object-properties-drag\";\nimport { TypeField } from \"./type-field\";\n\ninterface ObjectPropertyRowsProps {\n  model: PropertyObjectPropertiesFieldModel;\n  renderPlan: (plan: PropertySchemaPlan) => React.ReactNode;\n}\n\ninterface ObjectPropertyRowProps {\n  editable: boolean;\n  row: ObjectPropertyRowModel;\n  rowDragProps: React.HTMLAttributes<HTMLDivElement>;\n  renderPlan: (plan: PropertySchemaPlan) => React.ReactNode;\n}\n\nexport function ObjectPropertyRows({\n  model,\n  renderPlan,\n}: ObjectPropertyRowsProps) {\n  const rowDrag = useObjectPropertiesRowDrag({\n    rows: model.rows,\n    editable: model.editable,\n  });\n\n  return (\n    <div className=\"space-y-2\">\n      {model.rows.map((row) => (\n        <ObjectPropertyRow\n          key={row.id}\n          editable={model.editable}\n          row={row}\n          rowDragProps={rowDrag.getRowDragProps(row)}\n          renderPlan={renderPlan}\n        />\n      ))}\n    </div>\n  );\n}\n\nexport function ObjectPropertyRow({\n  editable,\n  row,\n  rowDragProps,\n  renderPlan,\n}: ObjectPropertyRowProps) {\n  return (\n    <div\n      className={cn(\"border-border ml-4 border-l\", editable && \"cursor-grab\")}\n      data-property-form-row-id={row.id}\n      data-property-form-property-name={row.name}\n      {...rowDragProps}\n    >\n      <SchemaFieldRow\n        grip={editable ? \"drag\" : \"empty\"}\n        name={\n          <SchemaInlineName\n            ariaLabel={row.nameField.ariaLabel}\n            value={row.nameField.value}\n            editable={row.nameField.editable}\n            validate={row.nameField.validate}\n            onCommit={row.nameField.onCommit}\n          />\n        }\n        description={\n          <SchemaInlineDescription\n            ariaLabel={row.descriptionField.ariaLabel}\n            value={row.descriptionField.value}\n            editable={row.descriptionField.editable}\n            onCommit={row.descriptionField.onCommit}\n          />\n        }\n        actions={\n          <SchemaRowActions\n            canDelete={true}\n            editable={editable}\n            deleteLabel={row.deleteAction.label}\n            onDelete={row.deleteAction.onDelete}\n          />\n        }\n        type={<TypeField field={row.typeField} variant=\"row\" />}\n      />\n      {renderPlan(row.schemaPlan)}\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/property-form/fields/object-property-row.tsx"
    },
    {
      "path": "components/schema-editor/property-form/fields/property-object-template-type-field.tsx",
      "content": "\"use client\";\n\nimport { definitionRef } from \"@/components/schema-editor/document/json-pointer\";\nimport type { ExtendedJSONSchema7 } from \"@/components/schema-editor/lib/json-schema-types\";\nimport { createObjectTemplateTypeTrailingContent } from \"@/components/schema-editor/object-template-type-section\";\nimport type {\n  PropertyFormSchemaContext,\n  PropertyTypeFieldModel,\n} from \"@/components/schema-editor/property-form/types\";\n\nimport {\n  createPropertyTypeField,\n  replacePropertyTypeSchemaNode,\n} from \"./property-type-field-model\";\n\ninterface ObjectTemplatePropertyTypeFieldInput {\n  editable: boolean;\n  schemaContext: PropertyFormSchemaContext;\n  schemaNode: ExtendedJSONSchema7;\n  onChange: (schemaNode: ExtendedJSONSchema7) => void;\n}\n\nexport function createPropertyTypeFieldWithObjectTemplates({\n  editable,\n  schemaContext,\n  schemaNode,\n  onChange,\n}: ObjectTemplatePropertyTypeFieldInput): PropertyTypeFieldModel {\n  const selectObjectTemplate = (templateName: string) => {\n    if (!editable) return;\n    void schemaContext.onCommand?.({\n      type: \"installObjectTemplate\",\n      templateName,\n    });\n    replacePropertyTypeSchemaNode({\n      schemaNode,\n      replacement: {\n        $ref: definitionRef(\"$defs\", templateName),\n      },\n      onChange,\n    });\n  };\n\n  return createPropertyTypeField({\n    editable,\n    schemaContext,\n    schemaNode,\n    trailingContent: schemaContext.objectTemplatesEnabled\n      ? createObjectTemplateTypeTrailingContent({\n          onSelectTemplate: selectObjectTemplate,\n        })\n      : undefined,\n    onChange,\n  });\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/property-form/fields/property-object-template-type-field.tsx"
    },
    {
      "path": "components/schema-editor/property-form/fields/property-schema-plan-field.tsx",
      "content": "\"use client\";\n\nimport { ArrayItemsField } from \"@/components/schema-editor/property-form/fields/array-items-field\";\nimport { EnumValuesField } from \"@/components/schema-editor/property-form/fields/enum-values-field\";\nimport { ObjectPropertiesPlanField } from \"@/components/schema-editor/property-form/fields/object-properties-plan-field\";\nimport { TypeField } from \"@/components/schema-editor/property-form/fields/type-field\";\nimport type { PropertySchemaPlan } from \"@/components/schema-editor/property-form/types\";\n\nexport function PropertySchemaPlanField({\n  plan,\n}: {\n  plan: PropertySchemaPlan;\n}) {\n  const renderPlan = (schemaPlan: PropertySchemaPlan) => (\n    <PropertySchemaPlanField plan={schemaPlan} />\n  );\n\n  return (\n    <div className=\"space-y-3\">\n      {plan.items.map((item) => {\n        switch (item.kind) {\n          case \"type\":\n            return <TypeField key={item.kind} field={item.field} />;\n          case \"enumValues\":\n            return (\n              <EnumValuesField\n                key={item.kind}\n                values={item.field.values}\n                resetKey={item.field.resetKey}\n                disabled={item.field.disabled}\n                onChange={item.field.onChange}\n              />\n            );\n          case \"objectProperties\":\n            return (\n              <ObjectPropertiesPlanField\n                key={item.kind}\n                plan={item.plan}\n                renderPlan={renderPlan}\n              />\n            );\n          case \"arrayItems\":\n            return (\n              <ArrayItemsField key={item.kind}>\n                <PropertySchemaPlanField plan={item.itemSchemaPlan} />\n              </ArrayItemsField>\n            );\n        }\n      })}\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/property-form/fields/property-schema-plan-field.tsx"
    },
    {
      "path": "components/schema-editor/property-form/fields/property-type-field-model.ts",
      "content": "\"use client\";\n\nimport {\n  definitionNameFromRef,\n  definitionRef,\n} from \"@/components/schema-editor/document/json-pointer\";\nimport {\n  getEffectiveType,\n  updateEffectiveNode,\n  updateType,\n} from \"@/components/schema-editor/draft/draft-node-edits\";\nimport type { ExtendedJSONSchema7 } from \"@/components/schema-editor/lib/json-schema-types\";\nimport { getEffectiveNode } from \"@/components/schema-editor/lib/json-schema-utils\";\nimport type {\n  SchemaTypeMenuSection,\n  SchemaTypeMenuTrailingContent,\n} from \"@/components/schema-editor/primitives/schema-type-menu\";\nimport type { SchemaTypeOptionId } from \"@/components/schema-editor/primitives/schema-type-options\";\nimport type {\n  PropertyFormSchemaContext,\n  PropertyTypeFieldModel,\n} from \"@/components/schema-editor/property-form/types\";\nimport {\n  createDefinitionTypeSubmenu,\n  createPrimitiveTypeItems,\n  createTypeMenuValue,\n} from \"@/components/schema-editor/schema-type-menu-sections\";\n\ninterface PropertyTypeFieldModelInput {\n  editable: boolean;\n  schemaContext: PropertyFormSchemaContext;\n  schemaNode: ExtendedJSONSchema7;\n  trailingContent?: SchemaTypeMenuTrailingContent;\n  onChange: (schemaNode: ExtendedJSONSchema7) => void;\n}\n\nexport function createPropertyTypeField({\n  editable,\n  schemaContext,\n  schemaNode,\n  trailingContent,\n  onChange,\n}: PropertyTypeFieldModelInput): PropertyTypeFieldModel {\n  const effectiveType = getEffectiveType(schemaNode);\n  const effectiveSchemaNode = getEffectiveNode(schemaNode);\n\n  const selectType = (type: SchemaTypeOptionId) => {\n    if (!editable) return;\n    onChange(updateType(type, effectiveType.isNullable, schemaNode));\n  };\n\n  const selectDefinition = (definitionName: string) => {\n    if (!editable) return;\n    void schemaContext.onCommand?.({\n      type: \"selectDefinition\",\n      definitionName,\n    });\n    replacePropertyTypeSchemaNode({\n      schemaNode,\n      replacement: { $ref: definitionRef(\"$defs\", definitionName) },\n      onChange,\n    });\n  };\n\n  const refName =\n    effectiveType.type === \"$ref\" && effectiveSchemaNode.$ref\n      ? definitionNameFromRef(effectiveSchemaNode.$ref)\n      : undefined;\n\n  return {\n    ariaLabel: `Data type${\n      schemaContext.fieldPath ? ` for ${schemaContext.fieldPath}` : \"\"\n    }`,\n    editable,\n    sections: [\n      {\n        id: \"types\",\n        kind: \"items\",\n        items: createPrimitiveTypeItems({ onSelectType: selectType }),\n      },\n      createDefinitionSection({\n        definitionNames: Object.keys(schemaContext.schemaDefinitions),\n        onCreateDefinition: () => {\n          if (!editable) return;\n          void schemaContext.onCommand?.({ type: \"createDefinition\" });\n        },\n        onSelectDefinition: selectDefinition,\n      }),\n    ],\n    trailingContent,\n    value: createTypeMenuValue({ type: effectiveType.type, refName }),\n  };\n}\n\nexport function replacePropertyTypeSchemaNode({\n  schemaNode,\n  replacement,\n  onChange,\n}: {\n  schemaNode: ExtendedJSONSchema7;\n  replacement: ExtendedJSONSchema7;\n  onChange: (schemaNode: ExtendedJSONSchema7) => void;\n}) {\n  onChange(\n    updateEffectiveNode(schemaNode, preserveMetadata(schemaNode, replacement)),\n  );\n}\n\nfunction createDefinitionSection({\n  definitionNames,\n  onCreateDefinition,\n  onSelectDefinition,\n}: {\n  definitionNames: string[];\n  onCreateDefinition: () => void;\n  onSelectDefinition: (definitionName: string) => void;\n}): SchemaTypeMenuSection {\n  return createDefinitionTypeSubmenu({\n    createDefinitionLabel: \"Create new definition\",\n    definitionNames,\n    onCreateDefinition,\n    onSelectDefinition,\n  });\n}\n\nfunction preserveMetadata(\n  schemaNode: ExtendedJSONSchema7,\n  replacement: ExtendedJSONSchema7,\n): ExtendedJSONSchema7 {\n  const { anyOf, description, title } = schemaNode;\n  if (anyOf && Array.isArray(anyOf)) return replacement;\n  return {\n    ...replacement,\n    ...(title ? { title } : {}),\n    ...(description ? { description } : {}),\n  };\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/property-form/fields/property-type-field-model.ts"
    },
    {
      "path": "components/schema-editor/property-form/fields/type-field.tsx",
      "content": "\"use client\";\n\nimport {\n  SchemaTypeMenu,\n  type SchemaTypeMenuVariant,\n} from \"@/components/schema-editor/primitives/schema-type-menu\";\nimport type { PropertyTypeFieldModel } from \"@/components/schema-editor/property-form/types\";\n\nexport function TypeField({\n  field,\n  variant = \"form\",\n}: {\n  field: PropertyTypeFieldModel;\n  variant?: SchemaTypeMenuVariant;\n}) {\n  return (\n    <SchemaTypeMenu\n      ariaLabel={field.ariaLabel}\n      editable={field.editable}\n      sections={field.sections}\n      trailingContent={field.trailingContent}\n      value={field.value}\n      variant={variant}\n    />\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/property-form/fields/type-field.tsx"
    },
    {
      "path": "components/schema-editor/property-form/model/effective-node-edits.ts",
      "content": "import type { JSONSchema7Definition } from \"json-schema\";\n\nimport { setNullable } from \"@/components/schema-editor/draft/draft-node-edits\";\nimport type { ExtendedJSONSchema7 } from \"@/components/schema-editor/lib/json-schema-types\";\nimport { getEffectiveNode } from \"@/components/schema-editor/lib/json-schema-utils\";\nimport type { PropertyDraft } from \"@/components/schema-editor/property-form/types\";\n\nexport function isObjectSchema(\n  value: JSONSchema7Definition | undefined,\n): value is ExtendedJSONSchema7 {\n  return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nexport function setDraftNullable(\n  propertyDraft: PropertyDraft,\n  isNullable: boolean,\n): PropertyDraft {\n  return {\n    ...propertyDraft,\n    schemaNode: setNullable(propertyDraft.schemaNode, isNullable),\n  };\n}\n\nexport function getArrayItemsForDraft(\n  schemaNode: ExtendedJSONSchema7,\n): ExtendedJSONSchema7 {\n  const effectiveSchemaNode = getEffectiveNode(schemaNode);\n  const effectiveItems = Array.isArray(effectiveSchemaNode.items)\n    ? undefined\n    : effectiveSchemaNode.items;\n  return isObjectSchema(effectiveItems) ? effectiveItems : { type: \"string\" };\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/property-form/model/effective-node-edits.ts"
    },
    {
      "path": "components/schema-editor/property-form/model/enum-values.ts",
      "content": "import type { JSONSchema7Type } from \"json-schema\";\n\nexport function formatEnumValueInput(value: JSONSchema7Type): string {\n  return typeof value === \"string\" ? value : JSON.stringify(value);\n}\n\nexport function parseEnumValueInput(value: string): JSONSchema7Type {\n  const trimmedValue = value.trim();\n  if (!trimmedValue) return \"\";\n\n  try {\n    const parsedValue = JSON.parse(trimmedValue);\n    if (isJsonSchemaValue(parsedValue)) return parsedValue;\n  } catch {\n    return trimmedValue;\n  }\n\n  return trimmedValue;\n}\n\nfunction isJsonSchemaValue(value: unknown): value is JSONSchema7Type {\n  if (value === null) return true;\n  if (\n    typeof value === \"string\" ||\n    typeof value === \"number\" ||\n    typeof value === \"boolean\"\n  ) {\n    return true;\n  }\n  if (Array.isArray(value)) return value.every(isJsonSchemaValue);\n  if (typeof value !== \"object\") return false;\n\n  return Object.values(value).every(isJsonSchemaValue);\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/property-form/model/enum-values.ts"
    },
    {
      "path": "components/schema-editor/property-form/model/object-properties-view.ts",
      "content": "import type { SchemaAddInputModel } from \"@/components/schema-editor/primitives/schema-add-input-model\";\nimport type {\n  PropertySchemaPlan,\n  PropertyTypeFieldModel,\n} from \"@/components/schema-editor/property-form/types\";\n\nexport interface PropertyObjectPropertiesFieldModel {\n  addInput: SchemaAddInputModel;\n  editable: boolean;\n  rows: ObjectPropertyRowModel[];\n}\n\nexport interface ObjectPropertyRowModel {\n  id: string;\n  name: string;\n  schemaPlan: PropertySchemaPlan;\n  nameField: ObjectPropertyNameFieldModel;\n  descriptionField: ObjectPropertyDescriptionFieldModel;\n  reorder: ObjectPropertyRowReorderModel;\n  typeField: PropertyTypeFieldModel;\n  deleteAction: {\n    label: string;\n    onDelete: () => void;\n  };\n}\n\nexport interface ObjectPropertyNameFieldModel {\n  ariaLabel: string;\n  value: string;\n  editable: boolean;\n  validate: (value: string) => string | null;\n  onCommit: (name: string) => void;\n}\n\nexport interface ObjectPropertyDescriptionFieldModel {\n  ariaLabel: string;\n  value: string;\n  editable: boolean;\n  onCommit: (description: string) => void;\n}\n\nexport interface ObjectPropertyRowReorderModel {\n  move: (targetIndex: number) => void;\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/property-form/model/object-properties-view.ts"
    },
    {
      "path": "components/schema-editor/property-form/model/object-property-edits.ts",
      "content": "import type { ExtendedJSONSchema7 } from \"@/components/schema-editor/lib/json-schema-types\";\nimport { moveOrderedItem } from \"@/components/schema-editor/primitives/schema-order\";\nimport { getObjectPropertyNames } from \"@/components/schema-editor/property-form/model/object-property-selectors\";\nimport { formatTitle } from \"@/components/schema-editor/schema-title\";\n\nexport function createObjectPropertySchema(\n  propertyName: string,\n): ExtendedJSONSchema7 {\n  return {\n    type: \"string\",\n    title: formatTitle(propertyName),\n  };\n}\n\nexport function replaceObjectProperty({\n  schemaNode,\n  propertyName,\n  propertySchema,\n}: {\n  schemaNode: ExtendedJSONSchema7;\n  propertyName: string;\n  propertySchema: ExtendedJSONSchema7;\n}): ExtendedJSONSchema7 {\n  return {\n    ...schemaNode,\n    properties: {\n      ...(schemaNode.properties || {}),\n      [propertyName]: propertySchema,\n    },\n    required: getObjectPropertyNames(schemaNode).includes(propertyName)\n      ? schemaNode.required\n      : [...(schemaNode.required || []), propertyName],\n  };\n}\n\nexport function renameObjectProperty({\n  schemaNode,\n  oldName,\n  newName,\n}: {\n  schemaNode: ExtendedJSONSchema7;\n  oldName: string;\n  newName: string;\n}): ExtendedJSONSchema7 {\n  if (!newName || oldName === newName) return schemaNode;\n  if (getObjectPropertyNames(schemaNode).includes(newName)) return schemaNode;\n\n  const properties = schemaNode.properties || {};\n  const nextProperties: NonNullable<ExtendedJSONSchema7[\"properties\"]> = {};\n  for (const [currentName, propertySchema] of Object.entries(properties)) {\n    setRecordValue(\n      nextProperties,\n      currentName === oldName ? newName : currentName,\n      propertySchema,\n    );\n  }\n\n  return {\n    ...schemaNode,\n    properties: nextProperties,\n    required: (schemaNode.required || []).map((name) =>\n      name === oldName ? newName : name,\n    ),\n  };\n}\n\nexport function removeObjectProperty({\n  schemaNode,\n  propertyName,\n}: {\n  schemaNode: ExtendedJSONSchema7;\n  propertyName: string;\n}): ExtendedJSONSchema7 {\n  const { [propertyName]: _removed, ...nextProperties } =\n    schemaNode.properties || {};\n  return {\n    ...schemaNode,\n    properties: nextProperties,\n    required: (schemaNode.required || []).filter(\n      (name) => name !== propertyName,\n    ),\n  };\n}\n\nexport function moveObjectProperty({\n  schemaNode,\n  propertyName,\n  targetIndex,\n}: {\n  schemaNode: ExtendedJSONSchema7;\n  propertyName: string;\n  targetIndex: number;\n}): ExtendedJSONSchema7 {\n  const properties = schemaNode.properties || {};\n  const propertyEntries = Object.entries(properties);\n  const sourceIndex = propertyEntries.findIndex(\n    ([name]) => name === propertyName,\n  );\n  if (sourceIndex < 0) return schemaNode;\n\n  const nextProperties: NonNullable<ExtendedJSONSchema7[\"properties\"]> = {};\n  for (const [name, propertySchema] of moveOrderedItem({\n    items: propertyEntries,\n    sourceIndex,\n    targetIndex,\n  })) {\n    setRecordValue(nextProperties, name, propertySchema);\n  }\n\n  return {\n    ...schemaNode,\n    properties: nextProperties,\n  };\n}\n\nfunction setRecordValue<T>(record: Record<string, T>, key: string, value: T) {\n  Object.defineProperty(record, key, {\n    value,\n    enumerable: true,\n    configurable: true,\n    writable: true,\n  });\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/property-form/model/object-property-edits.ts"
    },
    {
      "path": "components/schema-editor/property-form/model/object-property-selectors.ts",
      "content": "import type { JSONSchema7Definition } from \"json-schema\";\n\nimport type { ExtendedJSONSchema7 } from \"@/components/schema-editor/lib/json-schema-types\";\n\nexport function isSchemaNode(\n  value: JSONSchema7Definition | undefined,\n): value is ExtendedJSONSchema7 {\n  return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nexport function getObjectPropertyNames(schemaNode: ExtendedJSONSchema7) {\n  return Object.keys(schemaNode.properties || {});\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/property-form/model/object-property-selectors.ts"
    },
    {
      "path": "components/schema-editor/property-form/model/property-capabilities.ts",
      "content": "import type {\n  PropertyCapabilities,\n  PropertyFormMode,\n} from \"@/components/schema-editor/property-form/types\";\n\nexport function resolvePropertyCapabilities({\n  mode,\n  canDelete,\n}: {\n  mode: PropertyFormMode;\n  canDelete: boolean;\n}): PropertyCapabilities {\n  const editable = mode === \"editable\";\n  const descriptionOnly = mode === \"descriptionOnly\";\n  return {\n    mode,\n    canEditName: editable,\n    canEditType: editable,\n    canEditNullable: editable,\n    canEditDescription: editable || descriptionOnly,\n    canEditNestedObject: editable,\n    canEditArrayItems: editable,\n    canEditEnumValues: editable,\n    canDelete: editable && canDelete,\n  };\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/property-form/model/property-capabilities.ts"
    },
    {
      "path": "components/schema-editor/property-form/model/property-draft-commit.ts",
      "content": "import { formatTitle } from \"@/components/schema-editor/schema-title\";\nimport type { PropertyDraft } from \"@/components/schema-editor/property-form/types\";\n\nexport function buildCommittedDraft(\n  propertyDraft: PropertyDraft,\n): PropertyDraft {\n  return {\n    ...propertyDraft,\n    schemaNode: {\n      ...propertyDraft.schemaNode,\n      title: formatTitle(propertyDraft.name),\n    },\n  };\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/property-form/model/property-draft-commit.ts"
    },
    {
      "path": "components/schema-editor/property-form/model/property-schema-plan.ts",
      "content": "\"use client\";\n\nimport { updateEffectiveNode } from \"@/components/schema-editor/draft/draft-node-edits\";\nimport type { ExtendedJSONSchema7 } from \"@/components/schema-editor/lib/json-schema-types\";\nimport { getEffectiveNode } from \"@/components/schema-editor/lib/json-schema-utils\";\nimport { createPropertyTypeFieldWithObjectTemplates } from \"@/components/schema-editor/property-form/fields/property-object-template-type-field\";\nimport { getArrayItemsForDraft } from \"@/components/schema-editor/property-form/model/effective-node-edits\";\nimport type {\n  PropertyFormMode,\n  PropertyFormSchemaContext,\n  PropertySchemaPlanAccess,\n  PropertySchemaPlan,\n  PropertySchemaPlanItem,\n} from \"@/components/schema-editor/property-form/types\";\n\ninterface CreatePropertySchemaPlanInput {\n  schemaNode: ExtendedJSONSchema7;\n  schemaContext: PropertyFormSchemaContext;\n  mode: PropertyFormMode;\n  access: PropertySchemaPlanAccess;\n  editable: boolean;\n  showTypeSelector?: boolean;\n  onChange: (schemaNode: ExtendedJSONSchema7) => void;\n}\n\nexport function createPropertySchemaPlan({\n  schemaNode,\n  schemaContext,\n  mode,\n  access,\n  editable,\n  showTypeSelector = true,\n  onChange,\n}: CreatePropertySchemaPlanInput): PropertySchemaPlan {\n  const effectiveSchemaNode = getEffectiveNode(schemaNode);\n  const resetKey =\n    schemaContext.resetKey ??\n    schemaContext.fieldPath ??\n    schemaContext.originalName;\n  const items: PropertySchemaPlanItem[] = [];\n\n  const updateEffectiveSchemaNode = (nextSchemaNode: ExtendedJSONSchema7) => {\n    onChange(updateEffectiveNode(schemaNode, nextSchemaNode));\n  };\n\n  if (showTypeSelector) {\n    items.push({\n      kind: \"type\",\n      field: createPropertyTypeFieldWithObjectTemplates({\n        schemaNode,\n        schemaContext,\n        editable: editable && access.type,\n        onChange,\n      }),\n    });\n  }\n\n  if (access.enumValues && Array.isArray(effectiveSchemaNode.enum)) {\n    items.push({\n      kind: \"enumValues\",\n      field: {\n        values: effectiveSchemaNode.enum,\n        resetKey,\n        disabled: !editable || !access.enumValues,\n        onChange: (values) => {\n          updateEffectiveSchemaNode({\n            ...effectiveSchemaNode,\n            enum: values,\n          });\n        },\n      },\n    });\n  }\n\n  if (\n    access.objectProperties &&\n    effectiveSchemaNode.type === \"object\" &&\n    !effectiveSchemaNode.$ref\n  ) {\n    items.push({\n      kind: \"objectProperties\",\n      plan: {\n        schemaNode: effectiveSchemaNode,\n        schemaContext,\n        mode,\n        access,\n        editable: editable && access.objectProperties,\n        onChange: updateEffectiveSchemaNode,\n      },\n    });\n  }\n\n  if (access.arrayItems && effectiveSchemaNode.type === \"array\") {\n    items.push({\n      kind: \"arrayItems\",\n      itemSchemaPlan: createPropertySchemaPlan({\n        schemaNode: getArrayItemsForDraft(schemaNode),\n        schemaContext,\n        mode,\n        access,\n        editable,\n        onChange: (items) => {\n          updateEffectiveSchemaNode({\n            ...effectiveSchemaNode,\n            items,\n          });\n        },\n      }),\n    });\n  }\n\n  return { items };\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/property-form/model/property-schema-plan.ts"
    },
    {
      "path": "components/schema-editor/property-form/model/property-validation.ts",
      "content": "import type {\n  PropertyCapabilities,\n  PropertyValidation,\n} from \"@/components/schema-editor/property-form/types\";\n\nexport function normalizeValidationForCapabilities({\n  validation,\n  capabilities,\n}: {\n  validation: PropertyValidation;\n  capabilities: PropertyCapabilities;\n}): PropertyValidation {\n  const isEnumValueValidation =\n    validation.schemaNode.code === \"enum_empty\" ||\n    validation.schemaNode.code === \"enum_blank\" ||\n    validation.schemaNode.code === \"enum_duplicate\";\n  const canEditSchemaValidation =\n    validation.schemaNode.status !== \"invalid\" ||\n    (isEnumValueValidation\n      ? capabilities.canEditType || capabilities.canEditEnumValues\n      : capabilities.canEditType ||\n        capabilities.canEditNullable ||\n        capabilities.canEditNestedObject ||\n        capabilities.canEditArrayItems ||\n        capabilities.canEditEnumValues);\n  const name =\n    capabilities.canEditName || validation.name.status !== \"invalid\"\n      ? validation.name\n      : { status: \"valid\" as const };\n  const schemaNode = canEditSchemaValidation\n    ? validation.schemaNode\n    : { status: \"valid\" as const };\n\n  return {\n    ...validation,\n    name,\n    schemaNode,\n    canCommit: name.status !== \"invalid\" && schemaNode.status !== \"invalid\",\n  };\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/property-form/model/property-validation.ts"
    },
    {
      "path": "components/schema-editor/property-form/property-form-controller.ts",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { getEffectiveType } from \"@/components/schema-editor/draft/draft-node-edits\";\nimport type { ExtendedJSONSchema7 } from \"@/components/schema-editor/lib/json-schema-types\";\nimport { createPropertyTypeFieldWithObjectTemplates } from \"@/components/schema-editor/property-form/fields/property-object-template-type-field\";\nimport { resolvePropertyCapabilities } from \"@/components/schema-editor/property-form/model/property-capabilities\";\nimport { createPropertySchemaPlan } from \"@/components/schema-editor/property-form/model/property-schema-plan\";\nimport { normalizeValidationForCapabilities } from \"@/components/schema-editor/property-form/model/property-validation\";\nimport { propertyDraftReducer } from \"@/components/schema-editor/property-form/reducer\";\nimport { usePropertyFormSubmit } from \"@/components/schema-editor/property-form/property-form-submit\";\nimport type {\n  PropertyDraftOperation,\n  PropertyFormMode,\n  PropertyFormProps,\n  PropertyFormViewModel,\n} from \"@/components/schema-editor/property-form/types\";\nimport { validatePropertyDraft } from \"@/components/schema-editor/property-form/validation\";\nimport { useKeyedMountEffect } from \"@/hooks/use-keyed-mount-effect\";\nimport { joinEffectKey } from \"@/lib/effect-key\";\n\ntype PropertyFormControllerInput = Omit<\n  PropertyFormProps,\n  \"mode\" | \"submitLabel\"\n> & {\n  mode: PropertyFormMode;\n  submitLabel: string;\n  canDelete: boolean;\n};\n\nexport function usePropertyFormController({\n  propertyDraft: initialPropertyDraft,\n  schemaContext,\n  capabilities: capabilitiesProp,\n  validation: validationProp,\n  mode,\n  submitLabel,\n  canDelete,\n  onPropertyDraftChange,\n  onCommitPropertyDraft,\n  onCancel,\n  onDelete,\n}: PropertyFormControllerInput): PropertyFormViewModel {\n  const [propertyDraft, setPropertyDraft] =\n    React.useState(initialPropertyDraft);\n  const propertyDraftRef = React.useRef(initialPropertyDraft);\n  const [draftResetVersion, setDraftResetVersion] = React.useState(0);\n\n  useKeyedMountEffect(joinEffectKey([initialPropertyDraft]), () => {\n    propertyDraftRef.current = initialPropertyDraft;\n    setPropertyDraft(initialPropertyDraft);\n    setDraftResetVersion((version) => version + 1);\n  });\n\n  const capabilities = React.useMemo(() => {\n    if (mode !== \"editable\") {\n      return resolvePropertyCapabilities({\n        mode,\n        canDelete,\n      });\n    }\n\n    const nextCapabilities =\n      capabilitiesProp ??\n      resolvePropertyCapabilities({\n        mode,\n        canDelete,\n      });\n\n    return {\n      ...nextCapabilities,\n      mode,\n    };\n  }, [canDelete, capabilitiesProp, mode]);\n\n  const validation = normalizeValidationForCapabilities({\n    validation:\n      validationProp ??\n      validatePropertyDraft({\n        propertyDraft,\n        schemaContext,\n      }),\n    capabilities,\n  });\n  const effectiveType = getEffectiveType(propertyDraft.schemaNode);\n  const schemaPlanContext = React.useMemo(\n    () => ({\n      ...schemaContext,\n      resetKey: [\n        schemaContext.resetKey ??\n          schemaContext.fieldPath ??\n          schemaContext.originalName,\n        draftResetVersion,\n      ].join(\":\"),\n    }),\n    [draftResetVersion, schemaContext],\n  );\n\n  const updatePropertyDraft = React.useCallback(\n    (operation: PropertyDraftOperation) => {\n      const nextPropertyDraft = propertyDraftReducer(\n        propertyDraftRef.current,\n        operation,\n      );\n      propertyDraftRef.current = nextPropertyDraft;\n      setPropertyDraft(nextPropertyDraft);\n      onPropertyDraftChange?.(nextPropertyDraft);\n    },\n    [onPropertyDraftChange],\n  );\n\n  const { commitPropertyDraft, isSubmitting } = usePropertyFormSubmit({\n    capabilities,\n    propertyDraftRef,\n    schemaContext,\n    validation: validationProp,\n    onCommitPropertyDraft,\n  });\n\n  const keyDown = React.useCallback(\n    (event: React.KeyboardEvent) => {\n      if (event.key !== \"Enter\" || event.shiftKey) return;\n      if (event.nativeEvent.isComposing) return;\n      if (event.target instanceof HTMLButtonElement) return;\n      if (event.target instanceof HTMLTextAreaElement) {\n        if (event.ctrlKey || event.metaKey) {\n          event.preventDefault();\n          void commitPropertyDraft();\n        }\n        return;\n      }\n      event.preventDefault();\n      void commitPropertyDraft();\n    },\n    [commitPropertyDraft],\n  );\n\n  const schemaPlan = createPropertySchemaPlan({\n    schemaNode: propertyDraft.schemaNode,\n    schemaContext: schemaPlanContext,\n    mode: capabilities.mode,\n    access: {\n      arrayItems: capabilities.canEditArrayItems,\n      enumValues: capabilities.canEditEnumValues,\n      objectProperties: capabilities.canEditNestedObject,\n      type: capabilities.canEditType,\n    },\n    editable:\n      capabilities.canEditEnumValues ||\n      capabilities.canEditNestedObject ||\n      capabilities.canEditArrayItems,\n    showTypeSelector: false,\n    onChange: (schemaNode) =>\n      updatePropertyDraft({\n        type: \"replacePropertySchemaNode\",\n        schemaNode,\n      }),\n  });\n  const hasSchemaPlan = schemaPlan.items.length > 0;\n  const { description } = propertyDraft.schemaNode;\n\n  return {\n    validation,\n    capabilities,\n    fields: {\n      name: {\n        value: propertyDraft.name,\n        validation: validation.name,\n        disabled: !capabilities.canEditName,\n        onChange: (name) =>\n          updatePropertyDraft({\n            type: \"renameProperty\",\n            name,\n          }),\n      },\n      type: createPropertyTypeFieldWithObjectTemplates({\n        schemaNode: propertyDraft.schemaNode,\n        schemaContext,\n        editable: capabilities.canEditType,\n        onChange: (schemaNode: ExtendedJSONSchema7) =>\n          updatePropertyDraft({\n            type: \"replacePropertySchemaNode\",\n            schemaNode,\n          }),\n      }),\n      nullable: {\n        isNullable: effectiveType.isNullable,\n        disabled: !capabilities.canEditNullable,\n        onChange: (isNullable) =>\n          updatePropertyDraft({\n            type: \"setPropertyNullable\",\n            isNullable,\n          }),\n      },\n      description: {\n        value: description || \"\",\n        disabled: !capabilities.canEditDescription,\n        onChange: (description) =>\n          updatePropertyDraft({\n            type: \"setPropertyDescription\",\n            description,\n          }),\n      },\n      schemaPlan: hasSchemaPlan ? schemaPlan : undefined,\n    },\n    footer: {\n      canDelete: capabilities.canDelete,\n      isSubmitting,\n      isSubmitDisabled: !validation.canCommit,\n      submitLabel,\n      onCancel,\n      onDelete,\n    },\n    events: {\n      submit: commitPropertyDraft,\n      keyDown,\n    },\n  };\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/property-form/property-form-controller.ts"
    },
    {
      "path": "components/schema-editor/property-form/property-form-footer.tsx",
      "content": "\"use client\";\n\nimport { Button } from \"@/components/ui/button\";\nimport { DialogFooter } from \"@/components/ui/dialog\";\n\nexport function PropertyFormFooter({\n  canDelete,\n  isSubmitting,\n  isSubmitDisabled,\n  submitLabel,\n  onCancel,\n  onDelete,\n}: {\n  canDelete: boolean;\n  isSubmitting: boolean;\n  isSubmitDisabled: boolean;\n  submitLabel: string;\n  onCancel?: () => void;\n  onDelete?: () => void;\n}) {\n  return (\n    <DialogFooter className=\"mx-0 mb-0 flex-row justify-between sm:justify-between\">\n      <div>\n        {onDelete && canDelete && (\n          <Button\n            type=\"button\"\n            variant=\"destructive\"\n            size=\"sm\"\n            disabled={isSubmitting}\n            onClick={onDelete}\n          >\n            Delete Property\n          </Button>\n        )}\n      </div>\n\n      <div className=\"flex space-x-2\">\n        {onCancel && (\n          <Button\n            type=\"button\"\n            variant=\"outline\"\n            size=\"sm\"\n            disabled={isSubmitting}\n            onClick={onCancel}\n          >\n            Cancel\n          </Button>\n        )}\n        <Button\n          type=\"submit\"\n          size=\"sm\"\n          className=\"px-3\"\n          disabled={isSubmitting || isSubmitDisabled}\n        >\n          {submitLabel}\n        </Button>\n      </div>\n    </DialogFooter>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/property-form/property-form-footer.tsx"
    },
    {
      "path": "components/schema-editor/property-form/property-form-shell.tsx",
      "content": "\"use client\";\n\nimport { AlertCircle } from \"lucide-react\";\n\nimport { DescriptionField } from \"@/components/schema-editor/property-form/fields/description-field\";\nimport { NameField } from \"@/components/schema-editor/property-form/fields/name-field\";\nimport { NullableField } from \"@/components/schema-editor/property-form/fields/nullable-field\";\nimport { PropertySchemaPlanField } from \"@/components/schema-editor/property-form/fields/property-schema-plan-field\";\nimport { TypeField } from \"@/components/schema-editor/property-form/fields/type-field\";\nimport { PropertyFormFooter } from \"@/components/schema-editor/property-form/property-form-footer\";\nimport type { PropertyFormViewModel } from \"@/components/schema-editor/property-form/types\";\nimport { Label } from \"@/components/ui/label\";\n\nexport function PropertyFormShell({\n  viewModel,\n}: {\n  viewModel: PropertyFormViewModel;\n}) {\n  const { fields, footer, capabilities, events, validation } = viewModel;\n  const isReadOnly = capabilities.mode === \"readOnly\";\n\n  return (\n    <form\n      onSubmit={(event) => {\n        event.preventDefault();\n        void events.submit();\n      }}\n      onKeyDown={events.keyDown}\n      className=\"flex h-full flex-col\"\n    >\n      <div className=\"max-h-[60vh] flex-1 overflow-y-auto\">\n        <div className=\"border-border space-y-4 border-b p-4\">\n          <NameField {...fields.name} />\n\n          <div>\n            <div className=\"mb-1.5 flex items-center justify-between\">\n              <Label htmlFor=\"type\">Data type</Label>\n              <NullableField\n                checked={fields.nullable.isNullable}\n                disabled={fields.nullable.disabled}\n                onChange={fields.nullable.onChange}\n              />\n            </div>\n            <TypeField field={fields.type} />\n            {validation.schemaNode.message && (\n              <p className=\"text-destructive mt-2 flex items-center gap-1 text-sm font-medium\">\n                <AlertCircle className=\"h-3 w-3\" />\n                {validation.schemaNode.message}\n              </p>\n            )}\n          </div>\n\n          {fields.schemaPlan && (\n            <PropertySchemaPlanField plan={fields.schemaPlan} />\n          )}\n        </div>\n        <div className=\"space-y-4 p-4\">\n          <DescriptionField {...fields.description} />\n        </div>\n      </div>\n\n      {!isReadOnly && (\n        <PropertyFormFooter\n          canDelete={footer.canDelete}\n          isSubmitting={footer.isSubmitting}\n          isSubmitDisabled={footer.isSubmitDisabled}\n          submitLabel={footer.submitLabel}\n          onCancel={footer.onCancel}\n          onDelete={footer.onDelete}\n        />\n      )}\n    </form>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/property-form/property-form-shell.tsx"
    },
    {
      "path": "components/schema-editor/property-form/property-form-submit.ts",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { buildCommittedDraft } from \"@/components/schema-editor/property-form/model/property-draft-commit\";\nimport { normalizeValidationForCapabilities } from \"@/components/schema-editor/property-form/model/property-validation\";\nimport type {\n  PropertyCapabilities,\n  PropertyDraft,\n  PropertyFormProps,\n  PropertyFormSchemaContext,\n  PropertyValidation,\n} from \"@/components/schema-editor/property-form/types\";\nimport { validatePropertyDraft } from \"@/components/schema-editor/property-form/validation\";\n\ninterface UsePropertyFormSubmitInput {\n  capabilities: PropertyCapabilities;\n  propertyDraftRef: React.MutableRefObject<PropertyDraft>;\n  schemaContext: PropertyFormSchemaContext;\n  validation?: PropertyValidation;\n  onCommitPropertyDraft: PropertyFormProps[\"onCommitPropertyDraft\"];\n}\n\nexport function usePropertyFormSubmit({\n  capabilities,\n  propertyDraftRef,\n  schemaContext,\n  validation,\n  onCommitPropertyDraft,\n}: UsePropertyFormSubmitInput) {\n  const [isSubmitting, setIsSubmitting] = React.useState(false);\n  const isSubmittingRef = React.useRef(false);\n\n  const commitPropertyDraft = React.useCallback(async () => {\n    if (isSubmittingRef.current) return false;\n    if (capabilities.mode === \"readOnly\") return false;\n\n    const currentPropertyDraft = propertyDraftRef.current;\n    const currentValidation = normalizeValidationForCapabilities({\n      validation:\n        validation ??\n        validatePropertyDraft({\n          propertyDraft: currentPropertyDraft,\n          schemaContext,\n        }),\n      capabilities,\n    });\n    if (!currentValidation.canCommit) return false;\n\n    isSubmittingRef.current = true;\n    setIsSubmitting(true);\n\n    try {\n      await onCommitPropertyDraft(buildCommittedDraft(currentPropertyDraft));\n      return true;\n    } catch {\n      return false;\n    } finally {\n      isSubmittingRef.current = false;\n      setIsSubmitting(false);\n    }\n  }, [\n    capabilities,\n    onCommitPropertyDraft,\n    propertyDraftRef,\n    schemaContext,\n    validation,\n  ]);\n\n  return {\n    commitPropertyDraft,\n    isSubmitting,\n  };\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/property-form/property-form-submit.ts"
    },
    {
      "path": "components/schema-editor/property-form/property-form.tsx",
      "content": "\"use client\";\n\nimport { usePropertyFormController } from \"@/components/schema-editor/property-form/property-form-controller\";\nimport { PropertyFormShell } from \"@/components/schema-editor/property-form/property-form-shell\";\nimport type { PropertyFormProps } from \"@/components/schema-editor/property-form/types\";\n\nexport function PropertyForm(props: PropertyFormProps) {\n  const mode = props.mode ?? \"editable\";\n\n  const viewModel = usePropertyFormController({\n    propertyDraft: props.propertyDraft,\n    schemaContext: props.schemaContext,\n    capabilities: props.capabilities,\n    validation: props.validation,\n    mode,\n    submitLabel: props.submitLabel ?? \"Save Changes\",\n    canDelete: Boolean(props.onDelete),\n    onPropertyDraftChange: props.onPropertyDraftChange,\n    onCommitPropertyDraft: props.onCommitPropertyDraft,\n    onCancel: props.onCancel,\n    onDelete: props.onDelete,\n  });\n\n  return <PropertyFormShell viewModel={viewModel} />;\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/property-form/property-form.tsx"
    },
    {
      "path": "components/schema-editor/property-form/reducer.ts",
      "content": "import { setDraftNullable } from \"@/components/schema-editor/property-form/model/effective-node-edits\";\nimport type {\n  PropertyDraft,\n  PropertyDraftOperation,\n} from \"@/components/schema-editor/property-form/types\";\n\nexport function propertyDraftReducer(\n  propertyDraft: PropertyDraft,\n  operation: PropertyDraftOperation,\n): PropertyDraft {\n  switch (operation.type) {\n    case \"renameProperty\":\n      return { ...propertyDraft, name: operation.name };\n    case \"setPropertyDescription\":\n      return {\n        ...propertyDraft,\n        schemaNode: {\n          ...propertyDraft.schemaNode,\n          description: operation.description,\n        },\n      };\n    case \"setPropertyNullable\":\n      return setDraftNullable(propertyDraft, operation.isNullable);\n    case \"replacePropertySchemaNode\":\n      return {\n        ...propertyDraft,\n        schemaNode: operation.schemaNode,\n      };\n  }\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/property-form/reducer.ts"
    },
    {
      "path": "components/schema-editor/property-form/types.ts",
      "content": "import type * as React from \"react\";\nimport type { JSONSchema7Definition, JSONSchema7Type } from \"json-schema\";\n\nimport type { ExtendedJSONSchema7 } from \"@/components/schema-editor/lib/json-schema-types\";\nimport type {\n  SchemaTypeMenuSection,\n  SchemaTypeMenuTrailingContent,\n  SchemaTypeMenuValue,\n} from \"@/components/schema-editor/primitives/schema-type-menu\";\n\nexport type PropertyFormMode = \"descriptionOnly\" | \"readOnly\" | \"editable\";\n\nexport interface PropertyDraft {\n  name: string;\n  schemaNode: ExtendedJSONSchema7;\n}\n\nexport interface FieldValidation {\n  status: \"valid\" | \"invalid\" | \"warning\";\n  message?: string;\n  code?: string;\n}\n\nexport interface NodeValidation {\n  status: \"valid\" | \"invalid\" | \"warning\";\n  message?: string;\n  code?: string;\n}\n\nexport interface PropertyValidation {\n  name: FieldValidation;\n  schemaNode: NodeValidation;\n  canCommit: boolean;\n}\n\nexport interface PropertyCapabilities {\n  mode: PropertyFormMode;\n  canEditName: boolean;\n  canEditType: boolean;\n  canEditNullable: boolean;\n  canEditDescription: boolean;\n  canEditNestedObject: boolean;\n  canEditArrayItems: boolean;\n  canEditEnumValues: boolean;\n  canDelete: boolean;\n}\n\nexport type PropertyFormCommand =\n  | { type: \"createDefinition\" }\n  | { type: \"selectDefinition\"; definitionName: string }\n  | { type: \"installObjectTemplate\"; templateName: string };\n\nexport interface PropertyFormSchemaContext {\n  siblingNames: string[];\n  originalName: string;\n  schemaDefinitions: Record<string, JSONSchema7Definition>;\n  fieldPath?: string;\n  resetKey?: string;\n  objectTemplatesEnabled?: boolean;\n  onCommand?: (command: PropertyFormCommand) => void | Promise<void>;\n}\n\nexport interface PropertyFormProps {\n  propertyDraft: PropertyDraft;\n  schemaContext: PropertyFormSchemaContext;\n  capabilities?: PropertyCapabilities;\n  validation?: PropertyValidation;\n  mode?: PropertyFormMode;\n  submitLabel?: string;\n  onPropertyDraftChange?: (propertyDraft: PropertyDraft) => void;\n  onCommitPropertyDraft: (propertyDraft: PropertyDraft) => void | Promise<void>;\n  onCancel?: () => void;\n  onDelete?: () => void;\n}\n\nexport type PropertyDraftOperation =\n  | { type: \"renameProperty\"; name: string }\n  | { type: \"setPropertyDescription\"; description: string }\n  | { type: \"setPropertyNullable\"; isNullable: boolean }\n  | { type: \"replacePropertySchemaNode\"; schemaNode: ExtendedJSONSchema7 };\n\nexport interface PropertyFormFooterModel {\n  canDelete: boolean;\n  isSubmitting: boolean;\n  isSubmitDisabled: boolean;\n  submitLabel: string;\n  onCancel?: () => void;\n  onDelete?: () => void;\n}\n\nexport interface PropertyTypeFieldModel {\n  ariaLabel: string;\n  editable: boolean;\n  sections: SchemaTypeMenuSection[];\n  trailingContent?: SchemaTypeMenuTrailingContent;\n  value: SchemaTypeMenuValue;\n}\n\nexport interface PropertyEnumValuesFieldModel {\n  values: JSONSchema7Type[];\n  resetKey: string;\n  disabled: boolean;\n  onChange: (values: JSONSchema7Type[]) => void;\n}\n\nexport interface PropertySchemaPlanAccess {\n  arrayItems: boolean;\n  enumValues: boolean;\n  objectProperties: boolean;\n  type: boolean;\n}\n\nexport interface PropertyObjectPropertiesPlan {\n  schemaNode: ExtendedJSONSchema7;\n  schemaContext: PropertyFormSchemaContext;\n  mode: PropertyFormMode;\n  access: PropertySchemaPlanAccess;\n  editable: boolean;\n  onChange: (schemaNode: ExtendedJSONSchema7) => void;\n}\n\nexport interface PropertySchemaPlan {\n  items: PropertySchemaPlanItem[];\n}\n\nexport type PropertySchemaPlanItem =\n  | PropertyTypePlanItem\n  | PropertyEnumPlanItem\n  | PropertyObjectPropertiesPlanItem\n  | PropertyArrayItemsPlanItem;\n\nexport interface PropertyTypePlanItem {\n  kind: \"type\";\n  field: PropertyTypeFieldModel;\n}\n\nexport interface PropertyEnumPlanItem {\n  kind: \"enumValues\";\n  field: PropertyEnumValuesFieldModel;\n}\n\nexport interface PropertyObjectPropertiesPlanItem {\n  kind: \"objectProperties\";\n  plan: PropertyObjectPropertiesPlan;\n}\n\nexport interface PropertyArrayItemsPlanItem {\n  kind: \"arrayItems\";\n  itemSchemaPlan: PropertySchemaPlan;\n}\n\nexport interface PropertyFormViewModel {\n  validation: PropertyValidation;\n  capabilities: PropertyCapabilities;\n  fields: {\n    name: {\n      value: string;\n      validation: FieldValidation;\n      disabled: boolean;\n      onChange: (name: string) => void;\n    };\n    type: PropertyTypeFieldModel;\n    nullable: {\n      isNullable: boolean;\n      disabled: boolean;\n      onChange: (isNullable: boolean) => void;\n    };\n    description: {\n      value: string;\n      disabled: boolean;\n      onChange: (description: string) => void;\n    };\n    schemaPlan?: PropertySchemaPlan;\n  };\n  footer: PropertyFormFooterModel;\n  events: {\n    submit: () => Promise<boolean>;\n    keyDown: (event: React.KeyboardEvent) => void;\n  };\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/property-form/types.ts"
    },
    {
      "path": "components/schema-editor/property-form/validation.ts",
      "content": "import { getEffectiveType } from \"@/components/schema-editor/draft/draft-node-edits\";\nimport {\n  getEffectiveNode,\n  validateName,\n} from \"@/components/schema-editor/lib/json-schema-utils\";\nimport type {\n  FieldValidation,\n  NodeValidation,\n  PropertyDraft,\n  PropertyFormSchemaContext,\n  PropertyValidation,\n} from \"@/components/schema-editor/property-form/types\";\n\nexport const PROPERTY_NAME_ERROR =\n  \"Name must start with a letter or underscore, contain only letters, numbers, or underscores, and be at most 64 characters long\";\n\nexport function validatePropertyFormName({\n  name,\n  siblingNames,\n  originalName,\n}: {\n  name: string;\n  siblingNames: string[];\n  originalName: string;\n}): string | null {\n  return validateName(name, siblingNames, originalName, \"property\");\n}\n\nfunction validField(): FieldValidation {\n  return { status: \"valid\" };\n}\n\nfunction invalidField(message: string, code: string): FieldValidation {\n  return { status: \"invalid\", message, code };\n}\n\nfunction validNode(): NodeValidation {\n  return { status: \"valid\" };\n}\n\nfunction invalidNode(message: string, code: string): NodeValidation {\n  return { status: \"invalid\", message, code };\n}\n\nfunction getStableValueKey(value: unknown): string {\n  if (Array.isArray(value)) {\n    return `[${value.map(getStableValueKey).join(\",\")}]`;\n  }\n  if (value && typeof value === \"object\") {\n    return `{${Object.entries(value)\n      .sort(([left], [right]) => left.localeCompare(right))\n      .map(\n        ([key, currentValue]) =>\n          `${JSON.stringify(key)}:${getStableValueKey(currentValue)}`,\n      )\n      .join(\",\")}}`;\n  }\n  return JSON.stringify(value);\n}\n\nfunction hasDuplicateEnumValues(values: unknown[]) {\n  const seen = new Set<string>();\n  for (const value of values) {\n    const key = getStableValueKey(value);\n    if (seen.has(key)) return true;\n    seen.add(key);\n  }\n  return false;\n}\n\nfunction hasBlankEnumValues(values: unknown[]) {\n  return values.some((value) => typeof value === \"string\" && !value.trim());\n}\n\nexport function validatePropertyDraft({\n  propertyDraft,\n  schemaContext,\n}: {\n  propertyDraft: PropertyDraft;\n  schemaContext: PropertyFormSchemaContext;\n}): PropertyValidation {\n  const nameError = validatePropertyFormName({\n    name: propertyDraft.name,\n    siblingNames: schemaContext.siblingNames,\n    originalName: schemaContext.originalName,\n  });\n  const name = nameError\n    ? invalidField(nameError, \"property_name_invalid\")\n    : validField();\n\n  const effectiveSchemaNode = getEffectiveNode(propertyDraft.schemaNode);\n  const effectiveType = getEffectiveType(propertyDraft.schemaNode);\n  const enumValues = Array.isArray(effectiveSchemaNode.enum)\n    ? effectiveSchemaNode.enum\n    : [];\n  const schemaNode =\n    effectiveType.type === \"enum\" && enumValues.length === 0\n      ? invalidNode(\n          \"Multiple choice fields need at least one option\",\n          \"enum_empty\",\n        )\n      : effectiveType.type === \"enum\" && hasBlankEnumValues(enumValues)\n        ? invalidNode(\"Multiple choice options cannot be blank\", \"enum_blank\")\n        : effectiveType.type === \"enum\" && hasDuplicateEnumValues(enumValues)\n          ? invalidNode(\n              \"Multiple choice options must be unique\",\n              \"enum_duplicate\",\n            )\n          : validNode();\n\n  return {\n    name,\n    schemaNode,\n    canCommit: name.status !== \"invalid\" && schemaNode.status !== \"invalid\",\n  };\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/property-form/validation.ts"
    },
    {
      "path": "components/schema-editor/root-dialog.tsx",
      "content": "import * as React from \"react\";\nimport {\n  Dialog,\n  DialogContent,\n  DialogHeader,\n  DialogTitle,\n  DialogDescription,\n  DialogFooter,\n} from \"@/components/ui/dialog\";\nimport { Button } from \"@/components/ui/button\";\nimport { Input } from \"@/components/ui/input\";\nimport { Label } from \"@/components/ui/label\";\nimport { Textarea } from \"@/components/ui/textarea\";\nimport { Info } from \"lucide-react\";\nimport {\n  Tooltip,\n  TooltipContent,\n  TooltipTrigger,\n} from \"@/components/ui/tooltip\";\n\ntype RootMetadataValues = {\n  title: string;\n  description: string;\n};\n\ninterface RootDialogProps {\n  isOpen: boolean;\n  onClose: () => void;\n  path: string;\n  schemaTitle: string;\n  setSchemaTitle: (name: string) => void;\n  metadataValues: RootMetadataValues;\n  setMetadataValues: (values: RootMetadataValues) => void;\n  onSave: (values: RootMetadataValues) => void;\n  mode?: \"descriptionOnly\" | \"readOnly\" | \"editable\";\n}\n\nexport function RootDialog({\n  isOpen,\n  onClose,\n  path,\n  schemaTitle,\n  setSchemaTitle,\n  metadataValues,\n  setMetadataValues,\n  onSave,\n  mode = \"editable\",\n}: RootDialogProps) {\n  return (\n    <Dialog open={isOpen} onOpenChange={onClose}>\n      <DialogContent className=\"max-h-[85vh] overflow-y-auto sm:max-w-lg\">\n        <DialogHeader>\n          <DialogTitle>\n            {mode === \"readOnly\" ? \"View Schema\" : \"Edit Schema\"}\n          </DialogTitle>\n          <DialogDescription>\n            {mode === \"readOnly\"\n              ? \"View schema title and description.\"\n              : \"Modify schema title and description.\"}\n          </DialogDescription>\n        </DialogHeader>\n\n        <div className=\"grid gap-4\">\n          <div className=\"grid gap-2\">\n            <div className=\"flex items-center gap-2\">\n              <Label htmlFor={`${path}-title`}>Schema Title</Label>\n              <Tooltip>\n                <TooltipTrigger asChild>\n                  <Info className=\"text-muted-foreground size-4\" />\n                </TooltipTrigger>\n                <TooltipContent>\n                  This title is used by the model to understand what is being\n                  extracted.\n                </TooltipContent>\n              </Tooltip>\n            </div>\n            <Input\n              id={`${path}-title`}\n              value={schemaTitle}\n              onChange={(e) => setSchemaTitle(e.target.value)}\n              placeholder=\"Enter schema title\"\n              disabled={mode === \"readOnly\" || mode === \"descriptionOnly\"}\n            />\n          </div>\n          <div className=\"grid gap-2\">\n            <Label htmlFor={`${path}-description`}>Description</Label>\n            <Textarea\n              id={`${path}-description`}\n              value={metadataValues.description}\n              onChange={(e) =>\n                setMetadataValues({\n                  ...metadataValues,\n                  description: e.target.value,\n                })\n              }\n              placeholder=\"Add a description to your schema\"\n              disabled={mode === \"readOnly\"}\n            />\n          </div>\n        </div>\n\n        <DialogFooter>\n          <Button type=\"button\" variant=\"outline\" onClick={onClose}>\n            Cancel\n          </Button>\n          <Button\n            type=\"button\"\n            onClick={() => {\n              onSave({\n                ...metadataValues,\n                title: schemaTitle,\n              });\n              onClose();\n            }}\n            disabled={mode === \"readOnly\"}\n          >\n            Save\n          </Button>\n        </DialogFooter>\n      </DialogContent>\n    </Dialog>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/root-dialog.tsx"
    },
    {
      "path": "components/schema-editor/schema-builder-types.ts",
      "content": "import type * as React from \"react\";\nimport type { ErrorObject } from \"ajv\";\n\nimport type { SchemaDocument } from \"@/components/schema-editor/document/types\";\nimport type { ExtendedJSONSchema7 } from \"@/components/schema-editor/lib/json-schema-types\";\n\nexport type { ExtendedJSONSchema7 };\n\nexport type SchemaBuilderView = \"fields\" | \"json\";\n\nexport interface SchemaBuilderFeatures {\n  definitions?: boolean;\n  objectTemplates?: boolean;\n  jsonMode?: boolean;\n  importExport?: boolean;\n}\n\nexport interface ResolvedSchemaBuilderFeatures {\n  definitions: boolean;\n  objectTemplates: boolean;\n  jsonMode: boolean;\n  importExport: boolean;\n}\n\nexport interface SchemaBuilderProps {\n  /** The JSON Schema being edited (controlled). */\n  value: ExtendedJSONSchema7;\n  /** Called with the next valid schema whenever the user commits an edit. */\n  onValueChange: (schema: ExtendedJSONSchema7) => void;\n  className?: string;\n  readOnly?: boolean;\n  view?: SchemaBuilderView;\n  onViewChange?: (view: SchemaBuilderView) => void;\n  features?: SchemaBuilderFeatures;\n}\n\nexport interface SchemaValidationResult {\n  isValid: boolean;\n  errors: SchemaValidationIssue[];\n  propertyCount: number;\n  isPropertyLimitExceeded: boolean;\n}\n\nexport interface SchemaValidationIssue {\n  code:\n    | \"invalid_schema\"\n    | \"numeric_property_name\"\n    | \"additional_properties_not_false\"\n    | \"property_limit_exceeded\";\n  path: string;\n  message: string;\n  source?: ErrorObject;\n}\n\nexport type SchemaDispatch = (\n  op: (doc: SchemaDocument) => SchemaDocument,\n  persist?: boolean,\n) => void;\n\nexport interface SchemaBuilderState {\n  doc: SchemaDocument;\n  schema: ExtendedJSONSchema7;\n  validation: SchemaValidationResult;\n  dispatch: SchemaDispatch;\n  replaceSchema: (\n    value: React.SetStateAction<ExtendedJSONSchema7>,\n    persist?: boolean,\n  ) => Promise<void>;\n}\n\nexport function resolveSchemaBuilderFeatures(\n  features?: SchemaBuilderFeatures,\n): ResolvedSchemaBuilderFeatures {\n  return {\n    definitions: features?.definitions ?? true,\n    objectTemplates: features?.objectTemplates ?? false,\n    jsonMode: features?.jsonMode ?? false,\n    importExport: features?.importExport ?? false,\n  };\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/schema-builder-types.ts"
    },
    {
      "path": "components/schema-editor/schema-builder.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { DocumentSchemaEditor } from \"@/components/schema-editor/document-schema-editor\";\nimport type {\n  ExtendedJSONSchema7,\n  SchemaBuilderProps,\n  SchemaBuilderView,\n} from \"@/components/schema-editor/schema-builder-types\";\nimport { resolveSchemaBuilderFeatures } from \"@/components/schema-editor/schema-builder-types\";\nimport { useSchemaBuilderState } from \"@/components/schema-editor/use-schema-builder-state\";\nimport { Button } from \"@/components/ui/button\";\nimport { cn } from \"@/lib/utils\";\n\nconst LazyJsonModeEditor = React.lazy(() =>\n  import(\"@/components/schema-editor/optional/json-mode/json-mode-editor\").then(\n    (module) => ({\n      default: module.JsonModeEditor,\n    }),\n  ),\n);\n\nexport function SchemaBuilder({\n  value,\n  onValueChange,\n  className,\n  readOnly = false,\n  view,\n  onViewChange,\n  features,\n}: SchemaBuilderProps) {\n  const resolvedFeatures = React.useMemo(\n    () => resolveSchemaBuilderFeatures(features),\n    [features],\n  );\n  const state = useSchemaBuilderState({\n    value,\n    onValueChange,\n    readOnly,\n  });\n  const [internalView, setInternalView] =\n    React.useState<SchemaBuilderView>(\"fields\");\n  const activeView = resolvedFeatures.jsonMode\n    ? (view ?? internalView)\n    : \"fields\";\n\n  const setView = React.useCallback(\n    (nextView: SchemaBuilderView) => {\n      if (nextView === \"json\" && !resolvedFeatures.jsonMode) return;\n      setInternalView(nextView);\n      onViewChange?.(nextView);\n    },\n    [onViewChange, resolvedFeatures.jsonMode],\n  );\n\n  return (\n    <div data-slot=\"schema-builder\" className={cn(\"w-full\", className)}>\n      {resolvedFeatures.jsonMode && (\n        <div className=\"mb-3 flex items-center gap-2\">\n          <Button\n            type=\"button\"\n            size=\"sm\"\n            variant={activeView === \"fields\" ? \"secondary\" : \"ghost\"}\n            onClick={() => setView(\"fields\")}\n          >\n            Fields\n          </Button>\n          <Button\n            type=\"button\"\n            size=\"sm\"\n            variant={activeView === \"json\" ? \"secondary\" : \"ghost\"}\n            onClick={() => setView(\"json\")}\n          >\n            JSON\n          </Button>\n        </div>\n      )}\n\n      {activeView === \"json\" ? (\n        <React.Suspense fallback={null}>\n          <LazyJsonModeEditor\n            schema={state.schema}\n            readOnly={readOnly}\n            replaceSchema={state.replaceSchema}\n          />\n        </React.Suspense>\n      ) : (\n        <DocumentSchemaEditor\n          doc={state.doc}\n          schema={state.schema}\n          validation={state.validation}\n          dispatch={state.dispatch}\n          mode={readOnly ? \"readOnly\" : \"editable\"}\n          features={resolvedFeatures}\n        />\n      )}\n    </div>\n  );\n}\n\nexport type {\n  ExtendedJSONSchema7,\n  SchemaBuilderFeatures,\n  SchemaBuilderProps,\n  SchemaBuilderView,\n} from \"@/components/schema-editor/schema-builder-types\";\n",
      "type": "registry:component",
      "target": "@components/schema-editor/schema-builder.tsx"
    },
    {
      "path": "components/schema-editor/schema-editor-mode.ts",
      "content": "export type SchemaEditorMode = \"descriptionOnly\" | \"readOnly\" | \"editable\";\n",
      "type": "registry:component",
      "target": "@components/schema-editor/schema-editor-mode.ts"
    },
    {
      "path": "components/schema-editor/schema-required-policy.ts",
      "content": "import type { JSONSchema7Definition } from \"json-schema\";\n\nconst SCHEMA_VALUE_KEYS = [\n  \"additionalItems\",\n  \"additionalProperties\",\n  \"contains\",\n  \"else\",\n  \"if\",\n  \"not\",\n  \"propertyNames\",\n  \"then\",\n  \"unevaluatedItems\",\n  \"unevaluatedProperties\",\n] as const;\n\nconst SCHEMA_MAP_KEYS = [\n  \"$defs\",\n  \"definitions\",\n  \"dependentSchemas\",\n  \"dependencies\",\n  \"patternProperties\",\n] as const;\n\n/**\n * Editor policy: every object property is required. Recursively sets each\n * object's `required` to all of its property keys. Nullability is orthogonal:\n * a field can be both required and nullable.\n */\nexport function requireAllProperties(\n  schema: JSONSchema7Definition,\n): JSONSchema7Definition {\n  if (typeof schema !== \"object\" || schema === null) return schema;\n\n  const out: Record<string, unknown> = { ...schema };\n\n  if (out.properties && typeof out.properties === \"object\") {\n    const properties = out.properties as Record<string, JSONSchema7Definition>;\n    const nextProperties: Record<string, JSONSchema7Definition> = {};\n    for (const [key, value] of Object.entries(properties)) {\n      setRecordValue(nextProperties, key, requireAllProperties(value));\n    }\n    out.properties = nextProperties;\n    out.required = mergeRequiredNames(\n      out.required,\n      Object.keys(nextProperties),\n    );\n  }\n\n  if (out.items) {\n    out.items = Array.isArray(out.items)\n      ? out.items.map((item) =>\n          requireAllProperties(item as JSONSchema7Definition),\n        )\n      : requireAllProperties(out.items as JSONSchema7Definition);\n  }\n\n  if (Array.isArray(out.prefixItems)) {\n    out.prefixItems = (out.prefixItems as JSONSchema7Definition[]).map(\n      requireAllProperties,\n    );\n  }\n\n  for (const key of [\"anyOf\", \"oneOf\", \"allOf\"] as const) {\n    if (Array.isArray(out[key])) {\n      out[key] = (out[key] as JSONSchema7Definition[]).map(\n        requireAllProperties,\n      );\n    }\n  }\n\n  for (const key of SCHEMA_VALUE_KEYS) {\n    if (isSchemaObject(out[key])) {\n      out[key] = requireAllProperties(out[key] as JSONSchema7Definition);\n    }\n  }\n\n  for (const key of SCHEMA_MAP_KEYS) {\n    if (out[key] && typeof out[key] === \"object\") {\n      const definitions = out[key] as Record<string, unknown>;\n      const nextDefinitions: Record<string, unknown> = {};\n      for (const [name, value] of Object.entries(definitions)) {\n        setRecordValue(\n          nextDefinitions,\n          name,\n          key === \"dependencies\" && Array.isArray(value)\n            ? value\n            : isJsonSchemaDefinition(value)\n              ? requireAllProperties(value)\n              : value,\n        );\n      }\n      out[key] = nextDefinitions;\n    }\n  }\n\n  return out as JSONSchema7Definition;\n}\n\nfunction isSchemaObject(value: unknown): value is Record<string, unknown> {\n  return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction isJsonSchemaDefinition(\n  value: unknown,\n): value is JSONSchema7Definition {\n  return typeof value === \"boolean\" || isSchemaObject(value);\n}\n\nfunction mergeRequiredNames(\n  existing: unknown,\n  propertyNames: string[],\n): string[] {\n  const required = Array.isArray(existing)\n    ? existing.filter((name): name is string => typeof name === \"string\")\n    : [];\n  const seen = new Set(required);\n\n  for (const propertyName of propertyNames) {\n    if (seen.has(propertyName)) continue;\n    required.push(propertyName);\n    seen.add(propertyName);\n  }\n\n  return required;\n}\n\nfunction setRecordValue<T>(record: Record<string, T>, key: string, value: T) {\n  Object.defineProperty(record, key, {\n    value,\n    enumerable: true,\n    configurable: true,\n    writable: true,\n  });\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/schema-required-policy.ts"
    },
    {
      "path": "components/schema-editor/schema-title.ts",
      "content": "export function formatTitle(rawName: string): string {\n  return rawName\n    .replace(/([a-z\\d])([A-Z])/g, \"$1 $2\")\n    .replace(/[^a-zA-Z0-9]+/g, \" \")\n    .split(\" \")\n    .filter(Boolean)\n    .map(\n      (chunk) => chunk.charAt(0).toUpperCase() + chunk.slice(1).toLowerCase(),\n    )\n    .join(\" \");\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/schema-title.ts"
    },
    {
      "path": "components/schema-editor/schema-type-menu-sections.tsx",
      "content": "\"use client\";\n\nimport { PlusIcon } from \"lucide-react\";\n\nimport type {\n  SchemaTypeMenuItem,\n  SchemaTypeMenuSection,\n  SchemaTypeMenuValue,\n} from \"@/components/schema-editor/primitives/schema-type-menu\";\nimport {\n  schemaTypeIcon,\n  schemaTypeLabel,\n  schemaTypeOptions,\n  type SchemaTypeOptionId,\n} from \"@/components/schema-editor/primitives/schema-type-options\";\nimport {\n  getTemplateIcon,\n  getTypeIcon,\n} from \"@/components/schema-editor/type-icons\";\n\nexport function createTypeMenuValue({\n  type,\n  refName,\n}: {\n  type: string;\n  refName?: string;\n}): SchemaTypeMenuValue {\n  return {\n    id: type,\n    label: schemaTypeLabel(type, refName),\n    icon: schemaTypeIcon(type, refName),\n  };\n}\n\nexport function createPrimitiveTypeItems({\n  onSelectType,\n}: {\n  onSelectType: (type: SchemaTypeOptionId) => void;\n}): SchemaTypeMenuItem[] {\n  return schemaTypeOptions.map((option) => ({\n    id: option.id,\n    label: option.label,\n    icon: option.icon,\n    onSelect: () => onSelectType(option.id),\n  }));\n}\n\nexport function createDefinitionTypeSubmenu({\n  createDefinitionLabel,\n  definitionNames,\n  onCreateDefinition,\n  onSelectDefinition,\n}: {\n  createDefinitionLabel: string;\n  definitionNames: string[];\n  onCreateDefinition: () => void;\n  onSelectDefinition: (definitionName: string) => void;\n}): SchemaTypeMenuSection {\n  return {\n    id: \"definitions\",\n    kind: \"submenu\",\n    label: \"definition\",\n    icon: getTypeIcon(\"$ref\"),\n    items:\n      definitionNames.length === 0\n        ? [\n            {\n              id: \"create-definition\",\n              label: createDefinitionLabel,\n              icon: <PlusIcon className=\"h-4 w-4\" />,\n              onSelect: onCreateDefinition,\n            },\n          ]\n        : definitionNames.map((definitionName) => ({\n            id: definitionName,\n            label: definitionName,\n            icon: getTemplateIcon(definitionName),\n            onSelect: () => onSelectDefinition(definitionName),\n          })),\n  };\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/schema-type-menu-sections.tsx"
    },
    {
      "path": "components/schema-editor/schema-validation.ts",
      "content": "\"use client\";\n\nimport Ajv from \"ajv\";\nimport type { ErrorObject, ValidateFunction } from \"ajv\";\nimport draft7MetaSchema from \"ajv/dist/refs/json-schema-draft-07.json\";\n\nimport {\n  addJsonSchemaErrors,\n  addJsonSchemaFormats,\n} from \"@/components/schema-editor/lib/configure-ajv\";\n\nlet ajvSingleton: Ajv | null = null;\nlet validateJsonSchemaSingleton: ValidateFunction | null = null;\n\nfunction isMutableObject(value: unknown): value is Record<string, unknown> {\n  return typeof value === \"object\" && value !== null;\n}\n\nfunction injectConstraints(schema: unknown): void {\n  if (!isMutableObject(schema)) return;\n\n  if (schema.type === \"object\") {\n    schema.propertyNames = {\n      type: \"string\",\n      pattern: \"^(?![-+]?(\\\\d+(\\\\.\\\\d*)?|\\\\.\\\\d+)$).+\",\n    };\n  }\n\n  for (const value of Object.values(schema)) {\n    if (isMutableObject(value)) {\n      injectConstraints(value);\n    }\n  }\n}\n\nfunction buildEnrichedDraft7MetaSchema(): Record<string, unknown> {\n  const schema = structuredClone(draft7MetaSchema) as Record<string, unknown>;\n  schema.$id = \"http://retab-json-schema.org/draft-07/enriched-schema#\";\n  injectConstraints(schema);\n\n  if (!isMutableObject(schema.properties)) {\n    schema.properties = {};\n  }\n\n  const properties = schema.properties;\n  if (!isMutableObject(properties)) return schema;\n\n  properties.additionalProperties = {\n    const: false,\n  };\n\n  return schema;\n}\n\nfunction getAjv(): Ajv {\n  if (!ajvSingleton) {\n    ajvSingleton = new Ajv({\n      allErrors: true,\n      allowUnionTypes: true,\n    });\n    addJsonSchemaFormats(ajvSingleton);\n    addJsonSchemaErrors(ajvSingleton);\n  }\n\n  return ajvSingleton;\n}\n\nfunction getValidator(): ValidateFunction {\n  if (!validateJsonSchemaSingleton) {\n    validateJsonSchemaSingleton = getAjv().compile(\n      buildEnrichedDraft7MetaSchema(),\n    );\n  }\n\n  return validateJsonSchemaSingleton;\n}\n\nexport function validateJsonSchema(schema: unknown): boolean {\n  return !!getValidator()(schema);\n}\n\nexport function getJsonSchemaValidationErrors(): ErrorObject[] | null {\n  return getValidator().errors ?? null;\n}\n\nexport function errorsText(\n  errors?: ErrorObject[] | null,\n  options?: { separator?: string; dataVar?: string },\n): string {\n  return getAjv().errorsText(errors ?? undefined, options);\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/schema-validation.ts"
    },
    {
      "path": "components/schema-editor/top-level-editor-controller.ts",
      "content": "import * as React from \"react\";\n\nimport type { ExtendedJSONSchema7 } from \"@/components/schema-editor/lib/json-schema-types\";\nimport type { SchemaEditorMode } from \"@/components/schema-editor/schema-editor-mode\";\n\nexport type TopLevelEditorProps = {\n  node: ExtendedJSONSchema7;\n  mode: SchemaEditorMode;\n  showImportExportActions?: boolean;\n  onTitleChange: (title: string) => void;\n  onDescriptionChange: (description: string) => void;\n  onEraseAll: () => void;\n  onEraseDescriptions: () => void;\n  onReplaceRoot: (node: ExtendedJSONSchema7) => void;\n};\n\nexport type TopLevelConfirmAction = \"eraseAll\" | \"eraseDescriptions\";\n\nexport function buildTopLevelMetadataValues(node: ExtendedJSONSchema7) {\n  return {\n    title: node.title || \"\",\n    description: node.description || \"\",\n  };\n}\n\nexport function useTopLevelEditorController({\n  node,\n  onTitleChange,\n  onDescriptionChange,\n  onEraseAll,\n  onEraseDescriptions,\n}: Pick<\n  TopLevelEditorProps,\n  | \"node\"\n  | \"onTitleChange\"\n  | \"onDescriptionChange\"\n  | \"onEraseAll\"\n  | \"onEraseDescriptions\"\n>) {\n  const [metadataDialogOpen, setMetadataDialogOpen] = React.useState(false);\n  const [confirmAction, setConfirmAction] =\n    React.useState<TopLevelConfirmAction | null>(null);\n  const [metadataValues, setMetadataValues] = React.useState(() =>\n    buildTopLevelMetadataValues(node),\n  );\n  const [draftTitle, setDraftTitle] = React.useState(node.title || \"\");\n  const [isTitleDirty, setIsTitleDirty] = React.useState(false);\n  const [draftDescription, setDraftDescription] = React.useState(\n    node.description || \"\",\n  );\n  const [isDescriptionDirty, setIsDescriptionDirty] = React.useState(false);\n  const [dialogPropertyName, setDialogPropertyName] = React.useState(\n    node.title || \"\",\n  );\n\n  const currentTitle = isTitleDirty ? draftTitle : node.title || \"\";\n  const currentDescription = isDescriptionDirty\n    ? draftDescription\n    : node.description || \"\";\n\n  const commitTitle = React.useCallback(() => {\n    if (currentTitle !== (node.title || \"\")) {\n      onTitleChange(currentTitle || \"\");\n    }\n    setIsTitleDirty(false);\n    setDraftTitle(node.title || \"\");\n  }, [currentTitle, node.title, onTitleChange]);\n\n  const commitDescription = React.useCallback(() => {\n    if (currentDescription !== (node.description || \"\")) {\n      onDescriptionChange(currentDescription);\n    }\n    setIsDescriptionDirty(false);\n    setDraftDescription(node.description || \"\");\n  }, [currentDescription, node.description, onDescriptionChange]);\n\n  const openMetadataDialog = React.useCallback(() => {\n    setMetadataValues(buildTopLevelMetadataValues(node));\n    setDialogPropertyName(node.title || \"\");\n    setMetadataDialogOpen(true);\n  }, [node]);\n\n  const confirmDestructiveAction = React.useCallback(() => {\n    if (confirmAction === \"eraseAll\") onEraseAll();\n    if (confirmAction === \"eraseDescriptions\") onEraseDescriptions();\n    setConfirmAction(null);\n  }, [confirmAction, onEraseAll, onEraseDescriptions]);\n\n  return {\n    metadataDialogOpen,\n    setMetadataDialogOpen,\n    confirmAction,\n    setConfirmAction,\n    metadataValues,\n    setMetadataValues,\n    draftTitle,\n    setDraftTitle,\n    setIsTitleDirty,\n    draftDescription,\n    setDraftDescription,\n    setIsDescriptionDirty,\n    dialogPropertyName,\n    setDialogPropertyName,\n    currentTitle,\n    currentDescription,\n    commitTitle,\n    commitDescription,\n    openMetadataDialog,\n    confirmDestructiveAction,\n  };\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/top-level-editor-controller.ts"
    },
    {
      "path": "components/schema-editor/top-level-editor.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport {\n  EllipsisVertical,\n  Eye,\n  MessageCircleOff,\n  Pencil,\n  Trash2,\n} from \"lucide-react\";\n\nimport {\n  AlertDialog,\n  AlertDialogAction,\n  AlertDialogCancel,\n  AlertDialogContent,\n  AlertDialogDescription,\n  AlertDialogFooter,\n  AlertDialogHeader,\n  AlertDialogTitle,\n} from \"@/components/ui/alert-dialog\";\nimport { Button } from \"@/components/ui/button\";\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuTrigger,\n} from \"@/components/ui/dropdown-menu\";\nimport { Textarea } from \"@/components/ui/textarea\";\nimport {\n  Tooltip,\n  TooltipContent,\n  TooltipTrigger,\n} from \"@/components/ui/tooltip\";\nimport { RootDialog } from \"@/components/schema-editor/root-dialog\";\nimport {\n  useTopLevelEditorController,\n  type TopLevelEditorProps,\n} from \"@/components/schema-editor/top-level-editor-controller\";\n\nconst LazyImportExportMenuItems = React.lazy(() =>\n  import(\"@/components/schema-editor/optional/import-export/import-export-menu-items\").then(\n    (module) => ({\n      default: module.ImportExportMenuItems,\n    }),\n  ),\n);\n\nexport function TopLevelEditor({\n  node,\n  mode,\n  showImportExportActions = true,\n  onTitleChange,\n  onDescriptionChange,\n  onEraseAll,\n  onEraseDescriptions,\n  onReplaceRoot,\n}: TopLevelEditorProps) {\n  const controller = useTopLevelEditorController({\n    node,\n    onTitleChange,\n    onDescriptionChange,\n    onEraseAll,\n    onEraseDescriptions,\n  });\n\n  return (\n    <div className=\"pb-4\">\n      <div className=\"group flex flex-col items-start justify-between pl-0 sm:flex-row sm:items-center\">\n        <div className=\"flex min-w-0 flex-1 items-center space-x-2\">\n          <input\n            className=\"text-foreground placeholder:text-muted-foreground/72 m-0 h-5 w-full min-w-0 rounded-none border-none bg-transparent p-0 text-lg font-medium shadow-none outline-none focus-visible:ring-0 disabled:opacity-64 md:text-lg\"\n            value={controller.currentTitle}\n            placeholder=\"Add a title to your schema\"\n            onChange={(event: React.ChangeEvent<HTMLInputElement>) => {\n              controller.setDraftTitle(event.target.value);\n              controller.setIsTitleDirty(true);\n            }}\n            onBlur={controller.commitTitle}\n            onKeyDown={(event: React.KeyboardEvent<HTMLInputElement>) => {\n              if (event.key === \"Enter\") controller.commitTitle();\n            }}\n            disabled={mode === \"readOnly\" || mode === \"descriptionOnly\"}\n          />\n        </div>\n\n        <div className=\"flex items-center gap-2\">\n          <DropdownMenu>\n            <DropdownMenuTrigger asChild>\n              <Button\n                type=\"button\"\n                variant=\"ghost\"\n                size=\"iconSm\"\n                aria-label=\"Open schema actions\"\n                className=\"flex items-center gap-2 opacity-0 transition-opacity group-hover:opacity-100\"\n              >\n                <EllipsisVertical className=\"h-4 w-4\" />\n              </Button>\n            </DropdownMenuTrigger>\n            <DropdownMenuContent align=\"end\">\n              {mode === \"editable\" && (\n                <DropdownMenuItem\n                  onClick={() => controller.setConfirmAction(\"eraseAll\")}\n                >\n                  <Trash2 className=\"mr-2 h-4 w-4\" />\n                  Delete Schema\n                </DropdownMenuItem>\n              )}\n\n              {(mode === \"editable\" || mode === \"descriptionOnly\") && (\n                <DropdownMenuItem\n                  onClick={() =>\n                    controller.setConfirmAction(\"eraseDescriptions\")\n                  }\n                >\n                  <MessageCircleOff className=\"mr-2 h-4 w-4\" />\n                  Delete all descriptions\n                </DropdownMenuItem>\n              )}\n\n              {showImportExportActions && (\n                <React.Suspense fallback={null}>\n                  <LazyImportExportMenuItems\n                    node={node}\n                    onReplaceRoot={onReplaceRoot}\n                  />\n                </React.Suspense>\n              )}\n            </DropdownMenuContent>\n          </DropdownMenu>\n        </div>\n      </div>\n\n      <div className=\"pb-2\">\n        <div className=\"flex items-start justify-between\">\n          <Textarea\n            className=\"text-muted-foreground placeholder:text-muted-foreground/70 m-0 max-h-64 min-h-6 resize-none rounded-none border-none bg-transparent p-0 text-sm font-normal shadow-none outline-none focus-visible:ring-0 md:text-sm dark:bg-transparent\"\n            value={controller.currentDescription}\n            placeholder=\"Add a description to your schema\"\n            onChange={(event) => {\n              controller.setDraftDescription(event.target.value);\n              controller.setIsDescriptionDirty(true);\n            }}\n            onBlur={controller.commitDescription}\n            onKeyDown={(event) => {\n              if (event.key === \"Enter\" && (event.ctrlKey || event.metaKey)) {\n                controller.commitDescription();\n              }\n            }}\n            disabled={mode === \"readOnly\"}\n          />\n\n          <Tooltip>\n            <TooltipTrigger asChild>\n              <Button\n                type=\"button\"\n                variant=\"ghost\"\n                size=\"icon\"\n                aria-label={\n                  mode === \"readOnly\"\n                    ? \"View schema properties\"\n                    : \"Edit schema properties\"\n                }\n                className=\"m-0 p-0\"\n                onClick={controller.openMetadataDialog}\n              >\n                {mode === \"readOnly\" ? (\n                  <Eye className=\"text-muted-foreground h-1 w-1 opacity-0 group-hover:opacity-100\" />\n                ) : (\n                  <Pencil className=\"text-muted-foreground h-1 w-1 opacity-0 group-hover:opacity-100\" />\n                )}\n              </Button>\n            </TooltipTrigger>\n            <TooltipContent className=\"max-w-xs\">\n              <p>\n                {mode === \"readOnly\"\n                  ? \"View schema properties\"\n                  : \"Edit schema properties\"}\n              </p>\n            </TooltipContent>\n          </Tooltip>\n        </div>\n      </div>\n\n      <RootDialog\n        isOpen={controller.metadataDialogOpen}\n        onClose={() => controller.setMetadataDialogOpen(false)}\n        path=\"#\"\n        schemaTitle={controller.dialogPropertyName}\n        setSchemaTitle={controller.setDialogPropertyName}\n        metadataValues={controller.metadataValues}\n        setMetadataValues={controller.setMetadataValues}\n        onSave={(metadata) => {\n          onTitleChange(metadata.title);\n          onDescriptionChange(metadata.description);\n        }}\n        mode={mode}\n      />\n\n      <AlertDialog\n        open={controller.confirmAction !== null}\n        onOpenChange={(open) => !open && controller.setConfirmAction(null)}\n      >\n        <AlertDialogContent>\n          <AlertDialogHeader>\n            <AlertDialogTitle>\n              {controller.confirmAction === \"eraseAll\"\n                ? \"Delete schema?\"\n                : \"Delete all descriptions?\"}\n            </AlertDialogTitle>\n            <AlertDialogDescription>\n              {controller.confirmAction === \"eraseAll\"\n                ? \"This clears every field in the current schema. This action cannot be undone.\"\n                : \"This removes the description from every field in the schema. This action cannot be undone.\"}\n            </AlertDialogDescription>\n          </AlertDialogHeader>\n          <AlertDialogFooter>\n            <AlertDialogCancel>Cancel</AlertDialogCancel>\n            <AlertDialogAction onClick={controller.confirmDestructiveAction}>\n              Delete\n            </AlertDialogAction>\n          </AlertDialogFooter>\n        </AlertDialogContent>\n      </AlertDialog>\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/top-level-editor.tsx"
    },
    {
      "path": "components/schema-editor/type-icons.tsx",
      "content": "\"use client\";\n\nimport {\n  Type,\n  Hash,\n  ToggleLeft,\n  List,\n  Braces,\n  Brackets,\n  Calendar,\n  Clock,\n  CalendarClock,\n  Link,\n  Shapes,\n  MapPin,\n  DollarSign,\n  User,\n  Building2,\n  CalendarDays,\n} from \"lucide-react\";\n\n// Get icon for data type\nexport const getTypeIcon = (type: string) => {\n  switch (type) {\n    case \"string\":\n      return <Type className=\"h-4 w-4\" />;\n    case \"number\":\n    case \"integer\":\n      return <Hash className=\"h-4 w-4\" />;\n    case \"boolean\":\n      return <ToggleLeft className=\"h-4 w-4\" />;\n    case \"enum\":\n      return <List className=\"h-4 w-4\" />;\n    case \"object\":\n      return <Braces className=\"h-4 w-4\" />;\n    case \"array\":\n      return <Brackets className=\"h-4 w-4\" />;\n    case \"date\":\n      return <Calendar className=\"h-4 w-4\" />;\n    case \"time\":\n      return <Clock className=\"h-4 w-4\" />;\n    case \"datetime\":\n      return <CalendarClock className=\"h-4 w-4\" />;\n    case \"$ref\":\n      return <Link className=\"h-4 w-4\" />;\n    default:\n      return <Shapes className=\"h-4 w-4\" />;\n  }\n};\n\n// Get icon for template objects\nexport const getTemplateIcon = (templateName: string) => {\n  switch (templateName) {\n    case \"Address\":\n      return <MapPin className=\"h-4 w-4\" />;\n    case \"Price\":\n      return <DollarSign className=\"h-4 w-4\" />;\n    case \"Person\":\n      return <User className=\"h-4 w-4\" />;\n    case \"Company\":\n      return <Building2 className=\"h-4 w-4\" />;\n    case \"Event\":\n      return <CalendarDays className=\"h-4 w-4\" />;\n    default:\n      return <Link className=\"h-4 w-4\" />;\n  }\n};\n",
      "type": "registry:component",
      "target": "@components/schema-editor/type-icons.tsx"
    },
    {
      "path": "components/schema-editor/use-schema-builder-state.ts",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport {\n  fromJsonSchema,\n  toJsonSchema,\n} from \"@/components/schema-editor/document/convert\";\nimport type { SchemaDocument } from \"@/components/schema-editor/document/types\";\nimport type {\n  ExtendedJSONSchema7,\n  SchemaBuilderState,\n} from \"@/components/schema-editor/schema-builder-types\";\nimport { requireAllProperties } from \"@/components/schema-editor/schema-required-policy\";\nimport { validateProjectedSchema } from \"@/components/schema-editor/validation\";\nimport { useKeyedLayoutEffect } from \"@/hooks/use-keyed-layout-effect\";\nimport { joinEffectKey } from \"@/lib/effect-key\";\n\nexport interface UseSchemaBuilderStateOptions {\n  value: ExtendedJSONSchema7;\n  onValueChange: (schema: ExtendedJSONSchema7) => void;\n  readOnly?: boolean;\n  onPersist?: (schema: ExtendedJSONSchema7) => Promise<void>;\n}\n\nexport function schemaSignature(value: unknown): string {\n  return JSON.stringify(value);\n}\n\nexport function projectSchemaDocument(\n  doc: SchemaDocument,\n): ExtendedJSONSchema7 {\n  return requireAllProperties(toJsonSchema(doc)) as ExtendedJSONSchema7;\n}\n\nexport function useSchemaBuilderState({\n  value,\n  onValueChange,\n  readOnly = false,\n  onPersist,\n}: UseSchemaBuilderStateOptions): SchemaBuilderState {\n  const [initialSignature] = React.useState(() => schemaSignature(value));\n  const [doc, setDoc] = React.useState<SchemaDocument>(() =>\n    fromJsonSchema(value),\n  );\n\n  const lastImportedSignatureRef = React.useRef(initialSignature);\n  const lastEmittedSignatureRef = React.useRef<string | null>(null);\n\n  const propSignature = React.useMemo(() => schemaSignature(value), [value]);\n\n  useKeyedLayoutEffect(joinEffectKey([propSignature, value]), () => {\n    if (propSignature === lastEmittedSignatureRef.current) {\n      lastImportedSignatureRef.current = propSignature;\n      return;\n    }\n\n    if (propSignature !== lastImportedSignatureRef.current) {\n      lastImportedSignatureRef.current = propSignature;\n      setDoc(fromJsonSchema(value));\n    }\n  });\n\n  const schema = React.useMemo(() => projectSchemaDocument(doc), [doc]);\n  const validation = React.useMemo(\n    () => validateProjectedSchema(schema),\n    [schema],\n  );\n\n  const docRef = React.useRef(doc);\n  useKeyedLayoutEffect(joinEffectKey([doc]), () => {\n    docRef.current = doc;\n  });\n\n  const schemaRef = React.useRef(schema);\n  useKeyedLayoutEffect(joinEffectKey([schema]), () => {\n    schemaRef.current = schema;\n  });\n\n  const emitSchema = React.useCallback(\n    (nextDoc: SchemaDocument, persist: boolean | undefined) => {\n      const nextSchema = projectSchemaDocument(nextDoc);\n      const nextSignature = schemaSignature(nextSchema);\n      lastEmittedSignatureRef.current = nextSignature;\n      lastImportedSignatureRef.current = nextSignature;\n      schemaRef.current = nextSchema;\n      onValueChange(nextSchema);\n      if ((persist ?? true) && onPersist) {\n        void onPersist(nextSchema);\n      }\n    },\n    [onPersist, onValueChange],\n  );\n\n  const dispatch = React.useCallback(\n    (op: (doc: SchemaDocument) => SchemaDocument, persist?: boolean) => {\n      if (readOnly) return;\n\n      const previous = docRef.current;\n      const next = op(previous);\n      if (next === previous) return;\n\n      docRef.current = next;\n      setDoc(next);\n      emitSchema(next, persist);\n    },\n    [emitSchema, readOnly],\n  );\n\n  const replaceSchema = React.useCallback(\n    async (\n      nextValue: React.SetStateAction<ExtendedJSONSchema7>,\n      persist?: boolean,\n    ) => {\n      if (readOnly) return;\n\n      const resolved = requireAllProperties(\n        typeof nextValue === \"function\"\n          ? nextValue(schemaRef.current)\n          : nextValue,\n      ) as ExtendedJSONSchema7;\n      const nextSignature = schemaSignature(resolved);\n\n      if (nextSignature === schemaSignature(schemaRef.current)) {\n        if ((persist ?? true) && onPersist) {\n          await onPersist(resolved);\n        }\n        return;\n      }\n\n      const nextDoc = fromJsonSchema(resolved);\n      docRef.current = nextDoc;\n      setDoc(nextDoc);\n      lastEmittedSignatureRef.current = nextSignature;\n      lastImportedSignatureRef.current = nextSignature;\n      schemaRef.current = resolved;\n      onValueChange(resolved);\n\n      if ((persist ?? true) && onPersist) {\n        await onPersist(resolved);\n      }\n    },\n    [onPersist, onValueChange, readOnly],\n  );\n\n  return {\n    doc,\n    schema,\n    validation,\n    dispatch,\n    replaceSchema,\n  };\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/use-schema-builder-state.ts"
    },
    {
      "path": "components/schema-editor/validation-error-display.tsx",
      "content": "\"use client\";\n\nimport { useState } from \"react\";\nimport { AlertCircle, ChevronDown, ChevronRight } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\nimport { Button } from \"@/components/ui/button\";\n\ninterface ValidationErrorDisplayProps {\n  validationErrors?: string | null;\n  className?: string;\n  /** Whether to show the full error panel or just an inline indicator. */\n  variant?: \"full\" | \"inline\" | \"compact\";\n}\n\nexport function ValidationErrorDisplay({\n  validationErrors,\n  className,\n  variant = \"full\",\n}: ValidationErrorDisplayProps) {\n  const [isExpanded, setIsExpanded] = useState(false);\n\n  if (!validationErrors) {\n    return null;\n  }\n\n  const errorItems = validationErrors.split(\"\\n\\n\").filter(Boolean);\n\n  const toggle = (\n    <Button\n      type=\"button\"\n      variant=\"ghost\"\n      onClick={() => setIsExpanded(!isExpanded)}\n      className=\"text-destructive hover:bg-destructive/10 hover:text-destructive h-auto w-full justify-start gap-2 px-2 py-1.5\"\n      title={isExpanded ? \"Collapse error details\" : \"Expand error details\"}\n    >\n      {isExpanded ? (\n        <ChevronDown className=\"size-4 shrink-0\" />\n      ) : (\n        <ChevronRight className=\"size-4 shrink-0\" />\n      )}\n      <AlertCircle className=\"size-4 shrink-0\" />\n      <span className=\"text-sm font-medium\">\n        {variant === \"compact\"\n          ? `${errorItems.length} validation error${errorItems.length !== 1 ? \"s\" : \"\"}`\n          : `Schema validation errors (${errorItems.length})`}\n      </span>\n    </Button>\n  );\n\n  const errorList = isExpanded ? (\n    <div className=\"border-destructive/30 space-y-2 border-t px-3 py-2\">\n      {errorItems.map((error, index) => (\n        <p\n          key={index}\n          className=\"text-destructive text-sm leading-relaxed break-words whitespace-pre-wrap\"\n        >\n          {error}\n        </p>\n      ))}\n    </div>\n  ) : null;\n\n  if (variant === \"compact\") {\n    return (\n      <div className={cn(\"border-destructive border-l-2\", className)}>\n        {toggle}\n        {errorList}\n      </div>\n    );\n  }\n\n  return (\n    <div\n      className={cn(\n        \"border-destructive bg-destructive/10 rounded-md border\",\n        className,\n      )}\n    >\n      {toggle}\n      {errorList}\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/validation-error-display.tsx"
    },
    {
      "path": "components/schema-editor/validation.ts",
      "content": "\"use client\";\n\nimport type { ErrorObject } from \"ajv\";\nimport type { JSONSchema7Definition } from \"json-schema\";\n\nimport {\n  errorsText,\n  getJsonSchemaValidationErrors,\n  validateJsonSchema,\n} from \"@/components/schema-editor/schema-validation\";\nimport type {\n  ExtendedJSONSchema7,\n  SchemaValidationIssue,\n  SchemaValidationResult,\n} from \"@/components/schema-editor/schema-builder-types\";\nimport { decodeJsonPointerSegment } from \"@/components/schema-editor/document/json-pointer\";\n\nconst PROPERTY_LIMIT = 500;\n\nconst SCHEMA_VALUE_KEYS = [\n  \"additionalItems\",\n  \"additionalProperties\",\n  \"contains\",\n  \"else\",\n  \"if\",\n  \"not\",\n  \"propertyNames\",\n  \"then\",\n  \"unevaluatedItems\",\n  \"unevaluatedProperties\",\n] as const;\n\nconst SCHEMA_MAP_KEYS = [\n  \"$defs\",\n  \"definitions\",\n  \"dependentSchemas\",\n  \"dependencies\",\n  \"patternProperties\",\n] as const;\n\nexport function validateProjectedSchema(\n  schema: ExtendedJSONSchema7,\n): SchemaValidationResult {\n  const propertyCount = countSchemaProperties(schema);\n  const isPropertyLimitExceeded = propertyCount > PROPERTY_LIMIT;\n  const ajvValid = validateJsonSchema(schema);\n  const ajvErrors = processValidationErrors(getJsonSchemaValidationErrors());\n  const errors = ajvErrors.map(errorToIssue);\n\n  if (isPropertyLimitExceeded) {\n    errors.unshift({\n      code: \"property_limit_exceeded\",\n      path: \"\",\n      message: `Schema has too many properties: ${propertyCount}. Maximum accepted is ${PROPERTY_LIMIT}. Please reduce the number of properties.`,\n    });\n  }\n\n  return {\n    isValid: ajvValid && !isPropertyLimitExceeded,\n    errors,\n    propertyCount,\n    isPropertyLimitExceeded,\n  };\n}\n\nexport function validationErrorsText(\n  validation: SchemaValidationResult,\n): string | undefined {\n  if (validation.isValid) return undefined;\n  return validation.errors.map((issue) => issue.message).join(\"\\n\\n\");\n}\n\nexport function countSchemaProperties(schema?: ExtendedJSONSchema7): number {\n  if (!schema || typeof schema !== \"object\") return 0;\n\n  const resolveLocalRef = (ref: string): ExtendedJSONSchema7 | undefined => {\n    if (!ref.startsWith(\"#/\")) return undefined;\n\n    const segments = ref.substring(2).split(\"/\").map(decodeJsonPointerSegment);\n    let refSchema: unknown = schema;\n    for (const segment of segments) {\n      if (refSchema && typeof refSchema === \"object\") {\n        const record = refSchema as Record<string, unknown>;\n        if (!Object.prototype.hasOwnProperty.call(record, segment)) {\n          return undefined;\n        }\n        refSchema = record[segment];\n      } else {\n        return undefined;\n      }\n    }\n\n    return refSchema && typeof refSchema === \"object\"\n      ? (refSchema as ExtendedJSONSchema7)\n      : undefined;\n  };\n\n  const countedSchemas = new Set<ExtendedJSONSchema7>();\n\n  const countProperties = (\n    node: ExtendedJSONSchema7,\n    path: string = \"\",\n    activeSchemas: Set<ExtendedJSONSchema7> = new Set(),\n  ): number => {\n    if (!node || typeof node !== \"object\") return 0;\n    if (countedSchemas.has(node)) return 0;\n    countedSchemas.add(node);\n\n    let count = 0;\n\n    if (node.$ref && typeof node.$ref === \"string\") {\n      const refSchema = resolveLocalRef(node.$ref);\n      if (refSchema && !activeSchemas.has(refSchema)) {\n        count += countProperties(\n          refSchema,\n          `${path}>${node.$ref}`,\n          new Set([...activeSchemas, refSchema]),\n        );\n      }\n    }\n\n    if (node.properties && typeof node.properties === \"object\") {\n      count += Object.keys(node.properties).length;\n      Object.entries(node.properties).forEach(([propertyName, propSchema]) => {\n        count += countProperties(\n          propSchema as ExtendedJSONSchema7,\n          `${path}/properties/${propertyName}`,\n          activeSchemas,\n        );\n      });\n    }\n\n    if (node.items) {\n      if (Array.isArray(node.items)) {\n        node.items.forEach((item, index) => {\n          count += countProperties(\n            item as ExtendedJSONSchema7,\n            `${path}/items/${index}`,\n            activeSchemas,\n          );\n        });\n      } else {\n        count += countProperties(\n          node.items as ExtendedJSONSchema7,\n          `${path}/items`,\n          activeSchemas,\n        );\n      }\n    }\n\n    const record = node as Record<string, unknown>;\n    if (Array.isArray(record.prefixItems)) {\n      record.prefixItems.forEach((item, index) => {\n        count += countProperties(\n          item as ExtendedJSONSchema7,\n          `${path}/prefixItems/${index}`,\n          activeSchemas,\n        );\n      });\n    }\n\n    for (const keyword of [\"allOf\", \"anyOf\", \"oneOf\"] as const) {\n      const compositionArray = record[keyword];\n      if (Array.isArray(compositionArray)) {\n        compositionArray.forEach((subSchema, index) => {\n          count += countProperties(\n            subSchema as ExtendedJSONSchema7,\n            `${path}/${keyword}/${index}`,\n            activeSchemas,\n          );\n        });\n      }\n    }\n\n    for (const keyword of SCHEMA_VALUE_KEYS) {\n      const value = record[keyword];\n      if (isSchemaObject(value)) {\n        count += countProperties(\n          value as ExtendedJSONSchema7,\n          `${path}/${keyword}`,\n          activeSchemas,\n        );\n      }\n    }\n\n    for (const keyword of SCHEMA_MAP_KEYS) {\n      const value = record[keyword];\n      if (!isSchemaObject(value)) continue;\n\n      for (const [name, child] of Object.entries(value)) {\n        count += countProperties(\n          child as ExtendedJSONSchema7,\n          `${path}/${keyword}/${name}`,\n          activeSchemas,\n        );\n      }\n    }\n\n    return count;\n  };\n\n  return countProperties(schema);\n}\n\nfunction isSchemaObject(value: unknown): value is Record<string, unknown> {\n  return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nexport function processValidationErrors(\n  errors: ErrorObject[] | null | undefined,\n): ErrorObject[] {\n  if (!errors || errors.length === 0) return [];\n\n  const processedErrors: ErrorObject[] = [];\n  const processedPropertyNames = new Set<string>();\n\n  for (const error of errors) {\n    if (\n      error.keyword === \"pattern\" &&\n      error.schemaPath?.includes(\"/propertyNames/pattern\") &&\n      error.data &&\n      typeof error.data === \"string\"\n    ) {\n      const propertyName = error.data;\n      if (!processedPropertyNames.has(propertyName)) {\n        processedErrors.push({\n          keyword: \"propertyNames\",\n          instancePath: error.instancePath,\n          schemaPath: error.schemaPath,\n          params: { propertyName },\n          message: `Property name \"${propertyName}\" must not be purely numeric`,\n          data: error.data,\n          schema: error.schema,\n        });\n        processedPropertyNames.add(propertyName);\n      }\n    } else if (\n      error.keyword === \"propertyNames\" &&\n      error.params?.propertyName\n    ) {\n      const propertyName = error.params.propertyName;\n      if (!processedPropertyNames.has(propertyName)) {\n        processedErrors.push({\n          keyword: \"propertyNames\",\n          instancePath: error.instancePath,\n          schemaPath: error.schemaPath,\n          params: { propertyName },\n          message: `Property name \"${propertyName}\" must not be purely numeric`,\n          data: error.data,\n          schema: error.schema,\n        });\n        processedPropertyNames.add(propertyName);\n      }\n    } else if (\n      error.keyword === \"const\" &&\n      error.schemaPath?.includes(\"/additionalProperties/const\")\n    ) {\n      processedErrors.push({\n        keyword: \"additionalProperties\",\n        instancePath: error.instancePath,\n        schemaPath: error.schemaPath,\n        params: error.params,\n        message:\n          \"`additionalProperties` are forbidden, if present they MUST be false.\",\n        data: error.data,\n        schema: error.schema,\n      });\n    } else if (\n      error.keyword !== \"propertyNames\" &&\n      !(\n        error.keyword === \"pattern\" &&\n        error.schemaPath?.includes(\"/propertyNames/pattern\")\n      ) &&\n      !(\n        error.keyword === \"const\" &&\n        error.schemaPath?.includes(\"/additionalProperties/const\")\n      )\n    ) {\n      processedErrors.push(error);\n    }\n  }\n\n  return processedErrors;\n}\n\nfunction errorToIssue(error: ErrorObject): SchemaValidationIssue {\n  if (error.keyword === \"propertyNames\") {\n    return {\n      code: \"numeric_property_name\",\n      path: error.instancePath,\n      message:\n        error.message ??\n        `Property name \"${String(error.params?.propertyName ?? \"\")}\" must not be purely numeric`,\n      source: error,\n    };\n  }\n\n  if (error.keyword === \"additionalProperties\") {\n    return {\n      code: \"additional_properties_not_false\",\n      path: error.instancePath,\n      message:\n        error.message ??\n        \"`additionalProperties` are forbidden, if present they MUST be false.\",\n      source: error,\n    };\n  }\n\n  return {\n    code: \"invalid_schema\",\n    path: error.instancePath,\n    message:\n      error.message ??\n      errorsText([error], {\n        separator: \"\\n\\n\",\n      }),\n    source: error,\n  };\n}\n\nexport function isJsonSchemaDefinition(\n  value: unknown,\n): value is JSONSchema7Definition {\n  return (\n    typeof value === \"boolean\" || (typeof value === \"object\" && value !== null)\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/schema-editor/validation.ts"
    },
    {
      "path": "registry/new-york-v4/ui/accordion.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { ChevronDownIcon } from \"lucide-react\";\nimport { Accordion as AccordionPrimitive } from \"radix-ui\";\n\nimport { cn } from \"@/lib/utils\";\n\nfunction Accordion({\n  ...props\n}: React.ComponentProps<typeof AccordionPrimitive.Root>) {\n  return <AccordionPrimitive.Root data-slot=\"accordion\" {...props} />;\n}\n\nfunction AccordionItem({\n  className,\n  ...props\n}: React.ComponentProps<typeof AccordionPrimitive.Item>) {\n  return (\n    <AccordionPrimitive.Item\n      data-slot=\"accordion-item\"\n      className={cn(\"border-b last:border-b-0\", className)}\n      {...props}\n    />\n  );\n}\n\nfunction AccordionTrigger({\n  className,\n  children,\n  ...props\n}: React.ComponentProps<typeof AccordionPrimitive.Trigger>) {\n  return (\n    <AccordionPrimitive.Header className=\"flex\">\n      <AccordionPrimitive.Trigger\n        data-slot=\"accordion-trigger\"\n        className={cn(\n          \"focus-visible:border-ring focus-visible:ring-ring/50 flex flex-1 items-start justify-between gap-4 rounded-md py-4 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&[data-state=open]>svg]:rotate-180\",\n          className,\n        )}\n        {...props}\n      >\n        {children}\n        <ChevronDownIcon className=\"text-muted-foreground pointer-events-none size-4 shrink-0 translate-y-0.5 transition-transform duration-200\" />\n      </AccordionPrimitive.Trigger>\n    </AccordionPrimitive.Header>\n  );\n}\n\nfunction AccordionContent({\n  className,\n  children,\n  ...props\n}: React.ComponentProps<typeof AccordionPrimitive.Content>) {\n  return (\n    <AccordionPrimitive.Content\n      data-slot=\"accordion-content\"\n      className=\"data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden text-sm\"\n      {...props}\n    >\n      <div className={cn(\"pt-0 pb-4\", className)}>{children}</div>\n    </AccordionPrimitive.Content>\n  );\n}\n\nexport { Accordion, AccordionItem, AccordionTrigger, AccordionContent };\n",
      "type": "registry:ui",
      "target": "@ui/accordion.tsx"
    }
  ],
  "type": "registry:ui"
}