{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "extract-viewer-block",
  "title": "Extract Viewer",
  "description": "Extracted data beside the source PDF, rendered as a JSON form and linked by sources — hover or select a field to highlight where its value came from and scroll the page to it.",
  "dependencies": [
    "react-hook-form",
    "@radix-ui/react-checkbox@^1.1.3",
    "@radix-ui/react-label@^2.1.1",
    "@radix-ui/react-slot@^1.1.1",
    "lucide-react@^0.460.0"
  ],
  "registryDependencies": [
    "@retab/file-viewer",
    "@retab/pdf-viewer",
    "@retab/document-source",
    "@retab/scroll-area",
    "@retab/segmented-document",
    "@retab/source-segmented-document",
    "@retab/utils",
    "@retab/source-field-link",
    "@retab/source-indicator",
    "@retab/data-cell",
    "@retab/input",
    "tooltip"
  ],
  "files": [
    {
      "path": "registry/new-york-v4/blocks/extract-viewer-block.tsx",
      "content": "\"use client\";\n\nimport type { JSONSchema7 } from \"json-schema\";\nimport { useForm } from \"react-hook-form\";\n\nimport type { Source } from \"@/lib/document-source\";\nimport {\n  FileViewerContent,\n  FileViewerHeader,\n  FileViewer,\n  FileViewerProvider,\n  FileViewerSidebar,\n  FileViewerSidebarContent,\n  FileViewerSidebarSection,\n  FileViewerSidebarSectionContent,\n  FileViewerSidebarSectionHeader,\n  FileViewerSidebarSectionTitle,\n  FileViewerSidebarTrigger,\n  FileViewerInset,\n  FileViewerViewport,\n} from \"@/components/ui/file-viewer\";\nimport {\n  PdfViewerPages,\n  PdfViewerProvider,\n  type PdfDocumentSource,\n} from \"@/components/ui/pdf-viewer\";\nimport {\n  SegmentedDocumentProvider,\n  useSegmentedDocumentViewport,\n} from \"@/components/ui/segmented-document-provider\";\nimport { useSegmentedSourceFieldLink } from \"@/components/ui/source-field-link\";\nimport { SourceIndicator } from \"@/components/ui/source-indicator\";\nimport { createSourcesSegmentedDocumentModel } from \"@/components/ui/source-segmented-document-model\";\nimport {\n  useSegmentedPdfSourceOverlay,\n  useSegmentedPdfViewerHandle,\n} from \"@/components/ui/source-segmented-document-overlays\";\nimport { JsonForm } from \"@/components/json-form/json-form\";\nimport extractSample from \"@/components/viewers/sample-data/extract.json\";\n\nconst PDF_URL = \"/samples/jane-doe-bank-statement-5-pages.pdf\";\n\ntype ExtractField = {\n  key: string;\n  label: string;\n  value: string;\n  /** Where this value was found in the document (its source). */\n  source: Source;\n};\n\n// Extracted values from the bank-statement sample with true text coordinates\n// (normalized pdf_bbox anchors), so each field's source highlight lands on the page.\nconst FIELDS = extractSample as ExtractField[];\nconst schema: JSONSchema7 = {\n  type: \"object\",\n  properties: Object.fromEntries(\n    FIELDS.map((field) => [\n      field.key,\n      {\n        type: \"string\",\n        title: field.label,\n      },\n    ]),\n  ),\n};\nconst defaultValues = Object.fromEntries(\n  FIELDS.map((field) => [field.key, field.value]),\n) as Record<string, unknown>;\nconst PDF_SOURCE: PdfDocumentSource = {\n  kind: \"url\",\n  url: PDF_URL,\n  fileName: \"jane-doe-bank-statement-5-pages.pdf\",\n};\nconst SOURCE_FIELDS = FIELDS.map((field) => ({\n  id: field.key,\n  label: field.label,\n  source: field.source,\n}));\nconst SEGMENTED_DOCUMENT = createSourcesSegmentedDocumentModel(SOURCE_FIELDS);\n\n/**\n * Extract viewer block — extracted fields beside the source document, linked by\n * their sources. Hovering or selecting a field highlights where its value came\n * from in the PDF and scrolls it into view; selection persists, hover previews.\n *\n * A thin composition over the segmented-document abstraction: `JsonForm` is the\n * emitter, the segmented provider owns hover/selection and the PDF pages\n * register their document handle for navigation.\n */\nexport function ExtractViewerBlock() {\n  return (\n    <SegmentedDocumentProvider model={SEGMENTED_DOCUMENT}>\n      <ExtractViewerContent />\n    </SegmentedDocumentProvider>\n  );\n}\n\nfunction ExtractViewerContent() {\n  const link = useSegmentedSourceFieldLink({\n    initialSourcePath: FIELDS[0]?.key,\n  });\n  const setPdfViewerHandle = useSegmentedPdfViewerHandle();\n  const renderPageOverlay = useSegmentedPdfSourceOverlay(link);\n  const { documentHandlers } = useSegmentedDocumentViewport();\n  const form = useForm<Record<string, unknown>>({ defaultValues });\n\n  return (\n    <FileViewerProvider source={PDF_SOURCE} defaultSidebarOpen>\n      <FileViewer className=\"bg-background min-h-[680px]\">\n        <FileViewerHeader className=\"flex min-h-10 items-center gap-2 px-2\">\n            <FileViewerSidebarTrigger />\n            <h2 className=\"min-w-0 truncate text-sm font-medium\">\n              Extracted data\n            </h2>\n            <span className=\"text-muted-foreground text-xs\">\n              {FIELDS.length} fields\n            </span>\n        </FileViewerHeader>\n        <FileViewerContent>\n          <FileViewerInset>\n            <FileViewerViewport>\n              <PdfViewerProvider>\n                <PdfViewerPages\n                  ref={setPdfViewerHandle}\n                  bare\n                  className=\"h-full\"\n                  onScrollProgressChange={\n                    documentHandlers.onScrollProgressChange\n                  }\n                  onVisiblePageChange={documentHandlers.onCurrentPageChange}\n                  renderPageOverlay={renderPageOverlay}\n                />\n              </PdfViewerProvider>\n            </FileViewerViewport>\n          </FileViewerInset>\n          <FileViewerSidebar\n            aria-label=\"Extracted fields\"\n            side=\"right\"\n            width=\"240px\"\n            className=\"flex flex-shrink-0 flex-col border-l\"\n          >\n            <FileViewerSidebarContent>\n              <FileViewerSidebarSection>\n                <FileViewerSidebarSectionHeader>\n                  <FileViewerSidebarSectionTitle className=\"sr-only\">\n                    Source fields\n                  </FileViewerSidebarSectionTitle>\n                  <SourceIndicator\n                    path={link.activeSourcePath}\n                    className=\"min-h-0 flex-1 px-0 py-0\"\n                  />\n                </FileViewerSidebarSectionHeader>\n                <FileViewerSidebarSectionContent>\n                  <JsonForm form={form} schema={schema} sourceLink={link} />\n                </FileViewerSidebarSectionContent>\n              </FileViewerSidebarSection>\n            </FileViewerSidebarContent>\n          </FileViewerSidebar>\n        </FileViewerContent>\n      </FileViewer>\n    </FileViewerProvider>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/blocks/extract-viewer-block.tsx"
    },
    {
      "path": "components/viewers/sample-data/extract.json",
      "content": "[\n  {\n    \"key\": \"accountNumber\",\n    \"label\": \"Account number\",\n    \"value\": \"000009752\",\n    \"source\": {\n      \"content\": \"000009752\",\n      \"anchor\": {\n        \"kind\": \"pdf_bbox\",\n        \"page\": 1,\n        \"left\": 0.8111,\n        \"top\": 0.1772,\n        \"width\": 0.0736,\n        \"height\": 0.0127\n      }\n    }\n  },\n  {\n    \"key\": \"statementDate\",\n    \"label\": \"Statement date\",\n    \"value\": \"July 8, 2003\",\n    \"source\": {\n      \"content\": \"July 8, 2003\",\n      \"anchor\": {\n        \"kind\": \"pdf_bbox\",\n        \"page\": 1,\n        \"left\": 0.8021,\n        \"top\": 0.1933,\n        \"width\": 0.0826,\n        \"height\": 0.0127\n      }\n    }\n  },\n  {\n    \"key\": \"beginningBalance\",\n    \"label\": \"Beginning balance\",\n    \"value\": \"$10,959.87\",\n    \"source\": {\n      \"content\": \"$10,959.87\",\n      \"anchor\": {\n        \"kind\": \"pdf_bbox\",\n        \"page\": 1,\n        \"left\": 0.8136,\n        \"top\": 0.2964,\n        \"width\": 0.0675,\n        \"height\": 0.0116\n      }\n    }\n  },\n  {\n    \"key\": \"endingBalance\",\n    \"label\": \"Ending balance\",\n    \"value\": \"$6,046.95\",\n    \"source\": {\n      \"content\": \"$6,046.95\",\n      \"anchor\": {\n        \"kind\": \"pdf_bbox\",\n        \"page\": 1,\n        \"left\": 0.8211,\n        \"top\": 0.3466,\n        \"width\": 0.06,\n        \"height\": 0.0116\n      }\n    }\n  },\n  {\n    \"key\": \"totalDeposits\",\n    \"label\": \"Total deposits & credits\",\n    \"value\": \"$2,670.93\",\n    \"source\": {\n      \"content\": \"+2,670.93\",\n      \"anchor\": {\n        \"kind\": \"pdf_bbox\",\n        \"page\": 1,\n        \"left\": 0.8207,\n        \"top\": 0.3116,\n        \"width\": 0.0603,\n        \"height\": 0.0116\n      }\n    }\n  },\n  {\n    \"key\": \"atmWithdrawal\",\n    \"label\": \"ATM withdrawal\",\n    \"value\": \"$69.01\",\n    \"source\": {\n      \"content\": \"$69.01\",\n      \"anchor\": {\n        \"kind\": \"pdf_bbox\",\n        \"page\": 1,\n        \"left\": 0.715,\n        \"top\": 0.4839,\n        \"width\": 0.0412,\n        \"height\": 0.0116\n      }\n    }\n  }\n]\n",
      "type": "registry:file",
      "target": "@components/viewers/sample-data/extract.json"
    },
    {
      "path": "components/json-form/array-fields.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { Plus, X } from \"lucide-react\";\nimport { useFieldArray, useFormContext, useWatch } from \"react-hook-form\";\n\nimport { cn } from \"@/lib/utils\";\nimport { Button } from \"@/components/ui/button\";\nimport { DisclosureHeader } from \"@/components/json-form/disclosure\";\nimport type { RenderJsonFormField } from \"@/components/json-form/field-renderer\";\nimport {\n  AUTO_COLLAPSE_DEPTH,\n  CARD_VIRTUALIZE_THRESHOLD,\n  LONG_ARRAY_THRESHOLD,\n} from \"@/components/json-form/json-form-constants\";\nimport { useJsonFormStartsOpen } from \"@/components/json-form/open-paths\";\nimport {\n  emptyArrayItemFormValue,\n  joinJsonFormPath,\n  joinJsonSourcePath,\n} from \"@/components/json-form/path-codec\";\nimport type { JsonFormTextInput } from \"@/components/json-form/scalar-control\";\nimport {\n  arrayItemSchemaAt,\n  canAppendArrayItem,\n  canRemoveArrayItem,\n  hasDynamicObjectProperties,\n  scalarObjectColumns,\n  type Schema,\n} from \"@/components/json-form/schema-model\";\nimport { ArrayTable } from \"@/components/json-form/table/array-table\";\nimport { VirtualList } from \"@/components/json-form/virtual-list\";\n\nexport function JsonFormArray({\n  name,\n  sourcePath,\n  schema,\n  label,\n  textInput,\n  className,\n  depth,\n  renderField,\n}: {\n  name: string;\n  sourcePath: string;\n  schema: Schema;\n  label: string;\n  textInput?: JsonFormTextInput;\n  className?: string;\n  depth: number;\n  renderField: RenderJsonFormField;\n}) {\n  const { control, getValues, setValue, unregister } = useFormContext();\n  const { fields, append, remove } = useFieldArray({ control, name });\n  const arrayValue = useWatch({ control, name });\n  const renderedFields = React.useMemo(() => {\n    // react-hook-form's useFieldArray compacts falsy primitive items (false,\n    // 0, \"\") out of `fields`, so derive the rendered item count from the\n    // watched array value instead of the compacted field list.\n    if (!Array.isArray(arrayValue)) {\n      return fields.map((field, index) => ({\n        id: field.id ?? `${name}.${index}`,\n      }));\n    }\n    return arrayValue.map((_, index) => ({\n      id: fields[index]?.id ?? `${name}.${index}`,\n    }));\n  }, [arrayValue, fields, name]);\n  const itemSchema = React.useMemo(\n    () => arrayItemSchemaAt(schema, 0),\n    [schema],\n  );\n  const isTupleArray = Array.isArray(schema.items);\n  const hasDynamicItemProperties = React.useMemo(\n    () => hasDynamicObjectProperties(itemSchema),\n    [itemSchema],\n  );\n  const itemSchemaForIndex = React.useCallback(\n    (index: number) => arrayItemSchemaAt(schema, index),\n    [schema],\n  );\n\n  const columns = React.useMemo(\n    () =>\n      isTupleArray || hasDynamicItemProperties\n        ? null\n        : scalarObjectColumns(itemSchema),\n    [hasDynamicItemProperties, isTupleArray, itemSchema],\n  );\n\n  const startsOpen = useJsonFormStartsOpen(\n    sourcePath,\n    depth < AUTO_COLLAPSE_DEPTH &&\n      renderedFields.length <= LONG_ARRAY_THRESHOLD,\n  );\n  const [open, setOpen] = React.useState(startsOpen);\n  const canAddItem = canAppendArrayItem(schema, renderedFields.length);\n  const canRemoveItem = canRemoveArrayItem(schema, renderedFields.length);\n\n  const add = React.useCallback(() => {\n    const current = getValues(name);\n    const nextIndex = Array.isArray(current)\n      ? current.length\n      : renderedFields.length;\n    if (!canAppendArrayItem(schema, nextIndex)) return;\n    const nextSchema = arrayItemSchemaAt(schema, nextIndex);\n    const nextItem = emptyArrayItemFormValue(nextSchema);\n    append(nextItem as never);\n    if (Array.isArray(current)) {\n      setValue(name, [...current, nextItem], { shouldDirty: true });\n    }\n    setOpen(true);\n  }, [append, getValues, name, renderedFields.length, schema, setValue]);\n  const removeAt = React.useCallback(\n    (index: number) => {\n      const current = getValues(name);\n      if (Array.isArray(current)) {\n        if (!canRemoveArrayItem(schema, current.length)) return;\n        const next = current.slice();\n        next.splice(index, 1);\n        remove(index);\n        setValue(name, next, { shouldDirty: true });\n        unregister(`${name}.${next.length}`);\n        return;\n      }\n      if (!canRemoveArrayItem(schema, renderedFields.length)) return;\n      remove(index);\n    },\n    [\n      getValues,\n      name,\n      remove,\n      renderedFields.length,\n      schema,\n      setValue,\n      unregister,\n    ],\n  );\n\n  return (\n    <div\n      className={cn(\n        \"bg-background overflow-hidden rounded-lg border shadow-sm\",\n        className,\n      )}\n    >\n      <DisclosureHeader\n        open={open}\n        onToggle={() => setOpen((o) => !o)}\n        title={label}\n        summary={`${renderedFields.length} item${renderedFields.length === 1 ? \"\" : \"s\"}`}\n        description={schema.description}\n        actions={\n          <Button\n            type=\"button\"\n            size=\"sm\"\n            variant=\"outline\"\n            onClick={add}\n            disabled={!canAddItem}\n          >\n            <Plus className=\"size-4\" />\n            Add\n          </Button>\n        }\n      />\n      {open ? (\n        <div className={cn(\"border-t\", columns ? \"\" : \"p-3\")}>\n          {renderedFields.length === 0 ? (\n            <p className=\"text-muted-foreground text-sm\">No items.</p>\n          ) : columns ? (\n            <ArrayTable\n              name={name}\n              sourcePath={sourcePath}\n              fields={renderedFields}\n              remove={removeAt}\n              canRemove={canRemoveItem}\n              columns={columns}\n            />\n          ) : (\n            <ArrayCards\n              name={name}\n              sourcePath={sourcePath}\n              fields={renderedFields}\n              remove={removeAt}\n              canRemove={canRemoveItem}\n              itemSchemaForIndex={itemSchemaForIndex}\n              label={label}\n              textInput={textInput}\n              depth={depth}\n              renderField={renderField}\n            />\n          )}\n        </div>\n      ) : null}\n    </div>\n  );\n}\n\ninterface ArrayCardsProps {\n  name: string;\n  sourcePath: string;\n  fields: { id: string }[];\n  remove: (index: number) => void;\n  canRemove: boolean;\n  itemSchemaForIndex: (index: number) => Schema;\n  label: string;\n  textInput?: JsonFormTextInput;\n  depth: number;\n  renderField: RenderJsonFormField;\n}\n\nfunction ArrayCards({\n  name,\n  sourcePath,\n  fields,\n  remove,\n  canRemove,\n  itemSchemaForIndex,\n  label,\n  textInput,\n  depth,\n  renderField,\n}: ArrayCardsProps) {\n  const renderCard = React.useCallback(\n    (index: number) => (\n      <ArrayCard\n        name={name}\n        sourcePath={sourcePath}\n        index={index}\n        remove={remove}\n        canRemove={canRemove}\n        itemSchema={itemSchemaForIndex(index)}\n        label={label}\n        textInput={textInput}\n        depth={depth}\n        renderField={renderField}\n      />\n    ),\n    [\n      name,\n      sourcePath,\n      remove,\n      canRemove,\n      itemSchemaForIndex,\n      label,\n      textInput,\n      depth,\n      renderField,\n    ],\n  );\n\n  if (fields.length > CARD_VIRTUALIZE_THRESHOLD) {\n    return (\n      <VirtualList\n        fields={fields}\n        estimateSize={64}\n        renderItem={renderCard}\n        gap={8}\n      />\n    );\n  }\n\n  return (\n    <div className=\"space-y-2\">\n      {fields.map((entry, index) => (\n        <React.Fragment key={entry.id}>{renderCard(index)}</React.Fragment>\n      ))}\n    </div>\n  );\n}\n\nconst ArrayCard = React.memo(function ArrayCard({\n  name,\n  sourcePath,\n  index,\n  remove,\n  canRemove,\n  itemSchema,\n  label,\n  textInput,\n  depth,\n  renderField,\n}: {\n  name: string;\n  sourcePath: string;\n  index: number;\n  remove: (index: number) => void;\n  canRemove: boolean;\n  itemSchema: Schema;\n  label: string;\n  textInput?: JsonFormTextInput;\n  depth: number;\n  renderField: RenderJsonFormField;\n}) {\n  return (\n    <div className=\"flex items-start gap-2\">\n      <div className=\"min-w-0 flex-1\">\n        {renderField({\n          name: joinJsonFormPath(name, index),\n          sourcePath: joinJsonSourcePath(sourcePath, index),\n          schema: itemSchema,\n          label: `${label} ${index + 1}`,\n          textInput,\n          depth: depth + 1,\n        })}\n      </div>\n      <Button\n        type=\"button\"\n        size=\"icon\"\n        variant=\"ghost\"\n        className=\"text-muted-foreground hover:border-border hover:text-destructive mt-1 border-transparent hover:bg-transparent\"\n        onClick={() => remove(index)}\n        aria-label=\"Remove item\"\n        disabled={!canRemove}\n      >\n        <X className=\"size-4\" />\n      </Button>\n    </div>\n  );\n});\n",
      "type": "registry:component",
      "target": "@components/json-form/array-fields.tsx"
    },
    {
      "path": "components/json-form/disclosure.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { ChevronRight } from \"lucide-react\";\n\nimport { cn } from \"@/lib/utils\";\nimport {\n  Tooltip,\n  TooltipContent,\n  TooltipTrigger,\n} from \"@/components/ui/tooltip\";\n\n/**\n * Wraps a label element so a field description shows as a hover tooltip on the\n * label itself rather than a body-text block that changes row height.\n */\nexport function WithDescription({\n  text,\n  children,\n}: {\n  text?: string;\n  children: React.ReactElement;\n}) {\n  if (!text) return children;\n  return (\n    <Tooltip>\n      <TooltipTrigger asChild>{children}</TooltipTrigger>\n      <TooltipContent className=\"max-w-xs text-left whitespace-pre-line\">\n        {text}\n      </TooltipContent>\n    </Tooltip>\n  );\n}\n\nexport function DisclosureHeader({\n  open,\n  onToggle,\n  title,\n  summary,\n  description,\n  actions,\n}: {\n  open: boolean;\n  onToggle: () => void;\n  title: string;\n  summary?: string;\n  description?: string;\n  actions?: React.ReactNode;\n}) {\n  return (\n    <div className=\"flex items-center gap-1 px-2 py-1.5\">\n      <button\n        type=\"button\"\n        onClick={onToggle}\n        aria-expanded={open}\n        aria-label={summary ? `${title} ${summary}` : title}\n        className=\"flex min-w-0 flex-1 items-center gap-1.5 text-left\"\n      >\n        <ChevronRight\n          className={cn(\n            \"text-muted-foreground size-4 shrink-0 transition-transform\",\n            open && \"rotate-90\",\n          )}\n        />\n        <WithDescription text={description}>\n          <span className=\"truncate text-sm font-medium\">{title}</span>\n        </WithDescription>\n        {summary ? (\n          <span className=\"text-muted-foreground shrink-0 text-xs\">\n            {summary}\n          </span>\n        ) : null}\n      </button>\n      {actions}\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/json-form/disclosure.tsx"
    },
    {
      "path": "components/json-form/field-renderer.ts",
      "content": "import type * as React from \"react\";\n\nimport type { JsonFormTextInput } from \"@/components/json-form/scalar-control\";\nimport type { Schema } from \"@/components/json-form/schema-model\";\n\nexport interface JsonFormFieldRenderProps {\n  name: string;\n  sourcePath?: string;\n  schema: Schema;\n  required?: boolean;\n  label?: string;\n  textInput?: JsonFormTextInput;\n  className?: string;\n  depth?: number;\n}\n\nexport type RenderJsonFormField = (\n  props: JsonFormFieldRenderProps,\n) => React.ReactNode;\n",
      "type": "registry:component",
      "target": "@components/json-form/field-renderer.ts"
    },
    {
      "path": "components/json-form/form-primitives.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport * as CheckboxPrimitive from \"@radix-ui/react-checkbox\";\nimport * as LabelPrimitive from \"@radix-ui/react-label\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { CheckIcon } from \"lucide-react\";\nimport {\n  Controller,\n  FormProvider,\n  useFormContext,\n  useFormState,\n  type ControllerProps,\n  type FieldPath,\n  type FieldValues,\n} from \"react-hook-form\";\n\nimport { cn } from \"@/lib/utils\";\n\nexport const Form = FormProvider;\n\ntype FormFieldContextValue<\n  TFieldValues extends FieldValues = FieldValues,\n  TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n> = {\n  name: TName;\n};\n\nconst FormFieldContext = React.createContext<FormFieldContextValue | null>(\n  null,\n);\n\nexport function FormField<\n  TFieldValues extends FieldValues = FieldValues,\n  TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n>(props: ControllerProps<TFieldValues, TName>) {\n  return (\n    <FormFieldContext.Provider value={{ name: props.name }}>\n      <Controller {...props} />\n    </FormFieldContext.Provider>\n  );\n}\n\ntype FormItemContextValue = {\n  id: string;\n};\n\nconst FormItemContext = React.createContext<FormItemContextValue | null>(null);\n\nfunction useFormField() {\n  const fieldContext = React.useContext(FormFieldContext);\n  const itemContext = React.useContext(FormItemContext);\n  const { getFieldState } = useFormContext();\n\n  if (!fieldContext) {\n    throw new Error(\"useFormField should be used within <FormField>\");\n  }\n  if (!itemContext) {\n    throw new Error(\"useFormField should be used within <FormItem>\");\n  }\n\n  const formState = useFormState({ name: fieldContext.name });\n  const fieldState = getFieldState(fieldContext.name, formState);\n  const { id } = itemContext;\n\n  return {\n    id,\n    name: fieldContext.name,\n    formItemId: `${id}-form-item`,\n    formDescriptionId: `${id}-form-item-description`,\n    formMessageId: `${id}-form-item-message`,\n    ...fieldState,\n  };\n}\n\nexport function FormItem({ className, ...props }: React.ComponentProps<\"div\">) {\n  const id = React.useId();\n\n  return (\n    <FormItemContext.Provider value={{ id }}>\n      <div\n        data-slot=\"form-item\"\n        className={cn(\"grid gap-2\", className)}\n        {...props}\n      />\n    </FormItemContext.Provider>\n  );\n}\n\nexport function FormLabel({\n  className,\n  ...props\n}: React.ComponentProps<typeof LabelPrimitive.Root>) {\n  const { error, formItemId } = useFormField();\n\n  return (\n    <LabelPrimitive.Root\n      data-slot=\"form-label\"\n      data-error={!!error}\n      className={cn(\"data-[error=true]:text-destructive\", className)}\n      htmlFor={formItemId}\n      {...props}\n    />\n  );\n}\n\nexport function FormControl(props: React.ComponentProps<typeof Slot>) {\n  const { error, formItemId, formDescriptionId, formMessageId } =\n    useFormField();\n  const { children, ...controlProps } = props;\n  const fieldProps = {\n    \"data-slot\": \"form-control\",\n    id: formItemId,\n    \"aria-describedby\": error\n      ? `${formDescriptionId} ${formMessageId}`\n      : `${formDescriptionId}`,\n    \"aria-invalid\": !!error,\n    ...controlProps,\n  };\n\n  if (React.isValidElement(children)) {\n    return React.cloneElement(\n      children as React.ReactElement<Record<string, unknown>>,\n      fieldProps,\n    );\n  }\n\n  return <Slot {...fieldProps} />;\n}\n\nexport function FormMessage({\n  className,\n  ...props\n}: React.ComponentProps<\"p\">) {\n  const { error, formMessageId } = useFormField();\n  const body = error ? String(error.message ?? \"\") : props.children;\n\n  if (!body) return null;\n\n  return (\n    <p\n      data-slot=\"form-message\"\n      id={formMessageId}\n      className={cn(\"text-destructive text-sm\", className)}\n      {...props}\n    >\n      {body}\n    </p>\n  );\n}\n\nexport function Checkbox({\n  className,\n  ...props\n}: React.ComponentProps<typeof CheckboxPrimitive.Root>) {\n  return (\n    <CheckboxPrimitive.Root\n      data-slot=\"checkbox\"\n      className={cn(\n        \"peer border-input focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:bg-input/30 dark:aria-invalid:ring-destructive/40 dark:data-[state=checked]:bg-primary size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50\",\n        className,\n      )}\n      {...props}\n    >\n      <CheckboxPrimitive.Indicator\n        data-slot=\"checkbox-indicator\"\n        className=\"flex items-center justify-center text-current transition-none\"\n      >\n        <CheckIcon className=\"size-3.5\" />\n      </CheckboxPrimitive.Indicator>\n    </CheckboxPrimitive.Root>\n  );\n}\n\nexport function Textarea({\n  className,\n  ...props\n}: React.ComponentProps<\"textarea\">) {\n  return (\n    <textarea\n      data-slot=\"textarea\"\n      className={cn(\n        \"border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:bg-input/30 dark:aria-invalid:ring-destructive/40 flex min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-none transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm\",\n        className,\n      )}\n      {...props}\n    />\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/json-form/form-primitives.tsx"
    },
    {
      "path": "components/json-form/json-form-constants.ts",
      "content": "export const AUTO_COLLAPSE_DEPTH = 1;\nexport const CARD_VIRTUALIZE_THRESHOLD = 30;\nexport const LONG_ARRAY_THRESHOLD = 8;\n",
      "type": "registry:component",
      "target": "@components/json-form/json-form-constants.ts"
    },
    {
      "path": "components/json-form/json-form.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { type SubmitHandler, type UseFormReturn } from \"react-hook-form\";\n\nimport { cn } from \"@/lib/utils\";\nimport { JsonFormArray } from \"@/components/json-form/array-fields\";\nimport { WithDescription } from \"@/components/json-form/disclosure\";\nimport type { JsonFormFieldRenderProps } from \"@/components/json-form/field-renderer\";\nimport {\n  Form,\n  FormControl,\n  FormField,\n  FormItem,\n  FormLabel,\n  FormMessage,\n} from \"@/components/json-form/form-primitives\";\nimport {\n  JsonFormObject,\n  JsonFormRootFields,\n} from \"@/components/json-form/object-fields\";\nimport { JsonFormOpenPathsContext } from \"@/components/json-form/open-paths\";\nimport {\n  decodeJsonFormValue,\n  encodeJsonFormValue,\n  schemaNeedsJsonFormPathEncoding,\n} from \"@/components/json-form/path-codec\";\nimport {\n  BooleanControl,\n  NullableBooleanControl,\n  ScalarControl,\n  type JsonFormTextInput,\n} from \"@/components/json-form/scalar-control\";\nimport {\n  expandRefs,\n  fieldKind,\n  labelFor,\n  unwrapNullable,\n  type Schema,\n} from \"@/components/json-form/schema-model\";\nimport {\n  JsonFormSourceLinkProvider,\n  SourceLinkShell,\n  type JsonFormSourceLink,\n} from \"@/components/json-form/source-link\";\nimport { useKeyedMountEffect } from \"@/hooks/use-keyed-mount-effect\";\nimport { joinEffectKey } from \"@/lib/effect-key\";\n\nexport type { JsonFormTextInput } from \"@/components/json-form/scalar-control\";\n\n/**\n * A JSON-Schema-driven form built entirely on shadcn's `FormField` abstraction.\n *\n * Each schema property is rendered through `<FormField>` →\n * `<FormItem>/<FormLabel>/<FormControl>/<FormDescription>/<FormMessage>`, so it\n * inherits shadcn's react-hook-form wiring, accessibility, and error display\n * with zero bespoke styling. Objects nest, arrays repeat, and scalars map to the\n * matching control. Drop it inside your own `useForm()` instance.\n *\n * Built to scale to deep, repetitive documents (e.g. an extraction with\n * `properties[] → production[] → line_items[]`):\n *\n *  - **Lazy mount.** Nested objects and arrays are collapsible; their children\n *    are only mounted in the DOM while expanded, so a 5,000-field tree boots as\n *    a handful of summary rows.\n *  - **Table mode.** An array whose items are flat objects of scalars renders as\n *    a dense editable table (one row per item, one column per field) instead of\n *    a stack of bordered cards.\n *  - **Virtualization.** Long arrays (card *or* table mode) window their rows\n *    through local row virtualizers, so only the visible items are in the DOM.\n *  - **Isolated re-renders.** Each row subscribes to its own field state, so a\n *    keystroke in one item never re-renders its siblings.\n */\n\n// ---------------------------------------------------------------------------\n// JsonFormField — the unit of composition\n// ---------------------------------------------------------------------------\n\nexport type JsonFormFieldProps = JsonFormFieldRenderProps;\n\nexport function JsonFormField({\n  name,\n  sourcePath,\n  schema: rawSchema,\n  required = false,\n  label,\n  textInput,\n  className,\n  depth = 0,\n}: JsonFormFieldProps) {\n  const expandedSchema = React.useMemo(\n    () => expandRefs(rawSchema),\n    [rawSchema],\n  );\n  const { schema, nullable } = unwrapNullable(expandedSchema);\n  const kind = fieldKind(schema);\n  const heading = labelFor(name, schema, label);\n  const resolvedSourcePath = sourcePath ?? name;\n\n  if (kind === \"object\") {\n    return (\n      <JsonFormObject\n        name={name}\n        sourcePath={resolvedSourcePath}\n        schema={schema}\n        label={heading}\n        textInput={textInput}\n        className={className}\n        depth={depth}\n        renderField={renderJsonFormField}\n      />\n    );\n  }\n\n  if (kind === \"array\") {\n    return (\n      <JsonFormArray\n        name={name}\n        sourcePath={resolvedSourcePath}\n        schema={schema}\n        label={heading}\n        textInput={textInput}\n        className={className}\n        depth={depth}\n        renderField={renderJsonFormField}\n      />\n    );\n  }\n\n  if (kind === \"boolean\") {\n    if (nullable) {\n      return (\n        <SourceLinkShell sourcePath={resolvedSourcePath}>\n          <FormField\n            name={name}\n            render={({ field }) => (\n              <FormItem className={className}>\n                <WithDescription text={schema.description}>\n                  <FormLabel>\n                    {heading}\n                    {required ? (\n                      <span className=\"text-destructive\"> *</span>\n                    ) : null}\n                  </FormLabel>\n                </WithDescription>\n                <FormControl>\n                  <NullableBooleanControl\n                    field={field}\n                    label={`${heading}${required ? \" *\" : \"\"}`}\n                  />\n                </FormControl>\n                <FormMessage />\n              </FormItem>\n            )}\n          />\n        </SourceLinkShell>\n      );\n    }\n\n    return (\n      <SourceLinkShell sourcePath={resolvedSourcePath}>\n        <FormField\n          name={name}\n          render={({ field }) => (\n            <FormItem className={className}>\n              <WithDescription text={schema.description}>\n                <FormLabel>\n                  {heading}\n                  {required ? (\n                    <span className=\"text-destructive\"> *</span>\n                  ) : null}\n                </FormLabel>\n              </WithDescription>\n              <FormControl>\n                <BooleanControl\n                  field={field}\n                  label={`${heading}${required ? \" *\" : \"\"}`}\n                />\n              </FormControl>\n              <FormMessage />\n            </FormItem>\n          )}\n        />\n      </SourceLinkShell>\n    );\n  }\n\n  return (\n    <SourceLinkShell sourcePath={resolvedSourcePath}>\n      <FormField\n        name={name}\n        render={({ field }) => (\n          <FormItem className={className}>\n            <WithDescription text={schema.description}>\n              <FormLabel>\n                {heading}\n                {required ? <span className=\"text-destructive\"> *</span> : null}\n              </FormLabel>\n            </WithDescription>\n            <FormControl>\n              <ScalarControl\n                kind={kind}\n                schema={schema}\n                field={field}\n                textInput={textInput}\n                nullable={nullable}\n              />\n            </FormControl>\n            <FormMessage />\n          </FormItem>\n        )}\n      />\n    </SourceLinkShell>\n  );\n}\n\nfunction renderJsonFormField(props: JsonFormFieldRenderProps) {\n  return <JsonFormField {...props} />;\n}\n\n// ---------------------------------------------------------------------------\n// JsonForm — convenience wrapper over a whole schema\n// ---------------------------------------------------------------------------\n\nexport interface JsonFormProps {\n  form: UseFormReturn<Record<string, unknown>>;\n  schema: Schema;\n  onSubmit?: SubmitHandler<Record<string, unknown>>;\n  className?: string;\n  /** Force plain string fields to render as single-line inputs or textareas. */\n  textInput?: JsonFormTextInput;\n  /**\n   * Opt into field-level source linking. When set, every scalar field becomes a\n   * hoverable card that reports its path and highlights when active — wire it\n   * straight from a source field link.\n   */\n  sourceLink?: JsonFormSourceLink;\n  /**\n   * Source/logical paths that should start expanded. Intended for controlled\n   * demos and benchmarks that need a deep virtualized body mounted immediately.\n   */\n  defaultOpenPaths?: readonly string[];\n  /** Rendered after the fields, e.g. a submit button. */\n  children?: React.ReactNode;\n}\n\nexport function JsonForm({\n  form,\n  schema,\n  onSubmit,\n  className,\n  textInput,\n  sourceLink,\n  defaultOpenPaths,\n  children,\n}: JsonFormProps) {\n  const expandedSchema = React.useMemo(() => expandRefs(schema), [schema]);\n  const usesEncodedPaths = React.useMemo(\n    () => schemaNeedsJsonFormPathEncoding(expandedSchema),\n    [expandedSchema],\n  );\n  const defaultOpenPathSet = React.useMemo(\n    () =>\n      defaultOpenPaths && defaultOpenPaths.length > 0\n        ? new Set(defaultOpenPaths)\n        : null,\n    [defaultOpenPaths],\n  );\n  const hasEncodedInitialValuesRef = React.useRef(false);\n\n  useKeyedMountEffect(\n    joinEffectKey([expandedSchema, form, usesEncodedPaths]),\n    () => {\n      if (!usesEncodedPaths || hasEncodedInitialValuesRef.current) {\n        return;\n      }\n      hasEncodedInitialValuesRef.current = true;\n      form.reset(\n        encodeJsonFormValue(expandedSchema, form.getValues()) as Record<\n          string,\n          unknown\n        >,\n      );\n    },\n  );\n\n  const handleSubmit = React.useCallback(\n    (event: React.FormEvent) => {\n      if (!onSubmit) {\n        event.preventDefault();\n        return;\n      }\n      const activeElement = event.currentTarget.ownerDocument.activeElement;\n      if (\n        activeElement instanceof HTMLElement &&\n        event.currentTarget.contains(activeElement)\n      ) {\n        activeElement.blur();\n      }\n      return form.handleSubmit((data, submitEvent) => {\n        const decoded = usesEncodedPaths\n          ? (decodeJsonFormValue(expandedSchema, data) as Record<\n              string,\n              unknown\n            >)\n          : data;\n        return onSubmit(decoded, submitEvent);\n      })(event);\n    },\n    [expandedSchema, form, onSubmit, usesEncodedPaths],\n  );\n\n  return (\n    <JsonFormSourceLinkProvider sourceLink={sourceLink}>\n      <JsonFormOpenPathsContext.Provider value={defaultOpenPathSet}>\n        <Form {...form}>\n          <form onSubmit={handleSubmit} className={cn(\"space-y-4\", className)}>\n            <JsonFormRootFields\n              schema={expandedSchema}\n              textInput={textInput}\n              renderField={renderJsonFormField}\n            />\n            {children}\n          </form>\n        </Form>\n      </JsonFormOpenPathsContext.Provider>\n    </JsonFormSourceLinkProvider>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/json-form/json-form.tsx"
    },
    {
      "path": "components/json-form/object-fields.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { useFormContext, useWatch } from \"react-hook-form\";\n\nimport { cn } from \"@/lib/utils\";\nimport { DisclosureHeader } from \"@/components/json-form/disclosure\";\nimport type { RenderJsonFormField } from \"@/components/json-form/field-renderer\";\nimport { AUTO_COLLAPSE_DEPTH } from \"@/components/json-form/json-form-constants\";\nimport { useJsonFormStartsOpen } from \"@/components/json-form/open-paths\";\nimport {\n  dynamicPropertyEntries,\n  joinJsonFormPath,\n  joinJsonSourcePath,\n  staticPropertyKeys,\n} from \"@/components/json-form/path-codec\";\nimport type { JsonFormTextInput } from \"@/components/json-form/scalar-control\";\nimport {\n  labelFor,\n  schemaProperties,\n  type Schema,\n} from \"@/components/json-form/schema-model\";\n\nexport function JsonFormObject({\n  name,\n  sourcePath,\n  schema,\n  label,\n  textInput,\n  className,\n  depth,\n  renderField,\n}: {\n  name: string;\n  sourcePath: string;\n  schema: Schema;\n  label: string;\n  textInput?: JsonFormTextInput;\n  className?: string;\n  depth: number;\n  renderField: RenderJsonFormField;\n}) {\n  const { control, getValues } = useFormContext();\n  const properties = React.useMemo(() => schemaProperties(schema), [schema]);\n  const required = React.useMemo(\n    () => new Set(schema.required ?? []),\n    [schema],\n  );\n  const entries = React.useMemo(() => Object.entries(properties), [properties]);\n  const currentValue = useWatch({\n    control,\n    name,\n    defaultValue: getValues(name),\n  }) as unknown;\n  const staticKeys = React.useMemo(() => staticPropertyKeys(schema), [schema]);\n  const dynamicEntries = React.useMemo(\n    () => dynamicPropertyEntries(schema, currentValue, staticKeys),\n    [currentValue, schema, staticKeys],\n  );\n  const fieldCount = entries.length + dynamicEntries.length;\n  const startsOpen = useJsonFormStartsOpen(\n    sourcePath,\n    depth < AUTO_COLLAPSE_DEPTH,\n  );\n  const [open, setOpen] = React.useState(startsOpen);\n\n  return (\n    <div className={cn(\"rounded-lg border\", className)}>\n      <DisclosureHeader\n        open={open}\n        onToggle={() => setOpen((o) => !o)}\n        title={label}\n        summary={`${fieldCount} field${fieldCount === 1 ? \"\" : \"s\"}`}\n        description={schema.description}\n      />\n      {open ? (\n        <div className=\"space-y-3 border-t p-3\">\n          {entries.map(([key, child]) =>\n            typeof child === \"object\" ? (\n              <React.Fragment key={key}>\n                {renderField({\n                  name: joinJsonFormPath(name, key),\n                  sourcePath: joinJsonSourcePath(sourcePath, key),\n                  schema: child,\n                  required: required.has(key),\n                  label: labelFor(key, child),\n                  textInput,\n                  depth: depth + 1,\n                })}\n              </React.Fragment>\n            ) : null,\n          )}\n          {dynamicEntries.map(({ key, schema: child }) => (\n            <React.Fragment key={key}>\n              {renderField({\n                name: joinJsonFormPath(name, key),\n                sourcePath: joinJsonSourcePath(sourcePath, key),\n                schema: child,\n                label: key,\n                textInput,\n                depth: depth + 1,\n              })}\n            </React.Fragment>\n          ))}\n        </div>\n      ) : null}\n    </div>\n  );\n}\n\nexport function JsonFormRootFields({\n  schema,\n  textInput,\n  renderField,\n}: {\n  schema: Schema;\n  textInput?: JsonFormTextInput;\n  renderField: RenderJsonFormField;\n}) {\n  const { control, getValues } = useFormContext();\n  const properties = schemaProperties(schema);\n  const required = new Set(schema.required ?? []);\n  const entries = Object.entries(properties);\n  const currentValue = useWatch({\n    control,\n    defaultValue: getValues(),\n  }) as unknown;\n  const staticKeys = React.useMemo(() => staticPropertyKeys(schema), [schema]);\n  const dynamicEntries = React.useMemo(\n    () => dynamicPropertyEntries(schema, currentValue, staticKeys),\n    [currentValue, schema, staticKeys],\n  );\n\n  return (\n    <>\n      {entries.map(([key, child]) =>\n        typeof child === \"object\" ? (\n          <React.Fragment key={key}>\n            {renderField({\n              name: joinJsonFormPath(\"\", key),\n              sourcePath: key,\n              schema: child,\n              required: required.has(key),\n              label: labelFor(key, child),\n              textInput,\n              depth: 0,\n            })}\n          </React.Fragment>\n        ) : null,\n      )}\n      {dynamicEntries.map(({ key, schema: child }) => (\n        <React.Fragment key={key}>\n          {renderField({\n            name: joinJsonFormPath(\"\", key),\n            sourcePath: key,\n            schema: child,\n            label: key,\n            textInput,\n            depth: 0,\n          })}\n        </React.Fragment>\n      ))}\n    </>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/json-form/object-fields.tsx"
    },
    {
      "path": "components/json-form/open-paths.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nexport const JsonFormOpenPathsContext =\n  React.createContext<ReadonlySet<string> | null>(null);\n\nexport function useJsonFormStartsOpen(sourcePath: string, fallback: boolean) {\n  return (\n    React.useContext(JsonFormOpenPathsContext)?.has(sourcePath) ?? fallback\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/json-form/open-paths.tsx"
    },
    {
      "path": "components/json-form/path-codec.ts",
      "content": "import {\n  arrayItemSchemaAt,\n  dynamicPropertySchemaFor,\n  emptyValueFor,\n  fieldKind,\n  isRecordValue,\n  schemaPatternProperties,\n  schemaProperties,\n  unwrapNullable,\n  type Schema,\n} from \"@/components/json-form/schema-model\";\n\nexport function encodeJsonFormKey(segment: string): string {\n  return encodeURIComponent(segment)\n    .replace(\n      /[!'()*]/g,\n      (char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`,\n    )\n    .replace(\n      /[.[\\]'\"]/g,\n      (char) => `%${char.charCodeAt(0).toString(16).toUpperCase()}`,\n    );\n}\n\nexport function decodeJsonFormKey(segment: string): string {\n  try {\n    return decodeURIComponent(segment);\n  } catch {\n    return segment;\n  }\n}\n\nexport function joinJsonFormPath(parent: string, key: string | number): string {\n  const segment =\n    typeof key === \"number\" ? String(key) : encodeJsonFormKey(key);\n  return parent ? `${parent}.${segment}` : segment;\n}\n\nexport function joinJsonSourcePath(\n  parent: string,\n  key: string | number,\n): string {\n  const segment = String(key);\n  return parent ? `${parent}.${segment}` : segment;\n}\n\nexport function staticPropertyKeys(schema: Schema): Set<string> {\n  return new Set(\n    Object.keys(schemaProperties(schema)).flatMap((key) => [\n      key,\n      encodeJsonFormKey(key),\n    ]),\n  );\n}\n\nexport function dynamicPropertyEntries(\n  schema: Schema,\n  currentValue: unknown,\n  staticKeys: Set<string>,\n): Array<{ key: string; schema: Schema }> {\n  if (!isRecordValue(currentValue)) return [];\n  return Object.keys(currentValue).flatMap((key) => {\n    if (staticKeys.has(key)) return [];\n    const decodedKey = decodeJsonFormKey(key);\n    if (staticKeys.has(decodedKey)) return [];\n    const childSchema = dynamicPropertySchemaFor(schema, decodedKey);\n    return childSchema ? [{ key: decodedKey, schema: childSchema }] : [];\n  });\n}\n\nexport function schemaNeedsJsonFormPathEncoding(schema: Schema): boolean {\n  const { schema: inner } = unwrapNullable(schema);\n  const kind = fieldKind(inner);\n  if (kind === \"object\") {\n    const properties = schemaProperties(inner);\n    const patternProperties = schemaPatternProperties(inner);\n    return (\n      isRecordValue(inner.additionalProperties) ||\n      Object.values(patternProperties).some(isRecordValue) ||\n      Object.entries(properties).some(([key, child]) => {\n        return (\n          encodeJsonFormKey(key) !== key ||\n          (typeof child === \"object\" &&\n            child !== null &&\n            schemaNeedsJsonFormPathEncoding(child))\n        );\n      })\n    );\n  }\n  if (kind === \"array\" && typeof inner.items === \"object\" && inner.items) {\n    if (Array.isArray(inner.items)) {\n      return inner.items.some((item) =>\n        isRecordValue(item)\n          ? schemaNeedsJsonFormPathEncoding(item as Schema)\n          : false,\n      );\n    }\n    return schemaNeedsJsonFormPathEncoding(inner.items as Schema);\n  }\n  return false;\n}\n\nexport function encodeJsonFormValue(schema: Schema, value: unknown): unknown {\n  const { schema: inner } = unwrapNullable(schema);\n  const kind = fieldKind(inner);\n\n  if (kind === \"array\") {\n    if (!Array.isArray(value)) return value;\n    return value.map((item, index) =>\n      encodeJsonFormValue(arrayItemSchemaAt(inner, index), item),\n    );\n  }\n\n  if (kind !== \"object\" || !isRecordValue(value)) return value;\n\n  const properties = schemaProperties(inner);\n  const encoded: Record<string, unknown> = {};\n  for (const [key, child] of Object.entries(properties)) {\n    if (typeof child !== \"object\" || child === null) continue;\n    const encodedKey = encodeJsonFormKey(key);\n    const rawValue = Object.prototype.hasOwnProperty.call(value, key)\n      ? value[key]\n      : value[encodedKey];\n    if (\n      rawValue !== undefined ||\n      Object.prototype.hasOwnProperty.call(value, key)\n    ) {\n      encoded[encodedKey] = encodeJsonFormValue(child, rawValue);\n    }\n  }\n  const propertyKeys = new Set(Object.keys(properties));\n  for (const [key, rawValue] of Object.entries(value)) {\n    const decodedKey = decodeJsonFormKey(key);\n    if (propertyKeys.has(key) || propertyKeys.has(decodedKey)) continue;\n    const childSchema = dynamicPropertySchemaFor(inner, decodedKey);\n    if (!childSchema) continue;\n    encoded[encodeJsonFormKey(decodedKey)] = encodeJsonFormValue(\n      childSchema,\n      rawValue,\n    );\n  }\n  return encoded;\n}\n\nexport function decodeJsonFormValue(schema: Schema, value: unknown): unknown {\n  const { schema: inner } = unwrapNullable(schema);\n  const kind = fieldKind(inner);\n\n  if (kind === \"array\") {\n    if (!Array.isArray(value)) return value;\n    return value.map((item, index) =>\n      decodeJsonFormValue(arrayItemSchemaAt(inner, index), item),\n    );\n  }\n\n  if (kind !== \"object\" || !isRecordValue(value)) return value;\n\n  const properties = schemaProperties(inner);\n  const decoded: Record<string, unknown> = {};\n  const handledKeys = new Set<string>();\n  for (const [key, child] of Object.entries(properties)) {\n    if (typeof child !== \"object\" || child === null) continue;\n    const encodedKey = encodeJsonFormKey(key);\n    const hasEncoded = Object.prototype.hasOwnProperty.call(value, encodedKey);\n    const rawValue = hasEncoded ? value[encodedKey] : value[key];\n    handledKeys.add(encodedKey);\n    handledKeys.add(key);\n    if (\n      rawValue !== undefined ||\n      hasEncoded ||\n      Object.prototype.hasOwnProperty.call(value, key)\n    ) {\n      decoded[key] = decodeJsonFormValue(child, rawValue);\n    }\n  }\n  for (const [key, rawValue] of Object.entries(value)) {\n    const decodedKey = decodeJsonFormKey(key);\n    if (handledKeys.has(key) || handledKeys.has(decodedKey)) continue;\n    const childSchema = dynamicPropertySchemaFor(inner, decodedKey);\n    if (!childSchema) continue;\n    decoded[decodedKey] = decodeJsonFormValue(childSchema, rawValue);\n  }\n  return decoded;\n}\n\nexport function emptyArrayItemFormValue(schema: Schema): unknown {\n  const { schema: inner, nullable } = unwrapNullable(schema);\n  if (nullable) return null;\n  if (fieldKind(inner) !== \"object\") return emptyValueFor(inner);\n\n  const value: Record<string, unknown> = {};\n  const shouldEncodeKeys = schemaNeedsJsonFormPathEncoding(inner);\n  for (const [key, child] of Object.entries(schemaProperties(inner))) {\n    if (typeof child === \"object\" && child !== null) {\n      value[shouldEncodeKeys ? encodeJsonFormKey(key) : key] =\n        emptyValueFor(child);\n    }\n  }\n  return value;\n}\n",
      "type": "registry:component",
      "target": "@components/json-form/path-codec.ts"
    },
    {
      "path": "components/json-form/scalar-control.tsx",
      "content": "\"use client\";\n\nimport {\n  BooleanControl,\n  NullableBooleanControl,\n} from \"@/components/json-form/scalar/boolean-control\";\nimport {\n  datetimeLocalInputValue,\n  DateTimeScalarControl,\n} from \"@/components/json-form/scalar/date-time-control\";\nimport {\n  EnumControl,\n  enumLabel,\n  enumValueEquals,\n} from \"@/components/json-form/scalar/enum-control\";\nimport {\n  dataCellNumberValue,\n  NumberControl,\n} from \"@/components/json-form/scalar/number-control\";\nimport {\n  dataCellTextValue,\n  TextControl,\n} from \"@/components/json-form/scalar/text-control\";\nimport type {\n  ControlFieldApi,\n  DateTimeControlKind,\n  JsonFormTextInput,\n  ScalarControlDomProps,\n} from \"@/components/json-form/scalar/types\";\nimport type { FieldKind, Schema } from \"@/components/json-form/schema-model\";\n\nexport type {\n  ControlFieldApi,\n  JsonFormTextInput,\n  ScalarControlDomProps,\n} from \"@/components/json-form/scalar/types\";\nexport {\n  BooleanControl,\n  dataCellNumberValue,\n  dataCellTextValue,\n  datetimeLocalInputValue,\n  enumLabel,\n  enumValueEquals,\n  NullableBooleanControl,\n};\n\nexport function ScalarControl({\n  kind,\n  schema,\n  field,\n  textInput,\n  compact = false,\n  nullable = false,\n  ...controlProps\n}: {\n  kind: FieldKind;\n  schema: Schema;\n  field: ControlFieldApi;\n  textInput?: JsonFormTextInput;\n  /** Dense, single-line variant for table cells. */\n  compact?: boolean;\n  nullable?: boolean;\n} & ScalarControlDomProps) {\n  if (kind === \"enum\") {\n    return (\n      <EnumControl\n        {...controlProps}\n        schema={schema}\n        field={field}\n        compact={compact}\n        nullable={nullable}\n      />\n    );\n  }\n\n  if (kind === \"number\" || kind === \"integer\") {\n    return (\n      <NumberControl\n        {...controlProps}\n        kind={kind}\n        field={field}\n        compact={compact}\n        nullable={nullable}\n      />\n    );\n  }\n\n  if (\n    schema.format === \"date\" ||\n    schema.format === \"time\" ||\n    schema.format === \"date-time\"\n  ) {\n    return (\n      <DateTimeScalarControl\n        {...controlProps}\n        kind={schema.format as DateTimeControlKind}\n        field={field}\n        compact={compact}\n        nullable={nullable}\n      />\n    );\n  }\n\n  return (\n    <TextControl\n      {...controlProps}\n      schema={schema}\n      field={field}\n      textInput={textInput}\n      compact={compact}\n      nullable={nullable}\n    />\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/json-form/scalar-control.tsx"
    },
    {
      "path": "components/json-form/scalar/boolean-control.tsx",
      "content": "\"use client\";\n\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from \"@/components/ui/select\";\nimport { Checkbox } from \"@/components/json-form/form-primitives\";\nimport { NULL_SELECT_VALUE } from \"@/components/json-form/scalar/enum-control\";\nimport {\n  type ControlFieldApi,\n  type ScalarControlDomProps,\n} from \"@/components/json-form/scalar/types\";\n\nexport function BooleanControl({\n  field,\n  label,\n}: {\n  field: ControlFieldApi;\n  label: string;\n}) {\n  return (\n    <Checkbox\n      checked={Boolean(field.value)}\n      aria-label={label}\n      onCheckedChange={(value) => field.onChange(value === true)}\n      onBlur={field.onBlur}\n    />\n  );\n}\n\nexport function NullableBooleanControl({\n  field,\n  label,\n  ...controlProps\n}: {\n  field: ControlFieldApi;\n  label: string;\n} & ScalarControlDomProps) {\n  const selectValue =\n    field.value === true\n      ? \"true\"\n      : field.value === false\n        ? \"false\"\n        : NULL_SELECT_VALUE;\n  const displayValue =\n    field.value === true\n      ? \"True\"\n      : field.value === false\n        ? \"False\"\n        : \"No value\";\n\n  return (\n    <Select\n      value={selectValue}\n      onValueChange={(value) => {\n        if (value === \"true\") {\n          field.onChange(true);\n          return;\n        }\n        if (value === \"false\") {\n          field.onChange(false);\n          return;\n        }\n        field.onChange(null);\n      }}\n    >\n      <SelectTrigger {...controlProps} aria-label={label}>\n        <SelectValue placeholder=\"Select...\">{displayValue}</SelectValue>\n      </SelectTrigger>\n      <SelectContent>\n        <SelectItem value={NULL_SELECT_VALUE}>No value</SelectItem>\n        <SelectItem value=\"true\">True</SelectItem>\n        <SelectItem value=\"false\">False</SelectItem>\n      </SelectContent>\n    </Select>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/json-form/scalar/boolean-control.tsx"
    },
    {
      "path": "components/json-form/scalar/date-time-control.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { CalendarIcon, ClockIcon } from \"lucide-react\";\n\nimport { cn } from \"@/lib/utils\";\nimport { Button } from \"@/components/ui/button\";\nimport { Calendar } from \"@/components/ui/calendar\";\nimport {\n  DataCell,\n  formatDataCellDisplayValue,\n} from \"@/components/ui/data-cell\";\nimport { Input } from \"@/components/ui/input\";\nimport {\n  Popover,\n  PopoverContent,\n  PopoverTrigger,\n} from \"@/components/ui/popover\";\nimport {\n  compactJsonFormDataCellClass,\n  type ControlFieldApi,\n  type DateTimeControlKind,\n  type ScalarControlDomProps,\n} from \"@/components/json-form/scalar/types\";\n\nexport function datetimeLocalInputValue(value: string): string {\n  const withoutTimezone = value.trim().replace(/(?:Z|[+-]\\d{2}:\\d{2})$/, \"\");\n  return withoutTimezone.match(/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}/)?.[0] ?? value;\n}\n\nexport function DateTimeScalarControl({\n  kind,\n  field,\n  compact,\n  nullable,\n  ...controlProps\n}: {\n  kind: DateTimeControlKind;\n  field: ControlFieldApi;\n  compact: boolean;\n  nullable: boolean;\n} & ScalarControlDomProps) {\n  const value = field.value == null ? \"\" : String(field.value);\n  if (!compact) {\n    return (\n      <DateTimePickerControl\n        {...controlProps}\n        kind={kind}\n        field={field}\n        nullable={nullable}\n      />\n    );\n  }\n\n  return (\n    <DataCell\n      {...controlProps}\n      kind={kind}\n      active\n      value={field.value == null ? null : value}\n      dateTimeZone={kind === \"date-time\" ? \"preserve\" : undefined}\n      draftValue={kind === \"date-time\" ? datetimeLocalInputValue(value) : value}\n      className={compactJsonFormDataCellClass}\n      onDraftValueChange={(nextValue) =>\n        field.onChange(nextValue === \"\" && nullable ? null : nextValue)\n      }\n      onCommit={(nextValue) =>\n        field.onChange(nextValue === \"\" && nullable ? null : nextValue)\n      }\n      onBlur={field.onBlur}\n      name={field.name}\n    />\n  );\n}\n\nfunction pickerPlaceholder(kind: DateTimeControlKind): string {\n  if (kind === \"time\") return \"Pick a time\";\n  if (kind === \"date-time\") return \"Pick a date and time\";\n  return \"Pick a date\";\n}\n\nfunction pickerEditValue(kind: DateTimeControlKind, value: string): string {\n  if (kind === \"date-time\") return datetimeLocalInputValue(value);\n  if (kind === \"date\") return value.match(/^\\d{4}-\\d{2}-\\d{2}/)?.[0] ?? value;\n  if (kind === \"time\")\n    return value.match(/^\\d{2}:\\d{2}(?::\\d{2})?/)?.[0] ?? value;\n  return value;\n}\n\nfunction pickerDate(\n  kind: DateTimeControlKind,\n  value: string,\n): Date | undefined {\n  if (kind === \"time\" || value === \"\") return undefined;\n  const dateValue =\n    kind === \"date-time\" ? value.match(/^\\d{4}-\\d{2}-\\d{2}/)?.[0] : value;\n  const match = dateValue?.match(/^(\\d{4})-(\\d{2})-(\\d{2})$/);\n  if (!match) return undefined;\n  return new Date(Number(match[1]), Number(match[2]) - 1, Number(match[3]));\n}\n\nfunction pickerTime(kind: DateTimeControlKind, value: string): string {\n  if (kind === \"date\") return \"\";\n  return value.match(/\\d{2}:\\d{2}(?::\\d{2})?/)?.[0] ?? \"\";\n}\n\nfunction pickerValueWithDate(\n  kind: DateTimeControlKind,\n  value: string,\n  date: Date,\n): string {\n  const dateValue = formatPickerDate(date);\n  if (kind === \"date\") return dateValue;\n  if (kind === \"time\") return value;\n  return `${dateValue}T${pickerTime(\"date-time\", value) || \"00:00\"}`;\n}\n\nfunction pickerValueWithTime(\n  kind: DateTimeControlKind,\n  value: string,\n  time: string,\n): string {\n  if (kind === \"time\") return time;\n  if (kind === \"date\") return value;\n  const dateValue =\n    value.match(/^\\d{4}-\\d{2}-\\d{2}/)?.[0] ?? formatPickerDate(new Date());\n  return `${dateValue}T${time || \"00:00\"}`;\n}\n\nfunction formatPickerDate(date: Date): string {\n  const year = date.getFullYear();\n  const month = String(date.getMonth() + 1).padStart(2, \"0\");\n  const day = String(date.getDate()).padStart(2, \"0\");\n  return `${year}-${month}-${day}`;\n}\n\nfunction DateTimePickerControl({\n  kind,\n  field,\n  nullable,\n  ...controlProps\n}: {\n  kind: DateTimeControlKind;\n  field: ControlFieldApi;\n  nullable: boolean;\n} & ScalarControlDomProps) {\n  const [open, setOpen] = React.useState(false);\n  const value = field.value == null ? \"\" : String(field.value);\n  const pickerValue = pickerEditValue(kind, value);\n  const selectedDate = pickerDate(kind, pickerValue);\n  const timeValue = pickerTime(kind, pickerValue);\n  const displayValue = formatDataCellDisplayValue(kind, value);\n  const isEmpty = displayValue === \"\";\n\n  const commitPickerValue = (nextValue: string) => {\n    field.onChange(nextValue === \"\" && nullable ? null : nextValue);\n  };\n\n  const setDate = (date: Date) => {\n    const nextValue = pickerValueWithDate(kind, pickerValue, date);\n    commitPickerValue(nextValue);\n    if (kind === \"date\") setOpen(false);\n  };\n\n  const setTime = (time: string) => {\n    commitPickerValue(pickerValueWithTime(kind, pickerValue, time));\n  };\n\n  const setToday = () => {\n    commitPickerValue(pickerValueWithDate(kind, pickerValue, new Date()));\n    if (kind === \"date\") setOpen(false);\n  };\n\n  const setNow = () => {\n    const now = new Date();\n    const time = `${String(now.getHours()).padStart(2, \"0\")}:${String(\n      now.getMinutes(),\n    ).padStart(2, \"0\")}`;\n    commitPickerValue(\n      kind === \"time\"\n        ? time\n        : pickerValueWithTime(\n            kind,\n            pickerValueWithDate(kind, pickerValue, now),\n            time,\n          ),\n    );\n  };\n\n  return (\n    <Popover open={open} onOpenChange={setOpen}>\n      <PopoverTrigger asChild>\n        <button\n          {...controlProps}\n          type=\"button\"\n          data-empty={isEmpty || undefined}\n          className=\"border-input bg-background text-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:border-destructive/36 focus-visible:aria-invalid:border-destructive/64 focus-visible:aria-invalid:ring-destructive/16 dark:bg-input/32 inline-flex h-8.5 w-full min-w-0 items-center justify-between gap-2 rounded-lg border px-[calc(--spacing(3)-1px)] text-left text-base font-normal shadow-xs/5 transition-shadow outline-none focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-64 sm:h-7.5 sm:text-sm\"\n          onBlur={field.onBlur}\n        >\n          <span className={cn(\"truncate\", isEmpty && \"text-muted-foreground\")}>\n            {isEmpty ? pickerPlaceholder(kind) : displayValue}\n          </span>\n          {kind === \"time\" ? (\n            <ClockIcon className=\"size-4.5 shrink-0 opacity-80 sm:size-4\" />\n          ) : (\n            <CalendarIcon className=\"size-4.5 shrink-0 opacity-80 sm:size-4\" />\n          )}\n        </button>\n      </PopoverTrigger>\n      <PopoverContent\n        align=\"start\"\n        className=\"w-auto rounded-xl p-2 before:rounded-[calc(var(--radius-xl)-1px)]\"\n      >\n        {(kind === \"date\" || kind === \"date-time\") && (\n          <Calendar\n            mode=\"single\"\n            selected={selectedDate}\n            defaultMonth={selectedDate}\n            onSelect={(date) => {\n              if (date) setDate(date);\n            }}\n          />\n        )}\n        {(kind === \"time\" || kind === \"date-time\") && (\n          <div className=\"border-t p-3 first:border-t-0\">\n            <Input\n              nativeInput\n              type=\"time\"\n              step={1}\n              value={timeValue}\n              onChange={(event) => setTime(event.currentTarget.value)}\n            />\n          </div>\n        )}\n        <div className=\"flex items-center justify-between gap-2 border-t p-2\">\n          <Button\n            type=\"button\"\n            variant=\"ghost\"\n            size=\"sm\"\n            onClick={() => commitPickerValue(\"\")}\n          >\n            Clear\n          </Button>\n          <Button\n            type=\"button\"\n            variant=\"ghost\"\n            size=\"sm\"\n            onClick={kind === \"time\" ? setNow : setToday}\n          >\n            {kind === \"time\" ? \"Now\" : \"Today\"}\n          </Button>\n        </div>\n      </PopoverContent>\n    </Popover>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/json-form/scalar/date-time-control.tsx"
    },
    {
      "path": "components/json-form/scalar/enum-control.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from \"@/components/ui/select\";\nimport {\n  compactJsonFormSelectDataCellClass,\n  type ControlFieldApi,\n  type ScalarControlDomProps,\n} from \"@/components/json-form/scalar/types\";\nimport {\n  isRecordValue,\n  type Schema,\n} from \"@/components/json-form/schema-model\";\n\nexport const NULL_SELECT_VALUE = \"__json-form-null__\";\n\nfunction enumOptionValue(index: number): string {\n  return `enum:${index}`;\n}\n\nexport function enumLabel(value: unknown): string {\n  if (value === null) return \"No value\";\n  if (typeof value === \"string\") return value;\n  return JSON.stringify(value);\n}\n\nfunction hasOwnRecordValue(\n  value: Record<string, unknown>,\n  key: string,\n): boolean {\n  return Object.prototype.hasOwnProperty.call(value, key);\n}\n\nexport function enumValueEquals(a: unknown, b: unknown): boolean {\n  if (Object.is(a, b)) return true;\n  if (Array.isArray(a) || Array.isArray(b)) {\n    if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) {\n      return false;\n    }\n    return a.every((item, index) => enumValueEquals(item, b[index]));\n  }\n  if (!isRecordValue(a) || !isRecordValue(b)) {\n    return false;\n  }\n\n  const aKeys = Object.keys(a);\n  const bKeys = Object.keys(b);\n  if (aKeys.length !== bKeys.length) return false;\n  return aKeys.every(\n    (key) => hasOwnRecordValue(b, key) && enumValueEquals(a[key], b[key]),\n  );\n}\n\nexport function EnumControl({\n  schema,\n  field,\n  compact,\n  nullable,\n  ...controlProps\n}: {\n  schema: Schema;\n  field: ControlFieldApi;\n  compact: boolean;\n  nullable: boolean;\n} & ScalarControlDomProps) {\n  const enumValues = schema.enum ?? [];\n  const hasNullEnumValue = enumValues.some((value) => value === null);\n  const currentIndex = enumValues.findIndex((value) =>\n    enumValueEquals(value, field.value),\n  );\n  const selectValue =\n    currentIndex >= 0\n      ? enumOptionValue(currentIndex)\n      : field.value === null && nullable\n        ? NULL_SELECT_VALUE\n        : undefined;\n  const displayValue =\n    currentIndex >= 0\n      ? enumLabel(enumValues[currentIndex])\n      : field.value === null && nullable\n        ? \"No value\"\n        : undefined;\n\n  return (\n    <Select\n      value={selectValue}\n      onValueChange={(value) => {\n        if (typeof value !== \"string\") return;\n        if (value === NULL_SELECT_VALUE) {\n          field.onChange(null);\n          return;\n        }\n        const index = Number(value.replace(\"enum:\", \"\"));\n        field.onChange(enumValues[index]);\n      }}\n    >\n      <SelectTrigger\n        {...controlProps}\n        {...(compact\n          ? {\n              \"data-slot\": \"data-cell\",\n              \"data-kind\": \"text\",\n              \"data-mode\": \"edit\",\n            }\n          : {})}\n        className={compact ? compactJsonFormSelectDataCellClass : undefined}\n      >\n        <SelectValue placeholder=\"Select...\">{displayValue}</SelectValue>\n      </SelectTrigger>\n      <SelectContent>\n        {nullable && !hasNullEnumValue ? (\n          <SelectItem value={NULL_SELECT_VALUE}>No value</SelectItem>\n        ) : null}\n        {enumValues.map((option, index) => (\n          <SelectItem\n            key={enumOptionValue(index)}\n            value={enumOptionValue(index)}\n          >\n            {enumLabel(option)}\n          </SelectItem>\n        ))}\n      </SelectContent>\n    </Select>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/json-form/scalar/enum-control.tsx"
    },
    {
      "path": "components/json-form/scalar/number-control.tsx",
      "content": "\"use client\";\n\nimport {\n  DataCell,\n  parseDataCellNumberInput,\n  type DataCellCommitValue,\n  type DataCellValueMeta,\n} from \"@/components/ui/data-cell\";\nimport { Input } from \"@/components/ui/input\";\nimport {\n  compactJsonFormDataCellClass,\n  type ControlFieldApi,\n  type ScalarControlDomProps,\n} from \"@/components/json-form/scalar/types\";\n\nexport function NumberControl({\n  kind,\n  field,\n  compact,\n  nullable,\n  ...controlProps\n}: {\n  kind: \"number\" | \"integer\";\n  field: ControlFieldApi;\n  compact: boolean;\n  nullable: boolean;\n} & ScalarControlDomProps) {\n  if (!compact) {\n    return (\n      <Input\n        {...controlProps}\n        nativeInput\n        type=\"number\"\n        inputMode={kind === \"integer\" ? \"numeric\" : \"decimal\"}\n        step={kind === \"integer\" ? 1 : \"any\"}\n        value={field.value == null ? \"\" : String(field.value)}\n        onChange={(event) =>\n          updateNumberValue({\n            kind,\n            value: event.currentTarget.value,\n            nullable,\n            field,\n          })\n        }\n        onBlur={field.onBlur}\n        name={field.name}\n      />\n    );\n  }\n\n  return (\n    <DataCell\n      {...controlProps}\n      kind={kind}\n      active\n      value={dataCellNumberValue(field.value)}\n      draftValue={field.value == null ? \"\" : String(field.value)}\n      className={compactJsonFormDataCellClass}\n      onDraftValueChange={(value, meta) =>\n        updateNumberValue({ kind, value, meta, nullable, field })\n      }\n      onCommit={(value, meta) =>\n        updateNumberValue({ kind, value, meta, nullable, field })\n      }\n      onBlur={field.onBlur}\n      name={field.name}\n    />\n  );\n}\n\nfunction updateNumberValue({\n  kind,\n  value,\n  meta,\n  nullable,\n  field,\n}: {\n  kind: \"number\" | \"integer\";\n  value: DataCellCommitValue | string;\n  meta?: DataCellValueMeta;\n  nullable: boolean;\n  field: ControlFieldApi;\n}) {\n  const rawValue = meta?.rawValue ?? (typeof value === \"string\" ? value : \"\");\n  const parsed = parseDataCellNumberInput({ kind, value: rawValue });\n\n  if (!parsed.isValid) return;\n  if (parsed.isEmpty) {\n    field.onChange(nullable ? null : undefined);\n    return;\n  }\n  field.onChange(parsed.value);\n}\n\nexport function dataCellNumberValue(value: unknown): string | number | null {\n  return typeof value === \"number\" || typeof value === \"string\" ? value : null;\n}\n",
      "type": "registry:component",
      "target": "@components/json-form/scalar/number-control.tsx"
    },
    {
      "path": "components/json-form/scalar/text-control.tsx",
      "content": "\"use client\";\n\nimport { DataCell } from \"@/components/ui/data-cell\";\nimport { Input } from \"@/components/ui/input\";\nimport { Textarea } from \"@/components/json-form/form-primitives\";\nimport {\n  compactJsonFormDataCellClass,\n  type ControlFieldApi,\n  type JsonFormTextInput,\n  type ScalarControlDomProps,\n} from \"@/components/json-form/scalar/types\";\nimport type { Schema } from \"@/components/json-form/schema-model\";\n\nexport function TextControl({\n  schema,\n  field,\n  textInput,\n  compact,\n  nullable,\n  ...controlProps\n}: {\n  schema: Schema;\n  field: ControlFieldApi;\n  textInput?: JsonFormTextInput;\n  compact: boolean;\n  nullable: boolean;\n} & ScalarControlDomProps) {\n  const value = field.value == null ? \"\" : String(field.value);\n\n  if (!compact && shouldRenderTextarea(schema, textInput)) {\n    return (\n      <Textarea\n        {...controlProps}\n        value={value}\n        onChange={(event) =>\n          field.onChange(\n            event.target.value === \"\" && nullable ? null : event.target.value,\n          )\n        }\n        onBlur={field.onBlur}\n        name={field.name}\n      />\n    );\n  }\n\n  if (!compact) {\n    return (\n      <Input\n        {...controlProps}\n        value={value}\n        onChange={(event) => {\n          const nextValue = event.currentTarget.value;\n          field.onChange(nextValue === \"\" && nullable ? null : nextValue);\n        }}\n        onBlur={field.onBlur}\n        name={field.name}\n      />\n    );\n  }\n\n  return (\n    <DataCell\n      {...controlProps}\n      kind=\"text\"\n      active\n      value={field.value == null ? null : value}\n      draftValue={value}\n      className={compactJsonFormDataCellClass}\n      onDraftValueChange={(nextValue) =>\n        field.onChange(nextValue === \"\" && nullable ? null : nextValue)\n      }\n      onCommit={(nextValue) =>\n        field.onChange(nextValue === \"\" && nullable ? null : nextValue)\n      }\n      onBlur={field.onBlur}\n      name={field.name}\n    />\n  );\n}\n\nfunction shouldRenderTextarea(\n  schema: Schema,\n  textInput: JsonFormTextInput | undefined,\n): boolean {\n  if (textInput === \"input\") return false;\n  if (textInput === \"textarea\") return true;\n  return schema.format === \"textarea\" || (schema.maxLength ?? 0) > 120;\n}\n\nexport function dataCellTextValue(value: unknown): string | null {\n  return value === null || value === undefined ? null : String(value);\n}\n",
      "type": "registry:component",
      "target": "@components/json-form/scalar/text-control.tsx"
    },
    {
      "path": "components/json-form/scalar/types.ts",
      "content": "import type * as React from \"react\";\n\nexport type JsonFormTextInput = \"input\" | \"textarea\";\nexport type DateTimeControlKind = \"date\" | \"time\" | \"date-time\";\n\nexport interface ControlFieldApi {\n  value: unknown;\n  onChange: (value: unknown) => void;\n  onBlur: () => void;\n  name: string;\n  ref?: React.Ref<HTMLElement>;\n}\n\nexport type ScalarControlDomProps = {\n  id?: string;\n  \"aria-describedby\"?: string;\n  \"aria-invalid\"?: boolean;\n  \"data-slot\"?: string;\n};\n\nexport const compactJsonFormDataCellClass =\n  \"h-8 rounded-md border-transparent bg-transparent px-2 text-sm shadow-none transition-colors hover:border-border hover:bg-background focus-visible:border-ring focus-visible:bg-background focus-visible:ring-1 focus-visible:ring-ring/30\";\n\nexport const compactJsonFormSelectDataCellClass =\n  \"h-8 rounded-md border-transparent bg-transparent px-2 text-sm shadow-none transition-colors hover:border-border hover:bg-background focus-visible:border-ring focus-visible:bg-background focus-visible:ring-1 focus-visible:ring-ring/30\";\n",
      "type": "registry:component",
      "target": "@components/json-form/scalar/types.ts"
    },
    {
      "path": "components/json-form/schema-model.ts",
      "content": "import type { JSONSchema7, JSONSchema7Definition } from \"json-schema\";\n\nexport type Schema = JSONSchema7;\nexport type JsonFormSchemaNode = Schema;\n\nexport type FieldKind =\n  | \"string\"\n  | \"number\"\n  | \"integer\"\n  | \"boolean\"\n  | \"enum\"\n  | \"object\"\n  | \"array\";\n\nexport type JsonFormFieldKind = FieldKind;\n\nexport interface NormalizedSchema {\n  schema: Schema;\n  nullable: boolean;\n}\n\nexport interface Column {\n  key: string;\n  schema: Schema;\n  kind: FieldKind;\n  required: boolean;\n  nullable: boolean;\n}\n\nexport type JsonFormColumn = Column;\n\nexport function isSchema(\n  value: JSONSchema7Definition | unknown,\n): value is Schema {\n  return typeof value === \"object\" && value !== null;\n}\n\nexport function isRecordValue(\n  value: unknown,\n): value is Record<string, unknown> {\n  return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction cloneSchema(schema: Schema): Schema {\n  return JSON.parse(JSON.stringify(schema)) as Schema;\n}\n\nfunction definitionsFrom(\n  schema: Schema,\n): Record<string, JSONSchema7Definition> {\n  return {\n    ...((schema.definitions ?? {}) as Record<string, JSONSchema7Definition>),\n    ...((schema.$defs ?? {}) as Record<string, JSONSchema7Definition>),\n  };\n}\n\nfunction mergeSchemas(base: Schema, next: Schema): Schema {\n  const merged: Schema = { ...base, ...next };\n  if (base.properties || next.properties) {\n    const baseProperties = (base.properties ?? {}) as Record<\n      string,\n      JSONSchema7Definition\n    >;\n    const nextProperties = (next.properties ?? {}) as Record<\n      string,\n      JSONSchema7Definition\n    >;\n    merged.properties = { ...baseProperties };\n    for (const [key, value] of Object.entries(nextProperties)) {\n      const existing = merged.properties[key];\n      merged.properties[key] =\n        isSchema(existing) && isSchema(value)\n          ? mergeSchemas(existing, value)\n          : value;\n    }\n  }\n  if (base.required || next.required) {\n    merged.required = Array.from(\n      new Set([...(base.required ?? []), ...(next.required ?? [])]),\n    );\n  }\n  return merged;\n}\n\nfunction decodePointerSegment(segment: string): string {\n  return segment.replace(/~1/g, \"/\").replace(/~0/g, \"~\");\n}\n\nfunction resolveLocalPointer(\n  schema: Schema,\n  ref: string,\n): JSONSchema7Definition | undefined {\n  if (!ref.startsWith(\"#/\")) return undefined;\n\n  let current: unknown = schema;\n  for (const segment of ref.slice(2).split(\"/\").map(decodePointerSegment)) {\n    if (!isSchema(current) && !Array.isArray(current)) return undefined;\n    current = (current as Record<string, unknown>)[segment];\n  }\n  return current as JSONSchema7Definition | undefined;\n}\n\nfunction refKey(ref: string): string | null {\n  if (ref.startsWith(\"#/$defs/\")) {\n    return decodePointerSegment(ref.slice(\"#/$defs/\".length));\n  }\n  if (ref.startsWith(\"#/definitions/\")) {\n    return decodePointerSegment(ref.slice(\"#/definitions/\".length));\n  }\n  return null;\n}\n\nfunction resolveRef(\n  ref: string,\n  rootSchema: Schema,\n  definitions: Record<string, JSONSchema7Definition>,\n): JSONSchema7Definition | undefined {\n  return resolveLocalPointer(rootSchema, ref) ?? definitions[refKey(ref) ?? \"\"];\n}\n\nexport function normalizeJsonFormSchema(schema: Schema): JsonFormSchemaNode {\n  return expandRefs(schema);\n}\n\nexport function expandRefs(\n  schema: Schema,\n  definitions: Record<string, JSONSchema7Definition> = definitionsFrom(schema),\n  visited: Set<string> = new Set(),\n  rootSchema: Schema = schema,\n): Schema {\n  const working = cloneSchema(schema);\n\n  if (typeof working.$ref === \"string\") {\n    const ref = working.$ref;\n    const target = resolveRef(ref, rootSchema, definitions);\n    if (!isSchema(target) || visited.has(ref)) return working;\n\n    const nextVisited = new Set(visited);\n    nextVisited.add(ref);\n    const { $ref: _ref, ...overrides } = working;\n    return expandRefs(\n      mergeSchemas(\n        expandRefs(target, definitions, nextVisited, rootSchema),\n        overrides,\n      ),\n      definitions,\n      nextVisited,\n      rootSchema,\n    );\n  }\n\n  let normalized = working;\n  if (Array.isArray(normalized.allOf)) {\n    const allOf = normalized.allOf;\n    delete normalized.allOf;\n    normalized = allOf.reduce<Schema>((merged, branch) => {\n      return isSchema(branch)\n        ? mergeSchemas(\n            merged,\n            expandRefs(branch, definitions, visited, rootSchema),\n          )\n        : merged;\n    }, normalized);\n  }\n\n  for (const key of [\"anyOf\", \"oneOf\"] as const) {\n    const branches = normalized[key];\n    if (Array.isArray(branches)) {\n      normalized[key] = branches.map((branch) =>\n        isSchema(branch)\n          ? expandRefs(branch, definitions, visited, rootSchema)\n          : branch,\n      );\n    }\n  }\n\n  if (normalized.properties) {\n    const properties: Record<string, JSONSchema7Definition> = {};\n    for (const [key, value] of Object.entries(normalized.properties)) {\n      properties[key] = isSchema(value)\n        ? expandRefs(value, definitions, visited, rootSchema)\n        : value;\n    }\n    normalized.properties = properties;\n  }\n\n  if (isSchema(normalized.items)) {\n    normalized.items = expandRefs(\n      normalized.items,\n      definitions,\n      visited,\n      rootSchema,\n    );\n  } else if (Array.isArray(normalized.items)) {\n    normalized.items = normalized.items.map((item) =>\n      isSchema(item)\n        ? expandRefs(item, definitions, visited, rootSchema)\n        : item,\n    );\n  }\n\n  if (isSchema(normalized.additionalProperties)) {\n    normalized.additionalProperties = expandRefs(\n      normalized.additionalProperties,\n      definitions,\n      visited,\n      rootSchema,\n    );\n  }\n\n  if (normalized.patternProperties) {\n    const patternProperties: Record<string, JSONSchema7Definition> = {};\n    for (const [key, value] of Object.entries(normalized.patternProperties)) {\n      patternProperties[key] = isSchema(value)\n        ? expandRefs(value, definitions, visited, rootSchema)\n        : value;\n    }\n    normalized.patternProperties = patternProperties;\n  }\n\n  return normalized;\n}\n\n/** Resolve nullable unions like `[\"string\",\"null\"]` or `anyOf:[X,{type:\"null\"}]`. */\nexport function unwrapNullable(schema: Schema): NormalizedSchema {\n  if (Array.isArray(schema.type)) {\n    const nonNull = schema.type.filter((type) => type !== \"null\");\n    return {\n      schema: {\n        ...schema,\n        type: nonNull.length === 1 ? nonNull[0] : nonNull,\n      },\n      nullable: schema.type.includes(\"null\"),\n    };\n  }\n\n  for (const key of [\"anyOf\", \"oneOf\"] as const) {\n    const branches = schema[key];\n    if (!Array.isArray(branches)) continue;\n\n    const schemaBranches = branches.filter(isSchema);\n    const nullable = schemaBranches.some((branch) => branch.type === \"null\");\n    const main = schemaBranches.find((branch) => branch.type !== \"null\");\n    if (main) {\n      return {\n        schema: {\n          ...main,\n          title: schema.title ?? main.title,\n          description: schema.description ?? main.description,\n        },\n        nullable,\n      };\n    }\n  }\n\n  return { schema, nullable: false };\n}\n\nexport function fieldKind(schema: Schema): FieldKind {\n  if (Array.isArray(schema.enum)) return \"enum\";\n  const type = Array.isArray(schema.type)\n    ? schema.type.find((item) => item !== \"null\")\n    : schema.type;\n  switch (type) {\n    case \"number\":\n      return \"number\";\n    case \"integer\":\n      return \"integer\";\n    case \"boolean\":\n      return \"boolean\";\n    case \"object\":\n      return \"object\";\n    case \"array\":\n      return \"array\";\n    default:\n      return \"string\";\n  }\n}\n\nexport const jsonFormFieldKind = fieldKind;\n\nexport function labelFor(\n  name: string,\n  schema: Schema,\n  explicit?: string,\n): string {\n  if (explicit) return explicit;\n  if (schema.title) return schema.title;\n  const leaf = name.split(\".\").pop() ?? name;\n  return leaf\n    .replace(/[_-]+/g, \" \")\n    .replace(/\\b\\w/g, (char) => char.toUpperCase())\n    .trim();\n}\n\nexport function emptyValueFor(schema: Schema): unknown {\n  const { schema: inner, nullable } = unwrapNullable(schema);\n  if (nullable) return null;\n  switch (fieldKind(inner)) {\n    case \"boolean\":\n      return false;\n    case \"object\":\n      return {};\n    case \"array\":\n      return [];\n    case \"number\":\n    case \"integer\":\n      return undefined;\n    default:\n      return \"\";\n  }\n}\n\nexport const emptyJsonFormValue = emptyValueFor;\n\nexport function isScalarKind(kind: FieldKind): boolean {\n  return kind !== \"object\" && kind !== \"array\";\n}\n\nexport function schemaProperties(\n  schema: Schema,\n): Record<string, JSONSchema7Definition> {\n  return (schema.properties ?? {}) as Record<string, JSONSchema7Definition>;\n}\n\nexport function schemaPatternProperties(\n  schema: Schema,\n): Record<string, JSONSchema7Definition> {\n  return (schema.patternProperties ?? {}) as Record<\n    string,\n    JSONSchema7Definition\n  >;\n}\n\nfunction patternPropertySchemaFor(schema: Schema, key: string): Schema | null {\n  for (const [pattern, child] of Object.entries(\n    schemaPatternProperties(schema),\n  )) {\n    if (!isRecordValue(child)) continue;\n    try {\n      if (new RegExp(pattern).test(key)) return child as Schema;\n    } catch {\n      continue;\n    }\n  }\n  return null;\n}\n\nfunction additionalPropertySchemaFor(schema: Schema): Schema | null {\n  return isRecordValue(schema.additionalProperties)\n    ? (schema.additionalProperties as Schema)\n    : null;\n}\n\nexport function dynamicPropertySchemaFor(\n  schema: Schema,\n  key: string,\n): Schema | null {\n  return (\n    patternPropertySchemaFor(schema, key) ?? additionalPropertySchemaFor(schema)\n  );\n}\n\nexport function hasDynamicObjectProperties(schema: Schema): boolean {\n  const { schema: inner } = unwrapNullable(schema);\n  if (fieldKind(inner) !== \"object\") return false;\n  return (\n    isRecordValue(inner.additionalProperties) ||\n    Object.values(schemaPatternProperties(inner)).some(isRecordValue)\n  );\n}\n\nexport function scalarObjectColumns(itemSchema: Schema): Column[] | null {\n  const { schema } = unwrapNullable(itemSchema);\n  if (fieldKind(schema) !== \"object\") return null;\n  const properties = schemaProperties(schema);\n  const required = new Set(schema.required ?? []);\n  const columns: Column[] = [];\n  for (const [key, child] of Object.entries(properties)) {\n    if (!isSchema(child)) return null;\n    const { schema: inner, nullable } = unwrapNullable(child);\n    const kind = fieldKind(inner);\n    if (!isScalarKind(kind)) return null;\n    columns.push({\n      key,\n      schema: inner,\n      kind,\n      required: required.has(key),\n      nullable,\n    });\n  }\n  return columns.length > 0 ? columns : null;\n}\n\nexport function jsonFormTableColumns(itemSchema: Schema): Column[] | null {\n  return scalarObjectColumns(itemSchema);\n}\n\nexport function arrayItemSchemaAt(schema: Schema, index: number): Schema {\n  const items = schema.items;\n  if (Array.isArray(items)) {\n    const item = items[index];\n    if (isRecordValue(item)) return item as Schema;\n    if (isRecordValue(schema.additionalItems)) {\n      return schema.additionalItems as Schema;\n    }\n    return { type: \"string\" };\n  }\n  return isRecordValue(items) ? (items as Schema) : { type: \"string\" };\n}\n\nexport const jsonFormArrayItemNode = arrayItemSchemaAt;\n\nexport function canAppendArrayItem(schema: Schema, length: number): boolean {\n  if (typeof schema.maxItems === \"number\" && length >= schema.maxItems) {\n    return false;\n  }\n  if (\n    Array.isArray(schema.items) &&\n    schema.additionalItems === false &&\n    length >= schema.items.length\n  ) {\n    return false;\n  }\n  return true;\n}\n\nexport function canRemoveArrayItem(schema: Schema, length: number): boolean {\n  return typeof schema.minItems !== \"number\" || length > schema.minItems;\n}\n",
      "type": "registry:component",
      "target": "@components/json-form/schema-model.ts"
    },
    {
      "path": "components/json-form/source-link-table-hover.ts",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport type { SourceFieldLink } from \"@/components/ui/source-field-link\";\nimport { useSourceLinkFocusPreviewIntent } from \"@/components/json-form/source-link-focus-intent\";\nimport { useKeyedMountEffect } from \"@/hooks/use-keyed-mount-effect\";\nimport { joinEffectKey } from \"@/lib/effect-key\";\n\nexport type JsonFormSourceLinkActions = Omit<\n  SourceFieldLink,\n  \"activeSourcePath\"\n>;\n\ntype SourceTableCell = HTMLElement;\ntype SourcePointerPoint = { x: number; y: number };\ntype TableSourceHoverState = {\n  phase: \"idle\" | \"focusing\" | \"hovering\" | \"scrolling\";\n  pointerPoint: SourcePointerPoint | null;\n  sourcePath: string | null;\n};\n\nconst SOURCE_PATH_ATTRIBUTE = \"data-source-path\";\nconst SOURCE_ACTIVE_ATTRIBUTE = \"data-source-active\";\nconst SOURCE_CELL_SELECTOR = `[${SOURCE_PATH_ATTRIBUTE}]`;\nconst TABLE_CELL_SELECTOR = \"[data-table-cell]\";\nconst SCROLL_SOURCE_HOVER_INTERVAL_MS = 32;\n\nexport function useSourceTableHoverController({\n  tableRef,\n  activeSourcePath,\n  sourceLinkActions,\n  refreshKey,\n}: {\n  tableRef: React.RefObject<HTMLElement | null>;\n  activeSourcePath: string | null;\n  sourceLinkActions: JsonFormSourceLinkActions | null;\n  refreshKey: unknown;\n}) {\n  const sourceLinked = Boolean(sourceLinkActions);\n  const activeSourceCellRef = React.useRef<Element | null>(null);\n  const hoverStateRef = React.useRef<TableSourceHoverState>({\n    phase: \"idle\",\n    pointerPoint: null,\n    sourcePath: null,\n  });\n  const pendingHoverPathRef = React.useRef<string | null>(null);\n  const pendingHoverFrameRef = React.useRef<number | null>(null);\n  const pendingScrollHoverFrameRef = React.useRef<number | null>(null);\n  const latestScrollHoverAtRef = React.useRef(Number.NEGATIVE_INFINITY);\n  const shouldPreviewFocus = useSourceLinkFocusPreviewIntent();\n\n  const setActiveSourceCell = React.useCallback((cell: Element | null) => {\n    if (activeSourceCellRef.current === cell) return;\n    activeSourceCellRef.current?.removeAttribute(SOURCE_ACTIVE_ATTRIBUTE);\n    if (cell) cell.setAttribute(SOURCE_ACTIVE_ATTRIBUTE, \"true\");\n    activeSourceCellRef.current = cell;\n  }, []);\n\n  const sourcePathForCell = React.useCallback(\n    (cell: Element | null): string | null =>\n      cell?.getAttribute(SOURCE_PATH_ATTRIBUTE) ?? null,\n    [],\n  );\n\n  useKeyedMountEffect(\n    joinEffectKey([\n      activeSourcePath,\n      sourceLinked,\n      refreshKey,\n      setActiveSourceCell,\n      tableRef,\n    ]),\n    () => {\n      if (!sourceLinked || !activeSourcePath) {\n        setActiveSourceCell(null);\n        return;\n      }\n      if (\n        hoverStateRef.current.sourcePath === activeSourcePath &&\n        activeSourceCellRef.current?.getAttribute(SOURCE_PATH_ATTRIBUTE) ===\n          activeSourcePath\n      ) {\n        return;\n      }\n\n      const table = tableRef.current;\n      if (!table) return;\n      for (const cell of table.querySelectorAll(SOURCE_CELL_SELECTOR)) {\n        if (cell.getAttribute(SOURCE_PATH_ATTRIBUTE) === activeSourcePath) {\n          setActiveSourceCell(cell);\n          return;\n        }\n      }\n      setActiveSourceCell(null);\n    },\n  );\n\n  const getCellFromTarget = React.useCallback(\n    (target: EventTarget | null): SourceTableCell | null => {\n      if (!(target instanceof Element)) return null;\n      const cell = target.closest<SourceTableCell>(TABLE_CELL_SELECTOR);\n      return cell && tableRef.current?.contains(cell) ? cell : null;\n    },\n    [tableRef],\n  );\n\n  const cancelPendingHover = React.useCallback(() => {\n    if (pendingHoverFrameRef.current === null) return;\n    cancelAnimationFrame(pendingHoverFrameRef.current);\n    pendingHoverFrameRef.current = null;\n  }, []);\n\n  const cancelPendingScrollHover = React.useCallback(() => {\n    if (pendingScrollHoverFrameRef.current === null) return;\n    cancelAnimationFrame(pendingScrollHoverFrameRef.current);\n    pendingScrollHoverFrameRef.current = null;\n  }, []);\n\n  const reportHoverSourcePath = React.useCallback(\n    (path: string | null) => {\n      if (!sourceLinkActions) return;\n      pendingHoverPathRef.current = path;\n      if (pendingHoverFrameRef.current !== null) return;\n      pendingHoverFrameRef.current = requestAnimationFrame(() => {\n        pendingHoverFrameRef.current = null;\n        sourceLinkActions.onSourceHover(pendingHoverPathRef.current);\n      });\n    },\n    [sourceLinkActions],\n  );\n\n  const setHoverSourcePath = React.useCallback(\n    (path: string | null, cell: Element | null) => {\n      if (!sourceLinkActions) return;\n      const currentState = hoverStateRef.current;\n      if (currentState.sourcePath === path) return;\n      hoverStateRef.current = {\n        phase:\n          currentState.phase === \"scrolling\"\n            ? \"scrolling\"\n            : path\n              ? \"hovering\"\n              : \"idle\",\n        pointerPoint: currentState.pointerPoint,\n        sourcePath: path,\n      };\n      setActiveSourceCell(cell);\n      reportHoverSourcePath(path);\n    },\n    [sourceLinkActions, reportHoverSourcePath, setActiveSourceCell],\n  );\n\n  useKeyedMountEffect(\n    joinEffectKey([cancelPendingHover, cancelPendingScrollHover]),\n    () => () => {\n      cancelPendingHover();\n      cancelPendingScrollHover();\n    },\n  );\n\n  const selectCellSource = React.useCallback(\n    (cell: HTMLElement | null) => {\n      const sourcePath = sourcePathForCell(cell);\n      if (!sourcePath) return false;\n      cancelPendingHover();\n      sourceLinkActions?.selectSourcePath?.(sourcePath);\n      return true;\n    },\n    [cancelPendingHover, sourceLinkActions, sourcePathForCell],\n  );\n\n  const handlePointerMove = React.useCallback(\n    (event: React.PointerEvent<HTMLElement>) => {\n      if (!sourceLinkActions) return;\n      hoverStateRef.current = {\n        ...hoverStateRef.current,\n        pointerPoint: {\n          x: event.clientX,\n          y: event.clientY,\n        },\n      };\n      if (hoverStateRef.current.phase === \"scrolling\") return;\n      const cell = getCellFromTarget(event.target);\n      setHoverSourcePath(sourcePathForCell(cell), cell);\n    },\n    [\n      sourceLinkActions,\n      getCellFromTarget,\n      setHoverSourcePath,\n      sourcePathForCell,\n    ],\n  );\n\n  const handlePointerLeave = React.useCallback(\n    (event: React.PointerEvent<HTMLElement>) => {\n      hoverStateRef.current = {\n        ...hoverStateRef.current,\n        pointerPoint: {\n          x: event.clientX,\n          y: event.clientY,\n        },\n      };\n      setHoverSourcePath(null, null);\n    },\n    [setHoverSourcePath],\n  );\n\n  const handleScrollStart = React.useCallback(() => {\n    hoverStateRef.current = {\n      phase: \"scrolling\",\n      pointerPoint: hoverStateRef.current.pointerPoint,\n      sourcePath: hoverStateRef.current.sourcePath,\n    };\n  }, []);\n\n  const restoreHoverSourceAtPointer = React.useCallback(() => {\n    if (!sourceLinkActions) return;\n    const point = hoverStateRef.current.pointerPoint;\n    if (!point) return;\n    const ownerDocument = tableRef.current?.ownerDocument;\n    if (!ownerDocument) return;\n    const element = ownerDocument.elementFromPoint(point.x, point.y);\n    const cell = getCellFromTarget(element);\n    setHoverSourcePath(sourcePathForCell(cell), cell);\n  }, [\n    sourceLinkActions,\n    tableRef,\n    getCellFromTarget,\n    setHoverSourcePath,\n    sourcePathForCell,\n  ]);\n\n  const handleScrollEnd = React.useCallback(() => {\n    hoverStateRef.current = {\n      phase: hoverStateRef.current.sourcePath ? \"hovering\" : \"idle\",\n      pointerPoint: hoverStateRef.current.pointerPoint,\n      sourcePath: hoverStateRef.current.sourcePath,\n    };\n    cancelPendingScrollHover();\n    latestScrollHoverAtRef.current = Number.NEGATIVE_INFINITY;\n    restoreHoverSourceAtPointer();\n  }, [cancelPendingScrollHover, restoreHoverSourceAtPointer]);\n\n  const handleScrollMove = React.useCallback(() => {\n    if (!sourceLinkActions || pendingScrollHoverFrameRef.current !== null) {\n      return;\n    }\n    const now = performance.now();\n    if (\n      now - latestScrollHoverAtRef.current <\n      SCROLL_SOURCE_HOVER_INTERVAL_MS\n    ) {\n      return;\n    }\n    pendingScrollHoverFrameRef.current = requestAnimationFrame(() => {\n      pendingScrollHoverFrameRef.current = null;\n      latestScrollHoverAtRef.current = performance.now();\n      restoreHoverSourceAtPointer();\n    });\n  }, [sourceLinkActions, restoreHoverSourceAtPointer]);\n\n  const handleFocus = React.useCallback(\n    (event: React.FocusEvent<HTMLElement>) => {\n      if (!sourceLinkActions) return;\n      if (!shouldPreviewFocus(event)) return;\n      const cell = getCellFromTarget(event.target);\n      if (!cell) return;\n      const sourcePath = sourcePathForCell(cell);\n      hoverStateRef.current = {\n        phase: sourcePath ? \"focusing\" : \"idle\",\n        pointerPoint: hoverStateRef.current.pointerPoint,\n        sourcePath,\n      };\n      setActiveSourceCell(cell);\n      sourceLinkActions.onSourceHover(sourcePath);\n    },\n    [\n      sourceLinkActions,\n      shouldPreviewFocus,\n      getCellFromTarget,\n      setActiveSourceCell,\n      sourcePathForCell,\n    ],\n  );\n\n  const handleBlur = React.useCallback(\n    (event: React.FocusEvent<HTMLElement>) => {\n      if (!sourceLinkActions) return;\n      const cell = getCellFromTarget(event.target);\n      if (!cell || cell.contains(event.relatedTarget as Node | null)) return;\n      const sourcePath = sourcePathForCell(cell);\n      if (\n        hoverStateRef.current.phase !== \"focusing\" ||\n        hoverStateRef.current.sourcePath !== sourcePath\n      ) {\n        return;\n      }\n      hoverStateRef.current = {\n        phase: \"idle\",\n        pointerPoint: hoverStateRef.current.pointerPoint,\n        sourcePath: null,\n      };\n      setActiveSourceCell(null);\n      sourceLinkActions.onSourceHover(null);\n    },\n    [\n      sourceLinkActions,\n      getCellFromTarget,\n      setActiveSourceCell,\n      sourcePathForCell,\n    ],\n  );\n\n  return {\n    sourceLinked,\n    getCellFromTarget,\n    selectCellSource,\n    handlePointerMove,\n    handlePointerLeave,\n    handleFocus,\n    handleBlur,\n    handleScrollStart,\n    handleScrollMove,\n    handleScrollEnd,\n  };\n}\n",
      "type": "registry:component",
      "target": "@components/json-form/source-link-table-hover.ts"
    },
    {
      "path": "components/json-form/source-link.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\nimport type { SourceFieldLink } from \"@/components/ui/source-field-link\";\nimport { useSourceLinkFocusPreviewIntent } from \"@/components/json-form/source-link-focus-intent\";\nimport {\n  useSourceTableHoverController,\n  type JsonFormSourceLinkActions,\n} from \"@/components/json-form/source-link-table-hover\";\n\nexport type JsonFormSourceLink = SourceFieldLink;\n\nconst ActiveSourcePathContext = React.createContext<string | null>(null);\nconst SourceLinkActionsContext =\n  React.createContext<JsonFormSourceLinkActions | null>(null);\n\nexport function JsonFormSourceLinkProvider({\n  sourceLink,\n  children,\n}: {\n  sourceLink?: JsonFormSourceLink;\n  children: React.ReactNode;\n}) {\n  const onSourceHover = sourceLink?.onSourceHover;\n  const selectSourcePath = sourceLink?.selectSourcePath;\n  const sourceLinkActions = React.useMemo<JsonFormSourceLinkActions | null>(\n    () => (onSourceHover ? { onSourceHover, selectSourcePath } : null),\n    [onSourceHover, selectSourcePath],\n  );\n\n  return (\n    <SourceLinkActionsContext.Provider value={sourceLinkActions}>\n      <ActiveSourcePathContext.Provider\n        value={sourceLink?.activeSourcePath ?? null}\n      >\n        {children}\n      </ActiveSourcePathContext.Provider>\n    </SourceLinkActionsContext.Provider>\n  );\n}\n\nexport function useActiveSourcePath(): string | null {\n  return React.useContext(ActiveSourcePathContext);\n}\n\nexport function useSourceLinkActions(): JsonFormSourceLinkActions | null {\n  return React.useContext(SourceLinkActionsContext);\n}\n\nexport function useSourceLinkedTableCells({\n  tableRef,\n  refreshKey,\n}: {\n  tableRef: React.RefObject<HTMLElement | null>;\n  refreshKey: unknown;\n}) {\n  return useSourceTableHoverController({\n    activeSourcePath: useActiveSourcePath(),\n    refreshKey,\n    sourceLinkActions: useSourceLinkActions(),\n    tableRef,\n  });\n}\n\nfunction shouldSelectSourceFromKeyDown(event: React.KeyboardEvent): boolean {\n  if (event.defaultPrevented || event.key !== \"Enter\") return false;\n  return !(event.target instanceof HTMLTextAreaElement);\n}\n\nfunction shouldPreviewSourceFromPointerMove(\n  event: React.PointerEvent,\n): boolean {\n  return !event.defaultPrevented && event.pointerType !== \"touch\";\n}\n\n/**\n * Wraps a scalar leaf so it reports its source path from explicit pointer or\n * keyboard intent. Without a source link, it renders children unchanged.\n */\nexport function SourceLinkShell({\n  sourcePath,\n  children,\n}: {\n  sourcePath: string;\n  children: React.ReactNode;\n}) {\n  const activeSourcePath = useActiveSourcePath();\n  const sourceLinkActions = useSourceLinkActions();\n  const shouldPreviewFocus = useSourceLinkFocusPreviewIntent();\n  const focusPreviewedRef = React.useRef(false);\n  const pointerPreviewedRef = React.useRef(false);\n  if (!sourceLinkActions) return <>{children}</>;\n  const active = activeSourcePath === sourcePath;\n\n  const clearPreviewIfIdle = () => {\n    if (focusPreviewedRef.current || pointerPreviewedRef.current) return;\n    sourceLinkActions.onSourceHover(null);\n  };\n\n  return (\n    <div\n      data-source-active={active ? \"true\" : \"false\"}\n      data-source-path={sourcePath}\n      onPointerMove={(event) => {\n        if (\n          pointerPreviewedRef.current ||\n          !shouldPreviewSourceFromPointerMove(event)\n        ) {\n          return;\n        }\n        pointerPreviewedRef.current = true;\n        sourceLinkActions.onSourceHover(sourcePath);\n      }}\n      onPointerLeave={() => {\n        if (!pointerPreviewedRef.current) return;\n        pointerPreviewedRef.current = false;\n        clearPreviewIfIdle();\n      }}\n      onFocus={(event) => {\n        if (!shouldPreviewFocus(event)) return;\n        focusPreviewedRef.current = true;\n        sourceLinkActions.onSourceHover(sourcePath);\n      }}\n      onBlur={() => {\n        if (!focusPreviewedRef.current) return;\n        focusPreviewedRef.current = false;\n        clearPreviewIfIdle();\n      }}\n      onClick={() => sourceLinkActions.selectSourcePath?.(sourcePath)}\n      onKeyDownCapture={(event) => {\n        if (shouldSelectSourceFromKeyDown(event)) {\n          sourceLinkActions.selectSourcePath?.(sourcePath);\n        }\n      }}\n      className={cn(\n        \"rounded-md border px-3 py-2 transition-colors\",\n        active\n          ? \"border-primary/40 bg-primary/5\"\n          : \"hover:bg-muted/60 border-transparent\",\n      )}\n    >\n      {children}\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/json-form/source-link.tsx"
    },
    {
      "path": "components/json-form/table/array-table-body.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { FixedGridRowWindow } from \"@/components/ui/fixed-grid-row-window\";\nimport { useFixedRowVirtualization } from \"@/components/ui/fixed-grid-virtualization\";\nimport {\n  TABLE_JUMP_ROW_OVERSCAN,\n  TABLE_MAX_HEIGHT,\n  TABLE_ROW_HEIGHT,\n  TABLE_ROW_OVERSCAN,\n} from \"@/components/json-form/table/array-table-config\";\nimport {\n  useArrayTableScrollActivity,\n  type ArrayTableScrollHandlers,\n} from \"@/components/json-form/table/array-table-scroll\";\n\ntype ArrayTableField = { id: string };\n\nexport function StaticArrayTableBody({\n  fields,\n  scrollHandlers,\n  renderItem,\n}: {\n  fields: ArrayTableField[];\n  scrollHandlers: ArrayTableScrollHandlers;\n  renderItem: (index: number) => React.ReactNode;\n}) {\n  const scrollRef = React.useRef<HTMLDivElement>(null);\n  useArrayTableScrollActivity(scrollRef, scrollHandlers);\n\n  return (\n    <div\n      ref={scrollRef}\n      data-slot=\"json-form-table-scroll\"\n      className=\"overflow-y-auto\"\n      style={{ maxHeight: TABLE_MAX_HEIGHT }}\n    >\n      <div className=\"[contain:layout_paint_style]\">\n        {fields.map((entry, index) => (\n          <React.Fragment key={entry.id}>{renderItem(index)}</React.Fragment>\n        ))}\n      </div>\n    </div>\n  );\n}\n\nexport function FixedArrayTableBody({\n  fields,\n  scrollHandlers,\n  renderItem,\n}: {\n  fields: ArrayTableField[];\n  scrollHandlers: ArrayTableScrollHandlers;\n  renderItem: (index: number, rowTopPx: number) => React.ReactNode;\n}) {\n  const scrollRef = React.useRef<HTMLDivElement>(null);\n  const { virtualRowWindow, totalRowSize, viewportClientHeight } =\n    useFixedRowVirtualization({\n      rowCount: fields.length,\n      rowSize: TABLE_ROW_HEIGHT,\n      rowOverscan: TABLE_ROW_OVERSCAN,\n      jumpRowOverscan: TABLE_JUMP_ROW_OVERSCAN,\n      scrollRef,\n    });\n  useArrayTableScrollActivity(scrollRef, scrollHandlers);\n\n  return (\n    <div\n      ref={scrollRef}\n      data-slot=\"json-form-table-scroll\"\n      className=\"overflow-y-auto\"\n      style={{ maxHeight: TABLE_MAX_HEIGHT }}\n    >\n      <FixedGridRowWindow\n        totalSize={totalRowSize}\n        minWidth=\"100%\"\n        rowMinWidth=\"100%\"\n        virtualRowWindow={virtualRowWindow}\n        viewportHeight={viewportClientHeight}\n        offsetDataSlot=\"json-form-table-row-offset\"\n        windowDataSlot=\"json-form-table-row-window\"\n        className=\"[contain:layout_paint_style]\"\n      >\n        {virtualRowWindow.items.map((virtualRow) => (\n          <React.Fragment key={fields[virtualRow.index].id}>\n            {renderItem(virtualRow.index, virtualRow.start)}\n          </React.Fragment>\n        ))}\n      </FixedGridRowWindow>\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/json-form/table/array-table-body.tsx"
    },
    {
      "path": "components/json-form/table/array-table-cell-commit.ts",
      "content": "import { type DataCellValueMeta } from \"@/components/ui/data-cell\";\nimport { datetimeLocalInputValue } from \"@/components/json-form/scalar-control\";\nimport type { Column } from \"@/components/json-form/schema-model\";\n\nexport type SetArrayTableCellValue = (\n  path: string,\n  value: unknown,\n  options: {\n    shouldDirty: true;\n    shouldTouch: true;\n    shouldValidate: true;\n  },\n) => void;\n\nexport type CommitArrayTableCellValue = (\n  value: unknown,\n  meta?: DataCellValueMeta,\n) => void;\n\nexport const NO_ARRAY_TABLE_CELL_COMMIT = Symbol(\"NO_ARRAY_TABLE_CELL_COMMIT\");\n\nexport function normalizeArrayTableCellValue({\n  column,\n  currentValue,\n  nextValue,\n  meta,\n}: {\n    column: Column;\n    currentValue: unknown;\n    nextValue: unknown;\n    meta?: DataCellValueMeta;\n}): unknown | typeof NO_ARRAY_TABLE_CELL_COMMIT {\n  let normalizedValue: unknown;\n  if (column.kind === \"enum\") {\n    normalizedValue = nextValue;\n  } else if (column.kind === \"number\" || column.kind === \"integer\") {\n    if (meta && !meta.isValid) return NO_ARRAY_TABLE_CELL_COMMIT;\n    normalizedValue =\n      typeof nextValue === \"number\"\n        ? nextValue\n        : nextValue === null && column.nullable && meta?.isEmpty !== false\n          ? null\n          : undefined;\n    if (normalizedValue === undefined) return NO_ARRAY_TABLE_CELL_COMMIT;\n  } else if (column.kind === \"boolean\") {\n    normalizedValue = Boolean(nextValue);\n  } else {\n    const currentText = currentValue == null ? \"\" : String(currentValue);\n    const currentDisplay =\n      column.schema.format === \"date-time\"\n        ? datetimeLocalInputValue(currentText)\n        : currentText;\n    const nextText = typeof nextValue === \"string\" ? nextValue : \"\";\n    const nextDisplay =\n      column.schema.format === \"date-time\"\n        ? datetimeLocalInputValue(nextText)\n        : nextText;\n\n    if (nextDisplay === currentDisplay) return NO_ARRAY_TABLE_CELL_COMMIT;\n    normalizedValue =\n      nextDisplay === \"\" && column.nullable ? null : nextDisplay;\n  }\n\n  return Object.is(currentValue, normalizedValue)\n    ? NO_ARRAY_TABLE_CELL_COMMIT\n    : normalizedValue;\n}\n\nexport function commitArrayTableCellValue({\n  column,\n  currentValue,\n  meta,\n  nextValue,\n  path,\n  setValue,\n}: {\n    column: Column;\n    currentValue: unknown;\n    meta?: DataCellValueMeta;\n    nextValue: unknown;\n    path: string;\n    setValue: SetArrayTableCellValue;\n}) {\n  const normalizedValue = normalizeArrayTableCellValue({\n    column,\n    currentValue,\n    nextValue,\n    meta,\n  });\n\n  if (normalizedValue === NO_ARRAY_TABLE_CELL_COMMIT) return;\n  setValue(path, normalizedValue, {\n    shouldDirty: true,\n    shouldTouch: true,\n    shouldValidate: true,\n  });\n}\n",
      "type": "registry:component",
      "target": "@components/json-form/table/array-table-cell-commit.ts"
    },
    {
      "path": "components/json-form/table/array-table-cell-model.ts",
      "content": "import { labelFor, type Column } from \"@/components/json-form/schema-model\";\nimport {\n  dataCellKindForColumn,\n  formatArrayTableCellValue,\n  type ArrayTableDataCellKind,\n} from \"@/components/json-form/table/array-table-format\";\n\nexport type ArrayTableCellModel = {\n  path: string;\n  sourcePath: string;\n  label: string;\n  displayText: string;\n  kind: ArrayTableDataCellKind;\n  value: unknown;\n  isEnum: boolean;\n  sourceLinked: boolean;\n};\n\nexport function createArrayTableCellModel({\n  path,\n  sourcePath,\n  column,\n  value,\n  sourceLinked,\n}: {\n  path: string;\n  sourcePath: string;\n  column: Column;\n  value: unknown;\n  sourceLinked: boolean;\n}): ArrayTableCellModel {\n  const isEnum = column.kind === \"enum\";\n\n  return {\n    path,\n    sourcePath,\n    label: labelFor(column.key, column.schema),\n    displayText: formatArrayTableCellValue({ value, column }),\n    kind: dataCellKindForColumn(column),\n    value,\n    isEnum,\n    sourceLinked,\n  };\n}\n",
      "type": "registry:component",
      "target": "@components/json-form/table/array-table-cell-model.ts"
    },
    {
      "path": "components/json-form/table/array-table-cell-props.ts",
      "content": "import { cn } from \"@/lib/utils\";\nimport type { ArrayTableCellModel } from \"@/components/json-form/table/array-table-cell-model\";\n\nexport function arrayTableCellClassName({\n  isEditing,\n  model,\n}: {\n  isEditing: boolean;\n  model: ArrayTableCellModel;\n}): string {\n  return cn(\n    \"min-w-0 rounded text-sm data-[source-active=true]:bg-primary/5 data-[source-active=true]:ring-1 data-[source-active=true]:ring-primary/30\",\n    !isEditing\n      ? \"hover:bg-background focus-visible:bg-background focus-visible:ring-1 focus-visible:ring-ring/30\"\n      : \"px-1 py-0.5\",\n    model.sourceLinked && isEditing && \"hover:bg-muted/55\",\n  );\n}\n\nexport function arrayTableCellProps(\n  model: ArrayTableCellModel,\n  { isEditing = false }: { isEditing?: boolean } = {},\n) {\n  return {\n    \"data-slot\": \"data-cell\",\n    \"data-table-cell\": \"\",\n    \"data-source-path\": model.sourceLinked ? model.sourcePath : undefined,\n    className: arrayTableCellClassName({ isEditing, model }),\n  };\n}\n",
      "type": "registry:component",
      "target": "@components/json-form/table/array-table-cell-props.ts"
    },
    {
      "path": "components/json-form/table/array-table-cell.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { DataCell } from \"@/components/ui/data-cell\";\nimport type { Column } from \"@/components/json-form/schema-model\";\nimport {\n  useArrayTableCellActive,\n  type ArrayTableActiveCellStore,\n} from \"@/components/json-form/table/array-table-active-cell-store\";\nimport {\n  commitArrayTableCellValue,\n  type CommitArrayTableCellValue,\n  type SetArrayTableCellValue,\n} from \"@/components/json-form/table/array-table-cell-commit\";\nimport { createArrayTableDataCellProps } from \"@/components/json-form/table/array-table-data-cell-props\";\nimport type { ArrayTableCellModel } from \"@/components/json-form/table/array-table-cell-model\";\n\nfunction ArrayTableCellContent({\n  model,\n  column,\n  activeCellStore,\n  setValue,\n  closeEditor,\n}: {\n  model: ArrayTableCellModel;\n  column: Column;\n  activeCellStore: ArrayTableActiveCellStore;\n  setValue: SetArrayTableCellValue;\n  closeEditor: () => void;\n}) {\n  const isEditing = useArrayTableCellActive(activeCellStore, model.path);\n  const commitValue = React.useCallback<CommitArrayTableCellValue>(\n    (nextValue, meta) => {\n      commitArrayTableCellValue({\n        column,\n        currentValue: model.value,\n        meta,\n        nextValue,\n        path: model.path,\n        setValue,\n      });\n    },\n    [column, model.path, model.value, setValue],\n  );\n\n  return (\n    <DataCell\n      {...createArrayTableDataCellProps({\n        column,\n        commitValue,\n        isEditing,\n        model,\n        onEditingEnd: closeEditor,\n      })}\n    />\n  );\n}\n\nexport const ArrayTableCell = React.memo(\n  ArrayTableCellContent,\n  (previous, next) =>\n    previous.model.path === next.model.path &&\n    previous.model.sourcePath === next.model.sourcePath &&\n    previous.model.label === next.model.label &&\n    previous.model.displayText === next.model.displayText &&\n    previous.model.kind === next.model.kind &&\n    Object.is(previous.model.value, next.model.value) &&\n    previous.model.isEnum === next.model.isEnum &&\n    previous.model.sourceLinked === next.model.sourceLinked &&\n    previous.column === next.column &&\n    previous.activeCellStore === next.activeCellStore &&\n    previous.setValue === next.setValue &&\n    previous.closeEditor === next.closeEditor,\n);\nArrayTableCell.displayName = \"ArrayTableCell\";\n",
      "type": "registry:component",
      "target": "@components/json-form/table/array-table-cell.tsx"
    },
    {
      "path": "components/json-form/table/array-table-config.ts",
      "content": "export const TABLE_MAX_HEIGHT = 420;\nexport const TABLE_ROW_HEIGHT = 44;\nexport const TABLE_SCROLL_THRESHOLD = Math.floor(\n  TABLE_MAX_HEIGHT / TABLE_ROW_HEIGHT,\n);\nexport const TABLE_VIRTUALIZE_THRESHOLD = TABLE_SCROLL_THRESHOLD * 3;\nexport const TABLE_ROW_OVERSCAN = 3;\nexport const TABLE_JUMP_ROW_OVERSCAN = 6;\n",
      "type": "registry:component",
      "target": "@components/json-form/table/array-table-config.ts"
    },
    {
      "path": "components/json-form/table/array-table-format.ts",
      "content": "import {\n  enumLabel,\n  enumValueEquals,\n} from \"@/components/json-form/scalar-control\";\nimport type { Column } from \"@/components/json-form/schema-model\";\n\nexport type ArrayTableDataCellKind =\n  | \"text\"\n  | \"number\"\n  | \"integer\"\n  | \"boolean\"\n  | \"select\"\n  | \"date\"\n  | \"time\"\n  | \"date-time\";\n\nexport function formatArrayTableCellValue({\n  value,\n  column,\n}: {\n  value: unknown;\n  column: Column;\n}) {\n  if (value == null || value === \"\") return \"—\";\n  if (column.kind === \"enum\") {\n    const option = column.schema.enum?.find((candidate) =>\n      enumValueEquals(candidate, value),\n    );\n    return option === undefined ? enumLabel(value) : enumLabel(option);\n  }\n  if (typeof value === \"number\")\n    return Number.isFinite(value) ? String(value) : \"—\";\n  if (typeof value === \"boolean\") return value ? \"True\" : \"False\";\n  return String(value);\n}\n\nexport function dataCellKindForColumn(column: Column): ArrayTableDataCellKind {\n  if (column.kind === \"enum\") return \"select\";\n  if (column.kind === \"number\" || column.kind === \"integer\") return column.kind;\n  if (column.kind === \"boolean\") return \"boolean\";\n  if (column.schema.format === \"date-time\") return \"date-time\";\n  if (column.schema.format === \"date\") return \"date\";\n  if (column.schema.format === \"time\") return \"time\";\n  return \"text\";\n}\n",
      "type": "registry:component",
      "target": "@components/json-form/table/array-table-format.ts"
    },
    {
      "path": "components/json-form/table/array-table-row.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { X } from \"lucide-react\";\nimport { useFormContext, useWatch } from \"react-hook-form\";\n\nimport { cn } from \"@/lib/utils\";\nimport { getFixedGridRowStyle } from \"@/components/ui/fixed-grid-row-style\";\nimport {\n  encodeJsonFormKey,\n  joinJsonFormPath,\n  joinJsonSourcePath,\n} from \"@/components/json-form/path-codec\";\nimport type { Column } from \"@/components/json-form/schema-model\";\nimport type { ArrayTableActiveCellStore } from \"@/components/json-form/table/array-table-active-cell-store\";\nimport { ArrayTableCell } from \"@/components/json-form/table/array-table-cell\";\nimport { createArrayTableCellModel } from \"@/components/json-form/table/array-table-cell-model\";\nimport { TABLE_ROW_HEIGHT } from \"@/components/json-form/table/array-table-config\";\n\nexport const ArrayTableRow = React.memo(function ArrayTableRow({\n  name,\n  sourcePath,\n  index,\n  isLastRow,\n  columns,\n  remove,\n  canRemove,\n  sourceLinked,\n  template,\n  rowTopPx,\n  activeCellStore,\n}: {\n  name: string;\n  sourcePath: string;\n  index: number;\n  isLastRow: boolean;\n  columns: Column[];\n  remove: (index: number) => void;\n  canRemove: boolean;\n  sourceLinked: boolean;\n  template: string;\n  rowTopPx?: number;\n  activeCellStore: ArrayTableActiveCellStore;\n}) {\n  const { control, getValues, setValue } = useFormContext();\n  const rowPath = joinJsonFormPath(name, index);\n  const rowSourcePath = joinJsonSourcePath(sourcePath, index);\n  const watchedRowValue = useWatch({\n    control,\n    name: rowPath,\n  }) as Record<string, unknown> | undefined;\n  const rowValue = (watchedRowValue ?? getValues(rowPath)) as\n    | Record<string, unknown>\n    | undefined;\n  const rowStyle = React.useMemo(\n    () =>\n      rowTopPx === undefined\n        ? { gridTemplateColumns: template }\n        : getFixedGridRowStyle({\n            gridTemplate: template,\n            rowHeight: TABLE_ROW_HEIGHT,\n            top: rowTopPx,\n          }),\n    [rowTopPx, template],\n  );\n  const closeEditor = React.useCallback(\n    () => activeCellStore.setActivePath(null),\n    [activeCellStore],\n  );\n\n  return (\n    <div\n      data-index={index}\n      className={cn(\n        \"hover:bg-muted/25 grid items-center gap-1 border-b px-2 py-1 [contain:layout_paint_style]\",\n        isLastRow && \"border-b-0\",\n      )}\n      style={rowStyle}\n    >\n      {columns.map((column) => {\n        const path = joinJsonFormPath(rowPath, column.key);\n        const value = rowValue?.[encodeJsonFormKey(column.key)];\n\n        return (\n          <ArrayTableCell\n            key={column.key}\n            model={createArrayTableCellModel({\n              path,\n              sourcePath: joinJsonSourcePath(rowSourcePath, column.key),\n              column,\n              value,\n              sourceLinked,\n            })}\n            column={column}\n            activeCellStore={activeCellStore}\n            setValue={setValue}\n            closeEditor={closeEditor}\n          />\n        );\n      })}\n      <button\n        type=\"button\"\n        className=\"text-muted-foreground hover:border-border hover:text-destructive focus-visible:ring-ring flex size-8 items-center justify-center rounded-md border border-transparent text-base leading-none transition-colors focus-visible:ring-2 focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50\"\n        onClick={() => remove(index)}\n        aria-label=\"Remove row\"\n        disabled={!canRemove}\n      >\n        <X className=\"size-4\" />\n      </button>\n    </div>\n  );\n});\n",
      "type": "registry:component",
      "target": "@components/json-form/table/array-table-row.tsx"
    },
    {
      "path": "components/json-form/table/array-table-scroll.ts",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { useMountEffect } from \"@/hooks/use-mount-effect\";\n\nexport type ArrayTableScrollHandlers = {\n  onScrollStart: () => void;\n  onScrollMove: () => void;\n  onScrollEnd: () => void;\n};\n\nexport function useArrayTableScrollActivity(\n  scrollRef: React.RefObject<HTMLElement | null>,\n  { onScrollStart, onScrollMove, onScrollEnd }: ArrayTableScrollHandlers,\n) {\n  const isScrollingRef = React.useRef(false);\n  const scrollEndTimeoutRef = React.useRef(0);\n  const callbacksRef = React.useRef({\n    onScrollStart,\n    onScrollMove,\n    onScrollEnd,\n  });\n  callbacksRef.current = { onScrollStart, onScrollMove, onScrollEnd };\n\n  const handleScroll = React.useCallback(() => {\n    if (!isScrollingRef.current) {\n      isScrollingRef.current = true;\n      callbacksRef.current.onScrollStart();\n    }\n    callbacksRef.current.onScrollMove();\n    window.clearTimeout(scrollEndTimeoutRef.current);\n    scrollEndTimeoutRef.current = window.setTimeout(() => {\n      isScrollingRef.current = false;\n      callbacksRef.current.onScrollEnd();\n    }, 120);\n  }, []);\n\n  useMountEffect(() => {\n    const scrollElement = scrollRef.current;\n    if (!scrollElement) return;\n    scrollElement.addEventListener(\"scroll\", handleScroll, { passive: true });\n    return () => {\n      window.clearTimeout(scrollEndTimeoutRef.current);\n      scrollElement.removeEventListener(\"scroll\", handleScroll);\n    };\n  });\n}\n",
      "type": "registry:component",
      "target": "@components/json-form/table/array-table-scroll.ts"
    },
    {
      "path": "components/json-form/table/array-table.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { getFixedGridCanvasStyle } from \"@/components/ui/fixed-grid-layout\";\nimport { WithDescription } from \"@/components/json-form/disclosure\";\nimport { labelFor, type Column } from \"@/components/json-form/schema-model\";\nimport { useSourceLinkedTableCells } from \"@/components/json-form/source-link\";\nimport {\n  createArrayTableActiveCellStore,\n  type ArrayTableActiveCellStore,\n} from \"@/components/json-form/table/array-table-active-cell-store\";\nimport {\n  FixedArrayTableBody,\n  StaticArrayTableBody,\n} from \"@/components/json-form/table/array-table-body\";\nimport {\n  TABLE_SCROLL_THRESHOLD,\n  TABLE_VIRTUALIZE_THRESHOLD,\n} from \"@/components/json-form/table/array-table-config\";\nimport { ArrayTableRow } from \"@/components/json-form/table/array-table-row\";\n\nexport function ArrayTable({\n  name,\n  sourcePath,\n  fields,\n  remove,\n  canRemove,\n  columns,\n}: {\n  name: string;\n  sourcePath: string;\n  fields: { id: string }[];\n  remove: (index: number) => void;\n  canRemove: boolean;\n  columns: Column[];\n}) {\n  const template = `${columns.map(() => \"minmax(9rem, 1fr)\").join(\" \")} 2.25rem`;\n  const minWidth = columns.length * 150 + 36;\n  const activeCellStoreRef = React.useRef<ArrayTableActiveCellStore | null>(null);\n  if (!activeCellStoreRef.current) {\n    activeCellStoreRef.current = createArrayTableActiveCellStore();\n  }\n  const activeCellStore = activeCellStoreRef.current;\n  const tableRef = React.useRef<HTMLDivElement>(null);\n  const sourceTable = useSourceLinkedTableCells({\n    tableRef,\n    refreshKey: fields.length,\n  });\n  const sourceLinked = sourceTable.sourceLinked;\n  const virtualize = fields.length > TABLE_VIRTUALIZE_THRESHOLD;\n  const scrollHandlers = React.useMemo(\n    () => ({\n      onScrollStart: sourceTable.handleScrollStart,\n      onScrollMove: sourceTable.handleScrollMove,\n      onScrollEnd: sourceTable.handleScrollEnd,\n    }),\n    [sourceTable],\n  );\n\n  const handleTableClickCapture = React.useCallback(\n    (event: React.MouseEvent<HTMLDivElement>) => {\n      const table = tableRef.current;\n      const activeElement = table?.ownerDocument.activeElement;\n      if (\n        !(activeElement instanceof HTMLElement) ||\n        activeElement.dataset.tableCellEditor !== \"true\" ||\n        !table?.contains(activeElement) ||\n        activeElement === event.target ||\n        activeElement.contains(event.target as Node)\n      ) {\n        return;\n      }\n      activeElement.blur();\n    },\n    [],\n  );\n\n  const handleTableClick = React.useCallback(\n    (event: React.MouseEvent<HTMLDivElement>) => {\n      const cell = sourceTable.getCellFromTarget(event.target);\n      if (!cell) return;\n      sourceTable.selectCellSource(cell);\n      if (cell.dataset.tableCellEditable !== \"true\") return;\n      const path = cell.dataset.tableCellPath;\n      if (path) activeCellStore.setActivePath(path);\n    },\n    [activeCellStore, sourceTable],\n  );\n\n  const handleTableKeyDown = React.useCallback(\n    (event: React.KeyboardEvent<HTMLDivElement>) => {\n      if (event.key !== \"Enter\" && event.key !== \" \") return;\n      const cell = sourceTable.getCellFromTarget(event.target);\n      if (!cell || cell.dataset.tableCellEditable !== \"true\") return;\n      const path = cell.dataset.tableCellPath;\n      if (!path) return;\n      sourceTable.selectCellSource(cell);\n      event.preventDefault();\n      activeCellStore.setActivePath(path);\n    },\n    [activeCellStore, sourceTable],\n  );\n\n  const renderRow = React.useCallback(\n    (index: number, rowTopPx?: number) => (\n      <ArrayTableRow\n        name={name}\n        sourcePath={sourcePath}\n        index={index}\n        isLastRow={index === fields.length - 1}\n        columns={columns}\n        remove={remove}\n        canRemove={canRemove}\n        sourceLinked={sourceLinked}\n        template={template}\n        rowTopPx={rowTopPx}\n        activeCellStore={activeCellStore}\n      />\n    ),\n    [\n      activeCellStore,\n      name,\n      sourcePath,\n      fields.length,\n      columns,\n      remove,\n      canRemove,\n      sourceLinked,\n      template,\n    ],\n  );\n\n  return (\n    <div\n      ref={tableRef}\n      onClickCapture={handleTableClickCapture}\n      onClick={handleTableClick}\n      onKeyDown={handleTableKeyDown}\n      onPointerMove={sourceLinked ? sourceTable.handlePointerMove : undefined}\n      onPointerLeave={sourceLinked ? sourceTable.handlePointerLeave : undefined}\n      onFocus={sourceTable.handleFocus}\n      onBlur={sourceTable.handleBlur}\n      className=\"bg-background overflow-x-auto\"\n    >\n      <div style={getFixedGridCanvasStyle({ minWidth })}>\n        <div\n          className=\"bg-muted/35 grid h-9 items-center gap-1 border-b px-2\"\n          style={{ gridTemplateColumns: template }}\n        >\n          {columns.map((column) => (\n            <div\n              key={column.key}\n              className=\"text-muted-foreground flex min-w-0 items-center gap-1 px-2 text-xs font-medium\"\n            >\n              <WithDescription text={column.schema.description}>\n                <span className=\"truncate\">\n                  {labelFor(column.key, column.schema)}\n                </span>\n              </WithDescription>\n              {column.required ? (\n                <span className=\"text-destructive\">*</span>\n              ) : null}\n            </div>\n          ))}\n          <span className=\"sr-only\">Actions</span>\n        </div>\n        {virtualize ? (\n          <FixedArrayTableBody\n            fields={fields}\n            scrollHandlers={scrollHandlers}\n            renderItem={renderRow}\n          />\n        ) : fields.length > TABLE_SCROLL_THRESHOLD ? (\n          <StaticArrayTableBody\n            fields={fields}\n            scrollHandlers={scrollHandlers}\n            renderItem={renderRow}\n          />\n        ) : (\n          <div>\n            {fields.map((entry, index) => (\n              <React.Fragment key={entry.id}>{renderRow(index)}</React.Fragment>\n            ))}\n          </div>\n        )}\n      </div>\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/json-form/table/array-table.tsx"
    },
    {
      "path": "components/json-form/virtual-list.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { FixedGridRowWindow } from \"@/components/ui/fixed-grid-row-window\";\nimport { useMeasuredRowVirtualization } from \"@/components/ui/measured-row-virtualization\";\n\nexport function VirtualList({\n  fields,\n  estimateSize,\n  renderItem,\n  maxHeight = 480,\n  gap = 0,\n}: {\n  fields: { id: string }[];\n  estimateSize: number;\n  renderItem: (index: number) => React.ReactNode;\n  maxHeight?: number;\n  gap?: number;\n}) {\n  const parentRef = React.useRef<HTMLDivElement>(null);\n  const getItemKey = React.useCallback(\n    (index: number) => fields[index]?.id ?? index,\n    [fields],\n  );\n  const { measureRow, totalSize, viewportClientHeight, virtualRowWindow } =\n    useMeasuredRowVirtualization({\n      count: fields.length,\n      estimateSize: estimateSize + gap,\n      getItemKey,\n      overscan: 8,\n      scrollRef: parentRef,\n    });\n\n  return (\n    <div ref={parentRef} style={{ maxHeight }} className=\"overflow-y-auto\">\n      <FixedGridRowWindow\n        data-slot=\"json-form-virtual-list-spacer\"\n        totalSize={totalSize}\n        virtualRowWindow={virtualRowWindow}\n        viewportHeight={viewportClientHeight}\n        offsetDataSlot=\"json-form-virtual-list-row-offset\"\n        windowDataSlot=\"json-form-virtual-list-row-window\"\n      >\n        {virtualRowWindow.items.map((virtualRow) => (\n          <div\n            key={virtualRow.key}\n            data-index={virtualRow.index}\n            ref={(element) => measureRow(virtualRow.index, element)}\n            style={{\n              position: \"absolute\",\n              top: 0,\n              left: 0,\n              width: \"100%\",\n              transform: `translateY(${virtualRow.start}px)`,\n              paddingBottom: gap,\n            }}\n          >\n            {renderItem(virtualRow.index)}\n          </div>\n        ))}\n      </FixedGridRowWindow>\n    </div>\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/json-form/virtual-list.tsx"
    },
    {
      "path": "registry/new-york-v4/ui/measured-row-virtualization.ts",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { useKeyedLayoutEffect } from \"@/hooks/use-keyed-layout-effect\";\nimport { joinEffectKey } from \"@/lib/effect-key\";\n\nconst DEFAULT_INITIAL_VIEWPORT_HEIGHT = 600;\nconst DEFAULT_OVERSCAN = 6;\nconst MAX_VIRTUAL_ITEMS = 1_000;\n\nexport interface MeasuredRowVirtualItem {\n  index: number;\n  key: React.Key;\n  start: number;\n  size: number;\n  end: number;\n}\n\nexport interface MeasuredRowVirtualItemWindow {\n  end: number;\n  items: MeasuredRowVirtualItem[];\n  size: number;\n  start: number;\n}\n\nexport interface MeasuredRowOffsets {\n  starts: number[];\n  totalSize: number;\n}\n\nexport interface MeasuredRowScrollTarget {\n  align?: \"start\" | \"center\" | \"end\";\n  behavior?: ScrollBehavior;\n}\n\nexport function buildMeasuredRowOffsets({\n  rowSizes,\n  paddingStart = 0,\n  paddingEnd = 0,\n}: {\n  rowSizes: readonly number[];\n  paddingStart?: number;\n  paddingEnd?: number;\n}): MeasuredRowOffsets {\n  const starts: number[] = [];\n  let offset = safeSize(paddingStart);\n\n  for (const rowSize of rowSizes) {\n    starts.push(offset);\n    offset += safeSize(rowSize);\n  }\n\n  return {\n    starts,\n    totalSize: offset + safeSize(paddingEnd),\n  };\n}\n\nexport function getMeasuredRowVirtualItems({\n  getItemKey,\n  maxItems = MAX_VIRTUAL_ITEMS,\n  offsets,\n  overscan = DEFAULT_OVERSCAN,\n  rowSizes,\n  scrollTop,\n  viewportHeight,\n}: {\n  getItemKey?: (index: number) => React.Key;\n  maxItems?: number;\n  offsets: MeasuredRowOffsets;\n  overscan?: number;\n  rowSizes: readonly number[];\n  scrollTop: number;\n  viewportHeight: number;\n}): MeasuredRowVirtualItem[] {\n  const count = rowSizes.length;\n  if (count === 0) return [];\n\n  const safeScrollTop = safeOffset(scrollTop);\n  const safeViewportHeight = Math.max(1, safeSize(viewportHeight));\n  const visibleStartIndex = findFirstRowEndingAfter({\n    offset: safeScrollTop,\n    rowSizes,\n    starts: offsets.starts,\n  });\n  const visibleEndExclusive = findFirstRowStartingAtOrAfter({\n    offset: safeScrollTop + safeViewportHeight,\n    starts: offsets.starts,\n  });\n  const start = Math.max(0, visibleStartIndex - safeCount(overscan));\n  const end = Math.min(\n    count,\n    Math.max(visibleStartIndex + 1, visibleEndExclusive) + safeCount(overscan),\n  );\n  const [cappedStart, cappedEnd] = capMeasuredRowRange({\n    count,\n    end,\n    maxItems,\n    start,\n    visibleEnd: Math.min(\n      count,\n      Math.max(visibleStartIndex + 1, visibleEndExclusive),\n    ),\n    visibleStart: visibleStartIndex,\n  });\n\n  return Array.from({ length: cappedEnd - cappedStart }, (_, offset) => {\n    const index = cappedStart + offset;\n    const rowStart = offsets.starts[index] ?? 0;\n    const size = safeSize(rowSizes[index]);\n    return {\n      index,\n      key: getItemKey?.(index) ?? index,\n      start: rowStart,\n      size,\n      end: rowStart + size,\n    };\n  });\n}\n\nexport function measuredRowVirtualItemWindow(\n  items: readonly MeasuredRowVirtualItem[],\n): MeasuredRowVirtualItemWindow {\n  const start = items[0]?.start ?? 0;\n  const end = items.length ? items[items.length - 1]!.end : start;\n\n  return {\n    end,\n    items: items.map((item) => ({\n      ...item,\n      start: item.start - start,\n      end: item.end - start,\n    })),\n    size: Math.max(0, end - start),\n    start,\n  };\n}\n\nexport function measuredRowScrollTopForIndex({\n  align = \"center\",\n  index,\n  offsets,\n  rowSizes,\n  viewportHeight,\n}: {\n  align?: NonNullable<MeasuredRowScrollTarget[\"align\"]>;\n  index: number;\n  offsets: MeasuredRowOffsets;\n  rowSizes: readonly number[];\n  viewportHeight: number;\n}) {\n  if (!Number.isSafeInteger(index) || index < 0) return 0;\n\n  const rowStart = offsets.starts[index];\n  if (rowStart == null) return 0;\n\n  const rowSize = safeSize(rowSizes[index]);\n  const safeViewportHeight = safeSize(viewportHeight);\n\n  if (align === \"end\")\n    return Math.max(0, rowStart - safeViewportHeight + rowSize);\n  if (align === \"center\") {\n    return Math.max(0, rowStart - safeViewportHeight / 2 + rowSize / 2);\n  }\n  return Math.max(0, rowStart);\n}\n\nexport function useMeasuredRowVirtualization({\n  count,\n  estimateSize,\n  getItemKey,\n  initialViewportHeight = DEFAULT_INITIAL_VIEWPORT_HEIGHT,\n  overscan = DEFAULT_OVERSCAN,\n  paddingEnd = 0,\n  paddingStart = 0,\n  scrollRef,\n}: {\n  count: number;\n  estimateSize: number;\n  getItemKey?: (index: number) => React.Key;\n  initialViewportHeight?: number;\n  overscan?: number;\n  paddingEnd?: number;\n  paddingStart?: number;\n  scrollRef: React.RefObject<HTMLElement | null>;\n}) {\n  const safeCount = measuredRowCount(count);\n  const safeEstimateSize = Math.max(1, safeSize(estimateSize));\n  const [version, forceVersion] = React.useReducer(\n    (current: number) => current + 1,\n    0,\n  );\n  const [scrollElement, setScrollElement] = React.useState<HTMLElement | null>(\n    scrollRef.current,\n  );\n  const [viewport, setViewport] = React.useState({\n    clientHeight: 0,\n    scrollTop: 0,\n  });\n  const measuredSizesRef = React.useRef(new Map<number, number>());\n  const rowElementsRef = React.useRef(new Map<number, HTMLElement>());\n  const observedIndexesRef = React.useRef(new Map<HTMLElement, number>());\n  const rowObserverRef = React.useRef<ResizeObserver | null>(null);\n  const offsetsRef = React.useRef<MeasuredRowOffsets>({\n    starts: [],\n    totalSize: 0,\n  });\n  const rowSizesRef = React.useRef<number[]>([]);\n  const viewportRef = React.useRef(viewport);\n\n  viewportRef.current = viewport;\n\n  const updateMeasuredSize = React.useCallback(\n    (index: number, nextSize: number | null) => {\n      if (!Number.isSafeInteger(index) || index < 0 || index >= safeCount) {\n        return;\n      }\n      if (nextSize == null) return;\n\n      const safeNextSize = safeSize(nextSize);\n      if (safeNextSize <= 0) return;\n\n      const measuredSizes = measuredSizesRef.current;\n      const previousSize = measuredSizes.get(index) ?? safeEstimateSize;\n      if (previousSize === safeNextSize) return;\n\n      const previousOffsets = offsetsRef.current;\n      const previousStart = previousOffsets.starts[index] ?? 0;\n      const previousEnd = previousStart + previousSize;\n      const delta = safeNextSize - previousSize;\n      const currentScrollTop = viewportRef.current.scrollTop;\n\n      measuredSizes.set(index, safeNextSize);\n\n      const currentScrollElement = scrollRef.current;\n      if (\n        currentScrollElement &&\n        previousEnd <= currentScrollTop &&\n        delta !== 0\n      ) {\n        currentScrollElement.scrollTop = Math.max(\n          0,\n          currentScrollElement.scrollTop + delta,\n        );\n        setViewport((current) => ({\n          ...current,\n          scrollTop: safeOffset(currentScrollElement.scrollTop),\n        }));\n      }\n\n      forceVersion();\n    },\n    [safeCount, safeEstimateSize, scrollRef],\n  );\n\n  const measureRow = React.useCallback(\n    (index: number, element: HTMLElement | null) => {\n      if (!Number.isSafeInteger(index) || index < 0) return;\n\n      const rowElements = rowElementsRef.current;\n      const previousElement = rowElements.get(index);\n      if (previousElement && previousElement !== element) {\n        rowObserverRef.current?.unobserve(previousElement);\n        observedIndexesRef.current.delete(previousElement);\n        rowElements.delete(index);\n      }\n\n      if (!element) return;\n\n      rowElements.set(index, element);\n      observedIndexesRef.current.set(element, index);\n      rowObserverRef.current?.observe(element);\n      updateMeasuredSize(index, readElementHeight(element));\n    },\n    [updateMeasuredSize],\n  );\n\n  useKeyedLayoutEffect(\n    joinEffectKey([scrollRef, scrollRef.current, scrollElement]),\n    () => {\n      const nextScrollElement = scrollRef.current;\n      if (nextScrollElement !== scrollElement) {\n        setScrollElement(nextScrollElement);\n      }\n    },\n  );\n\n  useKeyedLayoutEffect(\n    joinEffectKey([scrollElement, updateMeasuredSize]),\n    () => {\n      if (typeof ResizeObserver === \"undefined\") return;\n\n      const observer = new ResizeObserver((entries) => {\n        for (const entry of entries) {\n          const index = observedIndexesRef.current.get(\n            entry.target as HTMLElement,\n          );\n          if (index == null) continue;\n          updateMeasuredSize(index, readResizeEntryHeight(entry));\n        }\n      });\n\n      rowObserverRef.current = observer;\n      for (const element of rowElementsRef.current.values()) {\n        observer.observe(element);\n      }\n\n      return () => {\n        observer.disconnect();\n        if (rowObserverRef.current === observer) rowObserverRef.current = null;\n      };\n    },\n  );\n\n  useKeyedLayoutEffect(\n    joinEffectKey([scrollElement, initialViewportHeight]),\n    () => {\n      if (!scrollElement) {\n        setViewport({ clientHeight: 0, scrollTop: 0 });\n        return;\n      }\n\n      let frame = 0;\n      const readViewport = () => {\n        frame = 0;\n        const next = {\n          clientHeight: safeSize(scrollElement.clientHeight),\n          scrollTop: safeOffset(scrollElement.scrollTop),\n        };\n        setViewport((current) =>\n          current.clientHeight === next.clientHeight &&\n          current.scrollTop === next.scrollTop\n            ? current\n            : next,\n        );\n      };\n      const scheduleRead = () => {\n        if (frame) return;\n        frame = requestFrame(readViewport);\n      };\n\n      readViewport();\n      scrollElement.addEventListener(\"scroll\", scheduleRead, { passive: true });\n      const observer =\n        typeof ResizeObserver !== \"undefined\"\n          ? new ResizeObserver(scheduleRead)\n          : null;\n      observer?.observe(scrollElement);\n\n      return () => {\n        if (frame) cancelFrame(frame);\n        scrollElement.removeEventListener(\"scroll\", scheduleRead);\n        observer?.disconnect();\n      };\n    },\n  );\n\n  const rowSizes = React.useMemo(\n    () => {\n      void version;\n      return Array.from(\n        { length: safeCount },\n        (_, index) => measuredSizesRef.current.get(index) ?? safeEstimateSize,\n      );\n    },\n    [safeCount, safeEstimateSize, version],\n  );\n  const offsets = React.useMemo(\n    () =>\n      buildMeasuredRowOffsets({\n        paddingEnd,\n        paddingStart,\n        rowSizes,\n      }),\n    [paddingEnd, paddingStart, rowSizes],\n  );\n  const viewportHeight =\n    viewport.clientHeight ||\n    safeSize(initialViewportHeight) ||\n    safeEstimateSize;\n  const virtualRows = React.useMemo(\n    () =>\n      getMeasuredRowVirtualItems({\n        getItemKey,\n        offsets,\n        overscan,\n        rowSizes,\n        scrollTop: viewport.scrollTop,\n        viewportHeight,\n      }),\n    [\n      getItemKey,\n      offsets,\n      overscan,\n      rowSizes,\n      viewport.scrollTop,\n      viewportHeight,\n    ],\n  );\n  const virtualRowWindow = React.useMemo(\n    () => measuredRowVirtualItemWindow(virtualRows),\n    [virtualRows],\n  );\n  const scrollToIndex = React.useCallback(\n    (index: number, options?: MeasuredRowScrollTarget) => {\n      const currentScrollElement = scrollRef.current;\n      if (!currentScrollElement) return;\n\n      const top = measuredRowScrollTopForIndex({\n        align: options?.align ?? \"center\",\n        index,\n        offsets: offsetsRef.current,\n        rowSizes: rowSizesRef.current,\n        viewportHeight: currentScrollElement.clientHeight || viewportHeight,\n      });\n      if (typeof currentScrollElement.scrollTo === \"function\") {\n        currentScrollElement.scrollTo({\n          behavior: options?.behavior ?? \"smooth\",\n          top,\n        });\n      } else {\n        currentScrollElement.scrollTop = top;\n      }\n      setViewport((current) => ({\n        ...current,\n        scrollTop: safeOffset(currentScrollElement.scrollTop),\n      }));\n    },\n    [scrollRef, viewportHeight],\n  );\n\n  offsetsRef.current = offsets;\n  rowSizesRef.current = rowSizes;\n\n  return {\n    measureRow,\n    scrollToIndex,\n    totalSize: offsets.totalSize,\n    viewportClientHeight: viewportHeight,\n    virtualRowWindow,\n    virtualRows,\n  };\n}\n\nfunction capMeasuredRowRange({\n  count,\n  end,\n  maxItems,\n  start,\n  visibleEnd,\n  visibleStart,\n}: {\n  count: number;\n  end: number;\n  maxItems: number;\n  start: number;\n  visibleEnd: number;\n  visibleStart: number;\n}) {\n  const safeMaxItems = Math.max(1, safeCount(maxItems));\n  if (end - start <= safeMaxItems) return [start, end] as const;\n\n  const visibleCount = Math.min(\n    safeMaxItems,\n    Math.max(1, visibleEnd - visibleStart),\n  );\n  const leadingBudget = Math.floor((safeMaxItems - visibleCount) / 2);\n  let cappedStart = Math.max(start, visibleStart - leadingBudget);\n  let cappedEnd = Math.min(end, cappedStart + safeMaxItems);\n\n  if (cappedEnd - cappedStart < safeMaxItems) {\n    cappedStart = Math.max(start, cappedEnd - safeMaxItems);\n  }\n  if (cappedEnd < visibleEnd) {\n    cappedEnd = Math.min(end, visibleEnd);\n    cappedStart = Math.max(start, cappedEnd - safeMaxItems);\n  }\n  cappedEnd = Math.min(count, cappedEnd);\n\n  return [cappedStart, cappedEnd] as const;\n}\n\nfunction findFirstRowEndingAfter({\n  offset,\n  rowSizes,\n  starts,\n}: {\n  offset: number;\n  rowSizes: readonly number[];\n  starts: readonly number[];\n}) {\n  let low = 0;\n  let high = rowSizes.length - 1;\n  let result = rowSizes.length - 1;\n\n  while (low <= high) {\n    const middle = Math.floor((low + high) / 2);\n    const end = (starts[middle] ?? 0) + safeSize(rowSizes[middle]);\n    if (end > offset) {\n      result = middle;\n      high = middle - 1;\n    } else {\n      low = middle + 1;\n    }\n  }\n\n  return result;\n}\n\nfunction findFirstRowStartingAtOrAfter({\n  offset,\n  starts,\n}: {\n  offset: number;\n  starts: readonly number[];\n}) {\n  let low = 0;\n  let high = starts.length - 1;\n  let result = starts.length;\n\n  while (low <= high) {\n    const middle = Math.floor((low + high) / 2);\n    if ((starts[middle] ?? 0) >= offset) {\n      result = middle;\n      high = middle - 1;\n    } else {\n      low = middle + 1;\n    }\n  }\n\n  return result;\n}\n\nfunction readElementHeight(element: HTMLElement) {\n  const rectHeight = element.getBoundingClientRect().height;\n  if (Number.isFinite(rectHeight) && rectHeight > 0) return rectHeight;\n  return element.offsetHeight;\n}\n\nfunction readResizeEntryHeight(entry: ResizeObserverEntry) {\n  const borderBoxSize = entry.borderBoxSize;\n  const boxSize = Array.isArray(borderBoxSize)\n    ? borderBoxSize[0]\n    : borderBoxSize;\n  if (boxSize && Number.isFinite(boxSize.blockSize) && boxSize.blockSize > 0) {\n    return boxSize.blockSize;\n  }\n  if (\n    Number.isFinite(entry.contentRect.height) &&\n    entry.contentRect.height > 0\n  ) {\n    return entry.contentRect.height;\n  }\n  return readElementHeight(entry.target as HTMLElement);\n}\n\nfunction requestFrame(callback: FrameRequestCallback) {\n  if (typeof window !== \"undefined\" && window.requestAnimationFrame) {\n    return window.requestAnimationFrame(callback);\n  }\n  return setTimeout(() => callback(performance.now()), 0) as unknown as number;\n}\n\nfunction cancelFrame(frame: number) {\n  if (typeof window !== \"undefined\" && window.cancelAnimationFrame) {\n    window.cancelAnimationFrame(frame);\n    return;\n  }\n  clearTimeout(frame);\n}\n\nfunction measuredRowCount(count: number) {\n  return Number.isFinite(count) && count > 0 ? Math.floor(count) : 0;\n}\n\nfunction safeCount(value: number) {\n  return Number.isFinite(value) && value > 0 ? Math.floor(value) : 0;\n}\n\nfunction safeOffset(value: number) {\n  return Number.isFinite(value) && value > 0 ? value : 0;\n}\n\nfunction safeSize(value: number | undefined) {\n  return Number.isFinite(value) && value != null && value > 0 ? value : 0;\n}\n",
      "type": "registry:ui",
      "target": "@ui/measured-row-virtualization.ts"
    },
    {
      "path": "components/json-form/table/array-table-active-cell-store.ts",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nexport type ArrayTableActiveCellStore = {\n  getSnapshot: () => string | null;\n  setActivePath: (path: string | null) => void;\n  subscribe: (listener: () => void) => () => void;\n};\n\nexport function createArrayTableActiveCellStore(): ArrayTableActiveCellStore {\n  let activePath: string | null = null;\n  const listeners = new Set<() => void>();\n\n  return {\n    getSnapshot: () => activePath,\n    setActivePath: (path) => {\n      if (activePath === path) return;\n      activePath = path;\n      listeners.forEach((listener) => listener());\n    },\n    subscribe: (listener) => {\n      listeners.add(listener);\n      return () => listeners.delete(listener);\n    },\n  };\n}\n\nexport function useArrayTableCellActive(\n  store: ArrayTableActiveCellStore,\n  path: string,\n): boolean {\n  return React.useSyncExternalStore(\n    store.subscribe,\n    () => store.getSnapshot() === path,\n    () => false,\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/json-form/table/array-table-active-cell-store.ts"
    },
    {
      "path": "components/json-form/table/array-table-data-cell-props.ts",
      "content": "import type {\n  DataCellProps,\n  DataCellSelectOption,\n} from \"@/components/ui/data-cell\";\nimport {\n  dataCellNumberValue,\n  dataCellTextValue,\n  enumLabel,\n  enumValueEquals,\n} from \"@/components/json-form/scalar-control\";\nimport { NULL_SELECT_VALUE } from \"@/components/json-form/scalar/enum-control\";\nimport type { Column } from \"@/components/json-form/schema-model\";\nimport type { CommitArrayTableCellValue } from \"@/components/json-form/table/array-table-cell-commit\";\nimport type { ArrayTableCellModel } from \"@/components/json-form/table/array-table-cell-model\";\nimport { arrayTableCellProps } from \"@/components/json-form/table/array-table-cell-props\";\n\ntype ArrayTableDataCellSharedProps = {\n  active: boolean;\n  autoFocus: boolean;\n  editable: boolean;\n  name: string;\n  onCommit: CommitArrayTableCellValue;\n  onEditingEnd: () => void;\n  role?: \"button\";\n  tabIndex: 0;\n  \"aria-label\": string;\n  \"data-table-cell-editable\"?: \"true\";\n  \"data-table-cell-editor\"?: \"true\";\n  \"data-table-cell-path\"?: string;\n};\n\nexport function createArrayTableDataCellProps({\n  column,\n  commitValue,\n  isEditing,\n  model,\n  onEditingEnd,\n}: {\n  column: Column;\n  commitValue: CommitArrayTableCellValue;\n  isEditing: boolean;\n  model: ArrayTableCellModel;\n  onEditingEnd: () => void;\n}): DataCellProps {\n  const sharedProps = arrayTableDataCellSharedProps({\n    commitValue,\n    isEditing,\n    model,\n    onEditingEnd,\n  });\n\n  if (column.kind === \"enum\") {\n    return arrayTableSelectDataCellProps({\n      column,\n      commitValue,\n      model,\n      sharedProps,\n    });\n  }\n\n  if (model.kind === \"number\" || model.kind === \"integer\") {\n    return {\n      ...arrayTableCellProps(model, { isEditing }),\n      ...sharedProps,\n      kind: model.kind,\n      value: dataCellNumberValue(model.value),\n      formatValue: () => model.displayText,\n      placeholder: \"\",\n    };\n  }\n\n  if (model.kind === \"boolean\") {\n    return {\n      ...arrayTableCellProps(model, { isEditing }),\n      ...sharedProps,\n      kind: \"boolean\",\n      value:\n        model.value === null || model.value === undefined\n          ? null\n          : Boolean(model.value),\n    };\n  }\n\n  if (model.kind === \"date\") {\n    return {\n      ...arrayTableCellProps(model, { isEditing }),\n      ...sharedProps,\n      kind: \"date\",\n      value: dataCellTextValue(model.value),\n      formatValue: () => model.displayText,\n      placeholder: \"\",\n    };\n  }\n\n  if (model.kind === \"time\") {\n    return {\n      ...arrayTableCellProps(model, { isEditing }),\n      ...sharedProps,\n      kind: \"time\",\n      value: dataCellTextValue(model.value),\n      formatValue: () => model.displayText,\n      placeholder: \"\",\n    };\n  }\n\n  if (model.kind === \"date-time\") {\n    return {\n      ...arrayTableCellProps(model, { isEditing }),\n      ...sharedProps,\n      kind: \"date-time\",\n      value: dataCellTextValue(model.value),\n      formatValue: () => model.displayText,\n      placeholder: \"\",\n    };\n  }\n\n  return {\n    ...arrayTableCellProps(model, { isEditing }),\n    ...sharedProps,\n    kind: \"text\",\n    value: dataCellTextValue(model.value),\n    formatValue: () => model.displayText,\n    placeholder: \"\",\n  };\n}\n\nfunction arrayTableDataCellSharedProps({\n  commitValue,\n  isEditing,\n  model,\n  onEditingEnd,\n}: {\n  commitValue: CommitArrayTableCellValue;\n  isEditing: boolean;\n  model: ArrayTableCellModel;\n  onEditingEnd: () => void;\n}): ArrayTableDataCellSharedProps {\n  return {\n    active: isEditing,\n    editable: isEditing,\n    autoFocus: isEditing,\n    name: model.path,\n    onCommit: commitValue,\n    onEditingEnd,\n    role: isEditing ? undefined : \"button\",\n    tabIndex: 0,\n    \"aria-label\": `${model.label} ${model.displayText}`,\n    \"data-table-cell-editable\": isEditing ? undefined : \"true\",\n    \"data-table-cell-editor\": isEditing ? \"true\" : undefined,\n    \"data-table-cell-path\": isEditing ? undefined : model.path,\n  };\n}\n\nfunction arrayTableSelectDataCellProps({\n  column,\n  commitValue,\n  model,\n  sharedProps,\n}: {\n  column: Column;\n  commitValue: CommitArrayTableCellValue;\n  model: ArrayTableCellModel;\n  sharedProps: ArrayTableDataCellSharedProps;\n}): DataCellProps {\n  return {\n    ...arrayTableCellProps(model, { isEditing: sharedProps.active }),\n    ...sharedProps,\n    kind: \"select\",\n    value: arrayTableSelectValue({\n      column,\n      value: model.value,\n    }),\n    selectOptions: arrayTableSelectOptions(column),\n    placeholder: \"Select...\",\n    formatValue: () => model.displayText,\n    onCommit: (value, meta) => {\n      commitValue(\n        arrayTableSelectCommitValue({\n          column,\n          value,\n        }),\n        meta,\n      );\n    },\n  };\n}\n\nfunction enumOptionValue(index: number): string {\n  return `enum:${index}`;\n}\n\nfunction arrayTableSelectOptions(column: Column): DataCellSelectOption[] {\n  const enumValues = column.schema.enum ?? [];\n  const hasNullEnumValue = enumValues.some((value) => value === null);\n  const nullableOptions: DataCellSelectOption[] =\n    column.nullable && !hasNullEnumValue\n      ? [{ value: NULL_SELECT_VALUE, label: \"No value\" }]\n      : [];\n\n  return [\n    ...nullableOptions,\n    ...enumValues.map((value, index) => ({\n      value: enumOptionValue(index),\n      label: enumLabel(value),\n    })),\n  ];\n}\n\nfunction arrayTableSelectValue({\n  column,\n  value,\n}: {\n  column: Column;\n  value: unknown;\n}): string | null {\n  const enumIndex =\n    column.schema.enum?.findIndex((candidate) =>\n      enumValueEquals(candidate, value),\n    ) ?? -1;\n\n  if (enumIndex >= 0) return enumOptionValue(enumIndex);\n  if (value === null && column.nullable) return NULL_SELECT_VALUE;\n  return null;\n}\n\nfunction arrayTableSelectCommitValue({\n  column,\n  value,\n}: {\n  column: Column;\n  value: string | null;\n}): unknown {\n  if (value === NULL_SELECT_VALUE || value === null) return null;\n  const match = value.match(/^enum:(\\d+)$/);\n  if (!match) return undefined;\n  return column.schema.enum?.[Number(match[1])];\n}\n",
      "type": "registry:component",
      "target": "@components/json-form/table/array-table-data-cell-props.ts"
    },
    {
      "path": "components/json-form/source-link-focus-intent.ts",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { useMountEffect } from \"@/hooks/use-mount-effect\";\n\ntype SourceLinkFocusModality = \"none\" | \"keyboard\" | \"pointer\";\ntype SourceLinkFocusIntentState = {\n  modality: SourceLinkFocusModality;\n};\n\nconst sourceLinkFocusIntentByDocument = new WeakMap<\n  Document,\n  SourceLinkFocusIntentState\n>();\n\nfunction isKeyboardFocusNavigation(event: KeyboardEvent): boolean {\n  if (\n    event.defaultPrevented ||\n    event.altKey ||\n    event.ctrlKey ||\n    event.metaKey\n  ) {\n    return false;\n  }\n  return (\n    event.key === \"Tab\" ||\n    event.key === \"ArrowDown\" ||\n    event.key === \"ArrowLeft\" ||\n    event.key === \"ArrowRight\" ||\n    event.key === \"ArrowUp\" ||\n    event.key === \"End\" ||\n    event.key === \"Home\" ||\n    event.key === \"PageDown\" ||\n    event.key === \"PageUp\"\n  );\n}\n\nfunction getSourceLinkFocusIntentState(\n  ownerDocument: Document,\n): SourceLinkFocusIntentState {\n  const existingState = sourceLinkFocusIntentByDocument.get(ownerDocument);\n  if (existingState) return existingState;\n\n  const state: SourceLinkFocusIntentState = { modality: \"none\" };\n  sourceLinkFocusIntentByDocument.set(ownerDocument, state);\n\n  ownerDocument.addEventListener(\n    \"keydown\",\n    (event) => {\n      if (isKeyboardFocusNavigation(event)) state.modality = \"keyboard\";\n    },\n    true,\n  );\n  ownerDocument.addEventListener(\n    \"pointerdown\",\n    () => {\n      state.modality = \"pointer\";\n    },\n    true,\n  );\n  ownerDocument.addEventListener(\n    \"mousedown\",\n    () => {\n      state.modality = \"pointer\";\n    },\n    true,\n  );\n  ownerDocument.addEventListener(\n    \"touchstart\",\n    () => {\n      state.modality = \"pointer\";\n    },\n    true,\n  );\n\n  return state;\n}\n\nexport function useSourceLinkFocusPreviewIntent() {\n  useMountEffect(() => {\n    getSourceLinkFocusIntentState(document).modality = \"none\";\n  });\n\n  return React.useCallback((event: React.FocusEvent<HTMLElement>): boolean => {\n    if (event.defaultPrevented) return false;\n    return (\n      getSourceLinkFocusIntentState(event.currentTarget.ownerDocument)\n        .modality === \"keyboard\"\n    );\n  }, []);\n}\n",
      "type": "registry:component",
      "target": "@components/json-form/source-link-focus-intent.ts"
    }
  ],
  "categories": [
    "primitives"
  ],
  "type": "registry:block"
}