{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "csv-sources-block",
  "title": "CSV Sources",
  "description": "Extracted values linked to spreadsheet cells — hover to highlight a cell and scroll to it.",
  "registryDependencies": [
    "@retab/csv-viewer",
    "@retab/csv-source",
    "@retab/document-source",
    "@retab/segmented-document",
    "@retab/source-segmented-document",
    "@retab/source-field-list",
    "@retab/source-field-link",
    "@retab/source-indicator"
  ],
  "files": [
    {
      "path": "registry/new-york-v4/blocks/csv-sources-block.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport type { Source } from \"@/lib/document-source\";\nimport {\n  sourceToCsvCell,\n  useCsvSourceTarget,\n} from \"@/components/ui/csv-source\";\nimport {\n  CsvViewerDocument,\n  type CsvViewerHandle,\n} from \"@/components/ui/csv-viewer\";\nimport {\n  FileViewer,\n  FileViewerContent,\n  FileViewerHeader,\n  FileViewerTitle,\n  FileViewerProvider,\n  FileViewerSidebar,\n  FileViewerSidebarContent,\n  FileViewerInset,\n  FileViewerControls,\n  FileViewerViewport,\n} from \"@/components/ui/file-viewer\";\nimport { SegmentedDocumentProvider } from \"@/components/ui/segmented-document-provider\";\nimport { useSegmentedSourceFieldLink } from \"@/components/ui/source-field-link\";\nimport {\n  SourceFieldList,\n  type SourceField,\n} from \"@/components/ui/source-field-list\";\nimport { createSourcesSegmentedDocumentModel } from \"@/components/ui/source-segmented-document-model\";\nimport csvSample from \"@/components/viewers/sample-data/csv-sources.json\";\n\nconst CSV_TEXT = `region,quarter,revenue,customers,nrr\nNorth America,Q1,1240000,48,1.12\nNorth America,Q2,1510000,61,1.21\nEMEA,Q1,820000,33,1.08\nEMEA,Q2,910000,39,1.15\nAPAC,Q1,430000,18,1.04\nAPAC,Q2,560000,24,1.11`;\nconst CSV_SOURCE = {\n  kind: \"text\" as const,\n  text: CSV_TEXT,\n  fileName: \"sales.csv\",\n};\n\ntype CsvField = SourceField & { source: Source };\n\nconst FIELDS = (csvSample as CsvField[]).map((field) => ({\n  ...field,\n  hint:\n    field.source.anchor.kind === \"csv_cell\"\n      ? `Cell ${field.source.anchor.coordinate ?? field.source.anchor.column}`\n      : undefined,\n}));\nconst FIELD_BY_KEY = new Map(FIELDS.map((field) => [field.key, field]));\nconst SEGMENTED_DOCUMENT = createSourcesSegmentedDocumentModel(\n  FIELDS.map((field) => ({\n    id: field.key,\n    label: field.label,\n    source: field.source,\n  })),\n);\n\n/**\n * CSV sources block — extracted values linked to the spreadsheet cells they came\n * from. Hovering a field highlights its cell and scrolls to it. Segmented\n * source interaction owns preview/selection while the CSV source adapter owns\n * cell navigation.\n */\nexport function CsvSourcesBlock() {\n  const viewerRef = React.useRef<CsvViewerHandle>(null);\n\n  return (\n    <SegmentedDocumentProvider model={SEGMENTED_DOCUMENT}>\n      <CsvSourcesContent viewerRef={viewerRef} />\n    </SegmentedDocumentProvider>\n  );\n}\n\nfunction CsvSourcesContent({\n  viewerRef,\n}: {\n  viewerRef: React.RefObject<CsvViewerHandle | null>;\n}) {\n  const target = useCsvSourceTarget(viewerRef);\n  const segmentedLink = useSegmentedSourceFieldLink({\n    initialSourcePath: FIELDS[0]?.key,\n  });\n  const link = useTargetedSourceFieldLink({\n    fieldByKey: FIELD_BY_KEY,\n    link: segmentedLink,\n    target,\n  });\n  const activeSource = link.activeSourcePath\n    ? FIELD_BY_KEY.get(link.activeSourcePath)?.source\n    : undefined;\n  const activeCell = sourceToCsvCell(activeSource);\n\n  return (\n    <FileViewerProvider source={CSV_SOURCE} defaultSidebarOpen>\n      <FileViewer\n        className=\"bg-background h-full min-h-[680px]\"\n       \n      >\n        <FileViewerHeader>\n            <FileViewerTitle />\n            <FileViewerControls />\n        </FileViewerHeader>\n        <FileViewerContent>\n          <FileViewerInset>\n            <FileViewerViewport>\n              <CsvViewerDocument\n                ref={viewerRef}\n                source={CSV_SOURCE}\n                fillHeight\n                className=\"h-full\"\n                controls={false}\n                activeCell={activeCell}\n              />\n            </FileViewerViewport>\n          </FileViewerInset>\n          <FileViewerSidebar\n            aria-label=\"Source fields\"\n            side=\"right\"\n            collapsible=\"none\"\n            width=\"360px\"\n            className=\"border-l\"\n          >\n            <FileViewerSidebarContent>\n              <SourceFieldList fields={FIELDS} link={link} />\n            </FileViewerSidebarContent>\n          </FileViewerSidebar>\n        </FileViewerContent>\n      </FileViewer>\n    </FileViewerProvider>\n  );\n}\n\nfunction useTargetedSourceFieldLink({\n  fieldByKey,\n  link,\n  target,\n}: {\n  fieldByKey: ReadonlyMap<string, CsvField>;\n  link: ReturnType<typeof useSegmentedSourceFieldLink>;\n  target: ReturnType<typeof useCsvSourceTarget>;\n}) {\n  const scrollToField = React.useCallback(\n    (path: string, behavior: ScrollBehavior) => {\n      const source = fieldByKey.get(path)?.source;\n      if (source) target.scrollTo?.(source, { behavior });\n    },\n    [fieldByKey, target],\n  );\n  const onSourceHover = React.useCallback(\n    (path: string | null) => {\n      link.onSourceHover(path);\n      if (path) scrollToField(path, \"auto\");\n    },\n    [link, scrollToField],\n  );\n  const selectSourcePath = React.useCallback(\n    (path: string) => {\n      link.selectSourcePath?.(path);\n      scrollToField(path, \"smooth\");\n    },\n    [link, scrollToField],\n  );\n\n  return React.useMemo(\n    () => ({\n      ...link,\n      onSourceHover,\n      selectSourcePath,\n    }),\n    [link, onSourceHover, selectSourcePath],\n  );\n}\n",
      "type": "registry:component",
      "target": "@components/blocks/csv-sources-block.tsx"
    },
    {
      "path": "components/viewers/sample-data/csv-sources.json",
      "content": "[\n  {\n    \"key\": \"naQ1Revenue\",\n    \"label\": \"North America Q1 revenue\",\n    \"value\": \"$1,240,000\",\n    \"source\": {\n      \"content\": \"1240000\",\n      \"anchor\": { \"kind\": \"csv_cell\", \"row\": 1, \"column\": \"C\", \"coordinate\": \"C2\" }\n    }\n  },\n  {\n    \"key\": \"naQ1Customers\",\n    \"label\": \"North America Q1 customers\",\n    \"value\": \"48\",\n    \"source\": {\n      \"content\": \"48\",\n      \"anchor\": { \"kind\": \"csv_cell\", \"row\": 1, \"column\": \"D\", \"coordinate\": \"D2\" }\n    }\n  },\n  {\n    \"key\": \"naQ1Nrr\",\n    \"label\": \"North America Q1 net revenue retention\",\n    \"value\": \"1.12\",\n    \"source\": {\n      \"content\": \"1.12\",\n      \"anchor\": { \"kind\": \"csv_cell\", \"row\": 1, \"column\": \"E\", \"coordinate\": \"E2\" }\n    }\n  },\n  {\n    \"key\": \"naQ2Revenue\",\n    \"label\": \"North America Q2 revenue\",\n    \"value\": \"$1,510,000\",\n    \"source\": {\n      \"content\": \"1510000\",\n      \"anchor\": { \"kind\": \"csv_cell\", \"row\": 2, \"column\": \"C\", \"coordinate\": \"C3\" }\n    }\n  },\n  {\n    \"key\": \"naQ2Customers\",\n    \"label\": \"North America Q2 customers\",\n    \"value\": \"61\",\n    \"source\": {\n      \"content\": \"61\",\n      \"anchor\": { \"kind\": \"csv_cell\", \"row\": 2, \"column\": \"D\", \"coordinate\": \"D3\" }\n    }\n  },\n  {\n    \"key\": \"naQ2Nrr\",\n    \"label\": \"North America Q2 net revenue retention\",\n    \"value\": \"1.21\",\n    \"source\": {\n      \"content\": \"1.21\",\n      \"anchor\": { \"kind\": \"csv_cell\", \"row\": 2, \"column\": \"E\", \"coordinate\": \"E3\" }\n    }\n  },\n  {\n    \"key\": \"emeaQ1Revenue\",\n    \"label\": \"EMEA Q1 revenue\",\n    \"value\": \"$820,000\",\n    \"source\": {\n      \"content\": \"820000\",\n      \"anchor\": { \"kind\": \"csv_cell\", \"row\": 3, \"column\": \"C\", \"coordinate\": \"C4\" }\n    }\n  },\n  {\n    \"key\": \"emeaQ1Customers\",\n    \"label\": \"EMEA Q1 customers\",\n    \"value\": \"33\",\n    \"source\": {\n      \"content\": \"33\",\n      \"anchor\": { \"kind\": \"csv_cell\", \"row\": 3, \"column\": \"D\", \"coordinate\": \"D4\" }\n    }\n  },\n  {\n    \"key\": \"emeaQ2Revenue\",\n    \"label\": \"EMEA Q2 revenue\",\n    \"value\": \"$910,000\",\n    \"source\": {\n      \"content\": \"910000\",\n      \"anchor\": { \"kind\": \"csv_cell\", \"row\": 4, \"column\": \"C\", \"coordinate\": \"C5\" }\n    }\n  },\n  {\n    \"key\": \"emeaQ2Customers\",\n    \"label\": \"EMEA Q2 customers\",\n    \"value\": \"39\",\n    \"source\": {\n      \"content\": \"39\",\n      \"anchor\": { \"kind\": \"csv_cell\", \"row\": 4, \"column\": \"D\", \"coordinate\": \"D5\" }\n    }\n  },\n  {\n    \"key\": \"emeaQ2Nrr\",\n    \"label\": \"EMEA Q2 net revenue retention\",\n    \"value\": \"1.15\",\n    \"source\": {\n      \"content\": \"1.15\",\n      \"anchor\": { \"kind\": \"csv_cell\", \"row\": 4, \"column\": \"E\", \"coordinate\": \"E5\" }\n    }\n  },\n  {\n    \"key\": \"apacQ1Revenue\",\n    \"label\": \"APAC Q1 revenue\",\n    \"value\": \"$430,000\",\n    \"source\": {\n      \"content\": \"430000\",\n      \"anchor\": { \"kind\": \"csv_cell\", \"row\": 5, \"column\": \"C\", \"coordinate\": \"C6\" }\n    }\n  },\n  {\n    \"key\": \"apacQ1Nrr\",\n    \"label\": \"APAC Q1 net revenue retention\",\n    \"value\": \"1.04\",\n    \"source\": {\n      \"content\": \"1.04\",\n      \"anchor\": { \"kind\": \"csv_cell\", \"row\": 5, \"column\": \"E\", \"coordinate\": \"E6\" }\n    }\n  },\n  {\n    \"key\": \"apacQ2Revenue\",\n    \"label\": \"APAC Q2 revenue\",\n    \"value\": \"$560,000\",\n    \"source\": {\n      \"content\": \"560000\",\n      \"anchor\": { \"kind\": \"csv_cell\", \"row\": 6, \"column\": \"C\", \"coordinate\": \"C7\" }\n    }\n  },\n  {\n    \"key\": \"apacQ2Customers\",\n    \"label\": \"APAC Q2 customers\",\n    \"value\": \"24\",\n    \"source\": {\n      \"content\": \"24\",\n      \"anchor\": { \"kind\": \"csv_cell\", \"row\": 6, \"column\": \"D\", \"coordinate\": \"D7\" }\n    }\n  },\n  {\n    \"key\": \"apacQ2Nrr\",\n    \"label\": \"APAC Q2 net revenue retention\",\n    \"value\": \"1.11\",\n    \"source\": {\n      \"content\": \"1.11\",\n      \"anchor\": { \"kind\": \"csv_cell\", \"row\": 6, \"column\": \"E\", \"coordinate\": \"E7\" }\n    }\n  }\n]\n",
      "type": "registry:file",
      "target": "@components/viewers/sample-data/csv-sources.json"
    }
  ],
  "categories": [
    "primitives"
  ],
  "type": "registry:block"
}