{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "xlsx-viewer",
  "title": "XLSX Viewer",
  "description": "A fixed-window virtualized Excel/spreadsheet viewer rendered entirely client-side: sheet tabs, zoom, download, formatted values, numeric alignment, sparse workbook storage, and row + column virtualization so huge sheets stay smooth. No useEffect for data loading.",
  "dependencies": [
    "lucide-react",
    "@e965/xlsx@0.20.3"
  ],
  "registryDependencies": [
    "@retab/xlsx-worker-protocol",
    "@retab/xlsx-workbook",
    "@retab/xlsx-sheetjs-flattener",
    "@retab/utils",
    "button",
    "separator",
    "@retab/skeleton",
    "@retab/viewer-controls",
    "@retab/csv",
    "@retab/use-keyed-layout-effect",
    "@retab/use-mount-effect"
  ],
  "files": [
    {
      "path": "registry/new-york-v4/ui/xlsx-viewer.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport {\n  createViewerResource,\n  type ViewerResource,\n} from \"@/lib/viewer-resource\";\nimport { useIsClient } from \"@/components/ui/use-is-client\";\nimport { ViewerErrorBoundary } from \"@/components/ui/viewer-error\";\n\nimport { XlsxViewerFallback } from \"./xlsx-viewer-chrome\";\nimport { XlsxViewerSession } from \"./xlsx-viewer-session\";\nimport type {\n  XlsxResourceContentProps,\n  XlsxViewerHandle,\n  XlsxViewerProps,\n} from \"./xlsx-viewer-types\";\n\nexport type {\n  XlsxCellRef,\n  XlsxDocumentSource,\n  XlsxResourceContentProps,\n  XlsxViewerHandle,\n  XlsxViewerProps,\n} from \"./xlsx-viewer-types\";\n\nexport type XlsxViewerProviderProps = {\n  children: React.ReactNode;\n  resource: ViewerResource;\n};\n\nexport type XlsxViewerWorkbookProps = Omit<\n  XlsxResourceContentProps,\n  \"resource\"\n>;\n\nexport const XlsxViewer = React.forwardRef<XlsxViewerHandle, XlsxViewerProps>(\n  function XlsxViewer(props, ref) {\n    const { source, ...resourceProps } = props;\n    const resource = React.useMemo(\n      () => createViewerResource(source),\n      [source],\n    );\n    return (\n      <XlsxResourceContent {...resourceProps} ref={ref} resource={resource} />\n    );\n  },\n);\n\nconst XlsxViewerResourceContext = React.createContext<ViewerResource | null>(\n  null,\n);\n\nexport function XlsxViewerProvider({\n  children,\n  resource,\n}: XlsxViewerProviderProps) {\n  return (\n    <XlsxViewerResourceContext.Provider value={resource}>\n      {children}\n    </XlsxViewerResourceContext.Provider>\n  );\n}\n\nfunction useXlsxViewerResource(): ViewerResource {\n  const resource = React.useContext(XlsxViewerResourceContext);\n  if (!resource) {\n    throw new Error(\n      \"XlsxViewerWorkbook must be used within XlsxViewerProvider.\",\n    );\n  }\n  return resource;\n}\n\nexport const XlsxViewerWorkbook = React.forwardRef<\n  XlsxViewerHandle,\n  XlsxViewerWorkbookProps\n>(function XlsxViewerWorkbook(props, ref) {\n  const resource = useXlsxViewerResource();\n  return <XlsxResourceContent {...props} ref={ref} resource={resource} />;\n});\n\nexport const XlsxResourceContent = React.forwardRef<\n  XlsxViewerHandle,\n  XlsxResourceContentProps\n>(function XlsxResourceContent(props, ref) {\n  const isClient = useIsClient();\n  const resource = props.resource;\n  if (!isClient) {\n    return (\n      <XlsxViewerFallback\n        className={props.className}\n        fallbackSheetTabs={props.fallbackSheetTabs}\n        controls={props.controls}\n        bare={props.bare}\n      />\n    );\n  }\n  return (\n    <ViewerErrorBoundary\n      className={props.className}\n      download={\n        props.controls === false || props.download === false\n          ? null\n          : resource.originalDownload\n      }\n      format=\"xlsx\"\n      resetKey={resource.keys.resource}\n      sourceKind={resource.sourceKind}\n    >\n      <React.Suspense\n        fallback={\n          <XlsxViewerFallback\n            className={props.className}\n            fallbackSheetTabs={props.fallbackSheetTabs}\n            controls={props.controls}\n            bare={props.bare}\n          />\n        }\n      >\n        <XlsxViewerSession\n          key={resource.keys.resource}\n          {...props}\n          forwardedRef={ref}\n          resource={resource}\n        />\n      </React.Suspense>\n    </ViewerErrorBoundary>\n  );\n});\n",
      "type": "registry:ui",
      "target": "@ui/xlsx-viewer.tsx"
    },
    {
      "path": "registry/new-york-v4/ui/file-viewer-core.ts",
      "content": "import {\n  detectCategory,\n  extensionOf,\n  extractName,\n  resolveViewerDescriptor,\n  textPayloadKey,\n  type FileCategory,\n  type ViewerDescriptor,\n  type ViewerSource,\n} from \"@/lib/viewer-source\";\n\nexport type { FileCategory, ViewerSource };\nexport type FileViewerFallbackSize = { width: number; height: number };\n\nexport interface FileViewerCoreProps {\n  source: ViewerSource;\n  category?: FileCategory;\n  className?: string;\n  /** Intrinsic first-frame size for image/TIFF loading skeletons. */\n  fallbackFrameSize?: FileViewerFallbackSize;\n  /** Intrinsic first-slide size for PPTX loading skeletons. */\n  fallbackSlideSize?: FileViewerFallbackSize;\n  isolateStyles?: boolean;\n}\n\nexport type FileDescriptor = ViewerDescriptor;\n\nexport function resolveFileDescriptor({\n  source,\n  category,\n}: FileViewerCoreProps): FileDescriptor {\n  return resolveViewerDescriptor({\n    source,\n    category,\n  });\n}\n\nexport function descriptorResetKey(descriptor: FileDescriptor): string {\n  return [\n    descriptorIdentityResetKey(descriptor),\n    descriptor.displayName,\n    descriptor.mimeType ?? \"\",\n    descriptor.category,\n  ].join(\"\\u0000\");\n}\n\nfunction descriptorIdentityResetKey(descriptor: FileDescriptor): string {\n  if (\n    descriptor.source.kind === \"text\" &&\n    descriptor.source.identityKey == null\n  ) {\n    return textPayloadKey(descriptor.source.text);\n  }\n  return descriptor.identityKey;\n}\n\nexport function isProseTextDescriptor(descriptor: FileDescriptor): boolean {\n  if (descriptor.category !== \"text\") return false;\n\n  const extension = extensionOf(descriptor.fileName);\n  if (extension === \"txt\" || extension === \"text\") return true;\n  return !extension && descriptor.mimeType?.toLowerCase() === \"text/plain\";\n}\n\nexport { detectCategory, extensionOf, extractName };\n",
      "type": "registry:ui",
      "target": "@ui/file-viewer-core.ts"
    },
    {
      "path": "registry/new-york-v4/lib/viewer-source.ts",
      "content": "export type FileCategory =\n  | \"pdf\"\n  | \"docx\"\n  | \"xlsx\"\n  | \"pptx\"\n  | \"csv\"\n  | \"image\"\n  | \"markdown\"\n  | \"html\"\n  | \"email\"\n  | \"text\"\n  | \"unsupported\";\n\nexport type ViewerSource = UrlViewerSource | TextSource | BlobViewerSource;\n\nexport interface UrlViewerSource {\n  kind: \"url\";\n  url: string;\n  fileName?: string;\n  mimeType?: string;\n  downloadUrl?: string;\n  identityKey?: string;\n}\n\nexport interface TextSource {\n  kind: \"text\";\n  text: string;\n  fileName?: string;\n  mimeType?: string;\n  identityKey?: string;\n}\n\nexport interface BlobViewerSource {\n  kind: \"blob\";\n  blob: Blob;\n  identityKey: string;\n  fileName?: string;\n  mimeType?: string;\n  downloadUrl?: string;\n}\n\nexport interface ViewerDescriptor {\n  source: ViewerSource;\n  category: FileCategory;\n  identityKey: string;\n  displayName: string;\n  fileName: string;\n  mimeType?: string;\n}\n\nconst EXTENSION_CATEGORY: Record<string, FileCategory> = {\n  pdf: \"pdf\",\n  docx: \"docx\",\n  xlsx: \"xlsx\",\n  xls: \"xlsx\",\n  xlsm: \"xlsx\",\n  pptx: \"pptx\",\n  csv: \"csv\",\n  tsv: \"csv\",\n  png: \"image\",\n  jpg: \"image\",\n  jpeg: \"image\",\n  gif: \"image\",\n  webp: \"image\",\n  avif: \"image\",\n  bmp: \"image\",\n  svg: \"image\",\n  ico: \"image\",\n  tif: \"image\",\n  tiff: \"image\",\n  md: \"markdown\",\n  markdown: \"markdown\",\n  mdx: \"text\",\n  html: \"html\",\n  htm: \"html\",\n  eml: \"email\",\n  txt: \"text\",\n  text: \"text\",\n  log: \"text\",\n  json: \"text\",\n  jsonl: \"text\",\n  json5: \"text\",\n  ndjson: \"text\",\n  xml: \"text\",\n  yaml: \"text\",\n  yml: \"text\",\n  toml: \"text\",\n  ini: \"text\",\n  env: \"text\",\n  js: \"text\",\n  mjs: \"text\",\n  cjs: \"text\",\n  jsx: \"text\",\n  ts: \"text\",\n  tsx: \"text\",\n  css: \"text\",\n  scss: \"text\",\n  less: \"text\",\n  py: \"text\",\n  rb: \"text\",\n  go: \"text\",\n  rs: \"text\",\n  java: \"text\",\n  kt: \"text\",\n  c: \"text\",\n  h: \"text\",\n  cpp: \"text\",\n  cc: \"text\",\n  cs: \"text\",\n  php: \"text\",\n  sh: \"text\",\n  bash: \"text\",\n  zsh: \"text\",\n  sql: \"text\",\n  graphql: \"text\",\n  proto: \"text\",\n  lua: \"text\",\n  r: \"text\",\n  swift: \"text\",\n  scala: \"text\",\n  pl: \"text\",\n  vue: \"text\",\n  svelte: \"text\",\n};\n\nexport function extensionOf(name: string): string | null {\n  const clean = name.split(/[?#]/)[0];\n  const base = clean.split(\"/\").pop() ?? clean;\n  const dot = base.lastIndexOf(\".\");\n  return dot > 0 ? base.slice(dot + 1).toLowerCase() : null;\n}\n\nexport function extractName(url: string): string {\n  const clean = url.split(/[?#]/)[0];\n  return clean.split(\"/\").pop() || \"file\";\n}\n\nexport function detectCategory(\n  fileName: string,\n  mimeType?: string,\n): FileCategory {\n  const ext = extensionOf(fileName);\n  if (ext && EXTENSION_CATEGORY[ext]) return EXTENSION_CATEGORY[ext];\n  if (mimeType) {\n    const fromMime = categoryFromMime(mimeType);\n    if (fromMime) return fromMime;\n  }\n  return \"unsupported\";\n}\n\nexport function resolveViewerDescriptor({\n  source,\n  category,\n}: {\n  source: ViewerSource;\n  category?: FileCategory;\n}): ViewerDescriptor {\n  const resolvedMimeType =\n    source.mimeType ??\n    (source.kind === \"blob\" && source.blob.type ? source.blob.type : undefined);\n  const displayName = source.fileName ?? defaultDisplayName(source);\n  const fileName = source.fileName ?? defaultFileName(source);\n  const resolvedCategory =\n    category ?? detectCategory(displayName, resolvedMimeType);\n\n  return {\n    source,\n    category: resolvedCategory,\n    identityKey: source.identityKey ?? defaultIdentityKey(source),\n    displayName,\n    fileName,\n    mimeType: resolvedMimeType,\n  };\n}\n\nfunction categoryFromMime(mimeType: string): FileCategory | null {\n  const mime = mimeType.toLowerCase().split(\";\")[0].trim();\n  if (mime === \"application/pdf\") return \"pdf\";\n  if (mime.includes(\"wordprocessingml\")) return \"docx\";\n  if (mime.includes(\"spreadsheet\") || mime.includes(\"ms-excel\")) return \"xlsx\";\n  if (mime.includes(\"presentation\") || mime.includes(\"ms-powerpoint\")) {\n    return \"pptx\";\n  }\n  if (mime === \"text/csv\" || mime === \"text/tab-separated-values\") return \"csv\";\n  if (mime === \"text/markdown\") return \"markdown\";\n  if (mime === \"text/html\") return \"html\";\n  if (mime === \"message/rfc822\" || mime === \"message/global\") {\n    return \"email\";\n  }\n  if (mime.startsWith(\"image/\")) return \"image\";\n  if (mime === \"application/json\" || mime === \"application/xml\") return \"text\";\n  if (mime.startsWith(\"text/\")) return \"text\";\n  return null;\n}\n\nfunction defaultDisplayName(source: ViewerSource) {\n  if (source.kind === \"url\") return source.url;\n  if (source.kind === \"text\") return \"text.txt\";\n  return \"file\";\n}\n\nfunction defaultFileName(source: ViewerSource) {\n  if (source.kind === \"url\") return extractName(source.url);\n  if (source.kind === \"text\") return \"text.txt\";\n  return \"file\";\n}\n\nfunction defaultIdentityKey(source: ViewerSource) {\n  if (source.kind === \"url\") return `url:${source.url}`;\n  if (source.kind === \"text\") return textPayloadIdentityKey(source.text);\n  return source.identityKey;\n}\n\nexport function textPayloadIdentityKey(text: string) {\n  return textPayloadKey(text);\n}\n\nexport function textPayloadKey(text: string) {\n  return `text:${text.length}:${hashString(text)}`;\n}\n\nfunction hashString(text: string) {\n  let hash = 0x811c9dc5;\n  for (let index = 0; index < text.length; index += 1) {\n    hash ^= text.charCodeAt(index);\n    hash = Math.imul(hash, 0x01000193);\n  }\n  return (hash >>> 0).toString(36);\n}\n",
      "type": "registry:lib",
      "target": "@lib/viewer-source.ts"
    },
    {
      "path": "registry/new-york-v4/lib/viewer-resource.ts",
      "content": "import {\n  createBlobDownloadAction,\n  createHrefDownloadAction,\n  createTextDownloadAction,\n  type ViewerDownloadAction,\n} from \"@/lib/viewer-download-actions\";\nimport {\n  isAbortError,\n  ResourceError,\n  type ResourceTooLargeReason,\n} from \"@/lib/viewer-errors\";\nimport {\n  resolveViewerDescriptor,\n  textPayloadKey,\n  type BlobViewerSource,\n  type FileCategory,\n  type TextSource,\n  type UrlViewerSource,\n  type ViewerDescriptor,\n  type ViewerSource,\n} from \"@/lib/viewer-source\";\n\nexport interface ResourceReadOptions {\n  cache?: RequestCache;\n  signal?: AbortSignal;\n}\n\nexport interface TextReadOptions extends ResourceReadOptions {\n  maxBytes?: number;\n  maxLines?: number;\n}\n\nexport interface ByteRange {\n  start: number;\n  end: number;\n}\n\nexport interface ByteRangeResult {\n  buffer: ArrayBuffer;\n  contentRange?: {\n    start: number;\n    end: number;\n    total: number | null;\n  };\n  isComplete: boolean;\n}\n\nexport interface ViewerResourceKeys {\n  readonly load: string;\n  readonly presentation: string;\n  readonly resource: string;\n}\n\nexport type ViewerResourcePayload =\n  | { kind: \"url\"; url: string }\n  | { kind: \"blob\"; blob: Blob }\n  | { kind: \"text\"; text: string };\n\nexport interface ViewerResourceContent {\n  readonly key: string;\n  readonly sourceKind: ViewerSource[\"kind\"];\n  readonly directUrl: string | null;\n  readonly mimeType?: string;\n  readonly payload: ViewerResourcePayload;\n  readBlob(options?: ResourceReadOptions): Promise<Blob>;\n  readBytes(options?: ResourceReadOptions): Promise<ArrayBuffer>;\n  readText(options?: TextReadOptions): Promise<string>;\n  readStream(\n    options?: ResourceReadOptions,\n  ): Promise<ReadableStream<Uint8Array>>;\n  readRange(\n    range: ByteRange,\n    options?: ResourceReadOptions,\n  ): Promise<ByteRangeResult>;\n}\n\nexport type ViewerContentIdentity = Pick<\n  ViewerResourceContent,\n  \"key\" | \"sourceKind\"\n>;\n\nexport type ViewerContentDirectUrl = ViewerContentIdentity &\n  Pick<ViewerResourceContent, \"directUrl\">;\n\nexport type ViewerContentPayload = ViewerContentIdentity &\n  Pick<ViewerResourceContent, \"payload\">;\n\nexport type ViewerContentMime = ViewerContentIdentity &\n  Pick<ViewerResourceContent, \"mimeType\">;\n\nexport type ViewerContentBlob = ViewerContentIdentity &\n  Pick<ViewerResourceContent, \"readBlob\">;\n\nexport type ViewerContentBytes = ViewerContentIdentity &\n  Pick<ViewerResourceContent, \"readBytes\">;\n\nexport type ViewerContentText = ViewerContentIdentity &\n  Pick<ViewerResourceContent, \"readText\">;\n\nexport type ViewerContentStream = ViewerContentIdentity &\n  Pick<ViewerResourceContent, \"readStream\">;\n\nexport type ViewerContentRange = ViewerContentIdentity &\n  Pick<ViewerResourceContent, \"readRange\">;\n\nexport interface ViewerResource {\n  readonly descriptor: ViewerDescriptor;\n  readonly sourceKind: ViewerSource[\"kind\"];\n  readonly keys: ViewerResourceKeys;\n  readonly identityKey: string;\n  readonly fileName: string;\n  readonly mimeType?: string;\n  readonly content: ViewerResourceContent;\n  readonly originalDownload: ViewerDownloadAction;\n}\n\nconst URL_RESOURCE_REGISTRY_MAX = 128;\nconst TEXT_RESOURCE_REGISTRY_MAX = 64;\n// LF, CR, CRLF, LINE SEPARATOR (U+2028), and PARAGRAPH SEPARATOR (U+2029) — the\n// ECMAScript LineTerminator set, matching what a browser breaks on in a\n// `white-space: pre` block. Kept in sync with text-viewer-resource's splitter.\nconst TEXT_LINE_BREAK_PATTERN = /\\r\\n|[\\n\\r\\u2028\\u2029]/g;\n\nconst urlViewerResourceRegistry = new Map<string, ViewerResource>();\nconst urlViewerResourceContentRegistry = new Map<\n  string,\n  ViewerResourceContent\n>();\nconst textViewerResourceRegistry = new Map<string, ViewerResource>();\nconst textViewerResourceContentRegistry = new Map<\n  string,\n  ViewerResourceContent\n>();\nlet blobViewerResourceRegistry = new WeakMap<\n  Blob,\n  Map<string, ViewerResource>\n>();\nlet blobViewerResourceContentRegistry = new WeakMap<\n  Blob,\n  Map<string, ViewerResourceContent>\n>();\nconst blobObjectKeys = new WeakMap<Blob, string>();\nlet nextBlobObjectKey = 0;\n\nexport function createViewerResource(\n  source: ViewerSource,\n  category?: FileCategory,\n): ViewerResource {\n  const descriptor = resolveViewerDescriptor({ source, category });\n  const keys = viewerResourceKeys(source, descriptor);\n\n  if (source.kind === \"url\") {\n    return internUrlResource(source, descriptor, keys);\n  }\n  if (source.kind === \"blob\") {\n    return internBlobResource(source, descriptor, keys);\n  }\n  return internTextResource(source, descriptor, keys);\n}\n\nexport function clearViewerResourceRegistryForTests() {\n  urlViewerResourceRegistry.clear();\n  urlViewerResourceContentRegistry.clear();\n  textViewerResourceRegistry.clear();\n  textViewerResourceContentRegistry.clear();\n  blobViewerResourceRegistry = new WeakMap();\n  blobViewerResourceContentRegistry = new WeakMap();\n}\n\nfunction internUrlResource(\n  source: UrlViewerSource,\n  descriptor: ViewerDescriptor,\n  keys: ViewerResourceKeys,\n): ViewerResource {\n  const cached = urlViewerResourceRegistry.get(keys.resource);\n  if (cached) return cached;\n\n  const resource = createUrlResource(source, descriptor, keys);\n  urlViewerResourceRegistry.set(keys.resource, resource);\n  pruneUrlResourceRegistry();\n  return resource;\n}\n\nfunction internBlobResource(\n  source: BlobViewerSource,\n  descriptor: ViewerDescriptor,\n  keys: ViewerResourceKeys,\n): ViewerResource {\n  let resources = blobViewerResourceRegistry.get(source.blob);\n  if (!resources) {\n    resources = new Map();\n    blobViewerResourceRegistry.set(source.blob, resources);\n  }\n\n  const cached = resources.get(keys.resource);\n  if (cached) return cached;\n\n  const resource = createBlobResource(source, descriptor, keys);\n  resources.set(keys.resource, resource);\n  return resource;\n}\n\nfunction internTextResource(\n  source: TextSource,\n  descriptor: ViewerDescriptor,\n  keys: ViewerResourceKeys,\n): ViewerResource {\n  const cached = textViewerResourceRegistry.get(keys.resource);\n  if (cached) return cached;\n\n  const resource = createTextResource(source, descriptor, keys);\n  textViewerResourceRegistry.set(keys.resource, resource);\n  pruneTextResourceRegistry();\n  return resource;\n}\n\nfunction pruneUrlResourceRegistry() {\n  while (urlViewerResourceRegistry.size > URL_RESOURCE_REGISTRY_MAX) {\n    const firstKey = urlViewerResourceRegistry.keys().next().value;\n    if (!firstKey) return;\n    urlViewerResourceRegistry.delete(firstKey);\n  }\n}\n\nfunction pruneUrlResourceContentRegistry() {\n  while (urlViewerResourceContentRegistry.size > URL_RESOURCE_REGISTRY_MAX) {\n    const firstKey = urlViewerResourceContentRegistry.keys().next().value;\n    if (!firstKey) return;\n    urlViewerResourceContentRegistry.delete(firstKey);\n  }\n}\n\nfunction pruneTextResourceRegistry() {\n  while (textViewerResourceRegistry.size > TEXT_RESOURCE_REGISTRY_MAX) {\n    const firstKey = textViewerResourceRegistry.keys().next().value;\n    if (!firstKey) return;\n    textViewerResourceRegistry.delete(firstKey);\n  }\n}\n\nfunction pruneTextResourceContentRegistry() {\n  while (textViewerResourceContentRegistry.size > TEXT_RESOURCE_REGISTRY_MAX) {\n    const firstKey = textViewerResourceContentRegistry.keys().next().value;\n    if (!firstKey) return;\n    textViewerResourceContentRegistry.delete(firstKey);\n  }\n}\n\nfunction viewerResourceKeys(\n  source: ViewerSource,\n  descriptor: ViewerDescriptor,\n): ViewerResourceKeys {\n  const load = viewerResourceLoadKey(source, descriptor);\n  const presentation = viewerResourcePresentationKey(source, descriptor);\n  return {\n    load,\n    presentation,\n    resource: [load, presentation].join(\"\\u0000\"),\n  };\n}\n\nfunction viewerResourceLoadKey(\n  source: ViewerSource,\n  descriptor: ViewerDescriptor,\n) {\n  return [\n    source.kind,\n    source.identityKey ?? \"\",\n    sourceMimeType(source) ?? \"\",\n    directLoadCacheKey(source),\n    payloadCacheKey(source, descriptor),\n  ].join(\"\\u0000\");\n}\n\nfunction viewerResourcePresentationKey(\n  source: ViewerSource,\n  descriptor: ViewerDescriptor,\n) {\n  return [\n    descriptor.category,\n    descriptor.displayName,\n    descriptor.fileName,\n    descriptor.mimeType ?? \"\",\n    downloadCacheKey(source),\n  ].join(\"\\u0000\");\n}\n\nfunction directLoadCacheKey(source: ViewerSource) {\n  return source.kind === \"url\" ? source.url : \"\";\n}\n\nfunction downloadCacheKey(source: ViewerSource) {\n  if (source.kind === \"text\") return \"\";\n  return source.downloadUrl ?? \"\";\n}\n\nfunction payloadCacheKey(source: ViewerSource, descriptor: ViewerDescriptor) {\n  if (source.kind === \"url\") return \"\";\n  if (source.kind === \"blob\") return blobObjectKey(source.blob);\n  return source.identityKey ? \"\" : descriptor.identityKey;\n}\n\nexport function viewerResourceRenderKey(resource: ViewerResource): string {\n  const load = [\n    resource.sourceKind,\n    resource.identityKey,\n    resource.mimeType ?? resource.content.mimeType ?? \"\",\n    resource.content.directUrl ?? \"\",\n    viewerContentRenderKey(resource.content),\n  ].join(\"\\u0000\");\n\n  return [load, resource.keys.presentation].join(\"\\u0000\");\n}\n\nexport function viewerContentRenderKey(content: ViewerResourceContent): string {\n  if (content.payload.kind === \"text\")\n    return textPayloadKey(content.payload.text);\n  return content.key;\n}\n\nfunction sourceMimeType(source: ViewerSource) {\n  if (source.kind === \"blob\") return source.mimeType ?? source.blob.type;\n  return source.mimeType;\n}\n\nfunction blobObjectKey(blob: Blob) {\n  let key = blobObjectKeys.get(blob);\n  if (!key) {\n    nextBlobObjectKey += 1;\n    key = `blob-object:${nextBlobObjectKey}`;\n    blobObjectKeys.set(blob, key);\n  }\n  return key;\n}\n\nexport function blobSource(\n  bytes: Blob | ArrayBuffer | Uint8Array,\n  metadata: {\n    identityKey: string;\n    fileName?: string;\n    mimeType?: string;\n    downloadUrl?: string;\n  },\n): BlobViewerSource {\n  const blob =\n    bytes instanceof Blob\n      ? bytes\n      : new Blob(\n          [bytes instanceof ArrayBuffer ? bytes : new Uint8Array(bytes)],\n          {\n            type: metadata.mimeType ?? \"\",\n          },\n        );\n  return {\n    kind: \"blob\",\n    blob,\n    fileName: metadata.fileName,\n    mimeType: metadata.mimeType ?? blob.type,\n    downloadUrl: metadata.downloadUrl,\n    identityKey: metadata.identityKey,\n  };\n}\n\nfunction createUrlResource(\n  source: UrlViewerSource,\n  descriptor: ViewerDescriptor,\n  keys: ViewerResourceKeys,\n): ViewerResource {\n  const content = internUrlResourceContent(source, keys);\n  const originalDownload = createHrefDownloadAction({\n    id: \"download-original\",\n    label: \"Download\",\n    href: source.downloadUrl ?? source.url,\n    fileName: descriptor.fileName,\n  });\n\n  return resourceBase(source, descriptor, keys, { content, originalDownload });\n}\n\nfunction internUrlResourceContent(\n  source: UrlViewerSource,\n  keys: ViewerResourceKeys,\n): ViewerResourceContent {\n  const cached = urlViewerResourceContentRegistry.get(keys.load);\n  if (cached) return cached;\n\n  const content = resourceContentBase(source, keys, {\n    directUrl: source.url,\n    payload: { kind: \"url\", url: source.url },\n    readBlob: async ({ cache, signal } = {}) => {\n      const response = await fetchResource(\n        source.url,\n        cache ? { cache, signal } : { signal },\n      );\n      validateFullContentResponse(response);\n      return readResponseBlob(response);\n    },\n    readBytes: async ({ cache, signal } = {}) => {\n      const response = await fetchResource(\n        source.url,\n        cache ? { cache, signal } : { signal },\n      );\n      validateFullContentResponse(response);\n      return readResponseArrayBuffer(response);\n    },\n    readText: async ({ cache, signal, maxBytes, maxLines } = {}) => {\n      const response = await fetchResource(\n        source.url,\n        cache ? { cache, signal } : { signal },\n      );\n      return readBoundedResponseText(response, { maxBytes, maxLines });\n    },\n    readStream: async ({ cache, signal } = {}) => {\n      const response = await fetchResource(\n        source.url,\n        cache ? { cache, signal } : { signal },\n      );\n      validateFullContentResponse(response);\n      if (!response.body) {\n        if (response.status === 204 || response.status === 205) {\n          return emptyByteStream();\n        }\n        throw new ResourceError({\n          kind: \"unsupported_capability\",\n          message: \"This response cannot be streamed.\",\n        });\n      }\n      return response.body;\n    },\n    readRange: async (range, { cache, signal } = {}) => {\n      validateByteRange(range);\n      const { start, end } = range;\n      const init = {\n        signal,\n        headers: { Range: `bytes=${start}-${end}` },\n      };\n      const response = await fetchResource(\n        source.url,\n        cache ? { ...init, cache } : init,\n      );\n      const buffer = await readResponseArrayBuffer(response);\n      const contentRange = parseContentRange(\n        response.headers.get(\"content-range\"),\n      );\n      validateUrlRangeResponse({\n        bufferLength: buffer.byteLength,\n        contentRange,\n        range,\n        status: response.status,\n      });\n      return {\n        buffer,\n        contentRange,\n        isComplete: isByteRangeComplete({\n          bufferLength: buffer.byteLength,\n          contentRange,\n          requestedLength: end - start + 1,\n          status: response.status,\n        }),\n      };\n    },\n  });\n  urlViewerResourceContentRegistry.set(keys.load, content);\n  pruneUrlResourceContentRegistry();\n  return content;\n}\n\nfunction emptyByteStream() {\n  return new ReadableStream<Uint8Array>({\n    start(controller) {\n      controller.close();\n    },\n  });\n}\n\nfunction createBlobResource(\n  source: BlobViewerSource,\n  descriptor: ViewerDescriptor,\n  keys: ViewerResourceKeys,\n): ViewerResource {\n  const blob = source.blob;\n  const content = internBlobResourceContent(source, keys);\n  const originalDownload = source.downloadUrl\n    ? createHrefDownloadAction({\n        id: \"download-original\",\n        label: \"Download\",\n        href: source.downloadUrl,\n        fileName: descriptor.fileName,\n      })\n    : createBlobDownloadAction({\n        id: \"download-original\",\n        label: \"Download\",\n        blob,\n        fileName: descriptor.fileName,\n      });\n\n  return resourceBase(source, descriptor, keys, { content, originalDownload });\n}\n\nfunction internBlobResourceContent(\n  source: BlobViewerSource,\n  keys: ViewerResourceKeys,\n): ViewerResourceContent {\n  const blob = source.blob;\n  let contents = blobViewerResourceContentRegistry.get(blob);\n  if (!contents) {\n    contents = new Map();\n    blobViewerResourceContentRegistry.set(blob, contents);\n  }\n\n  const cached = contents.get(keys.load);\n  if (cached) return cached;\n\n  const content = resourceContentBase(source, keys, {\n    directUrl: null,\n    payload: { kind: \"blob\", blob },\n    readBlob: async () => blob,\n    readBytes: async () => blob.arrayBuffer(),\n    readText: async ({ maxBytes, maxLines } = {}) =>\n      readBoundedBlobText(blob, { maxBytes, maxLines }),\n    readStream: async () => blob.stream(),\n    readRange: async (range) => {\n      validateByteRange(range);\n      const { start, end } = range;\n      validateKnownByteRangeStart(start, blob.size);\n      const rangeBlob = blob.slice(start, end + 1);\n      return {\n        buffer: await rangeBlob.arrayBuffer(),\n        contentRange: {\n          start,\n          end: Math.min(end, blob.size - 1),\n          total: blob.size,\n        },\n        isComplete: end >= blob.size - 1,\n      };\n    },\n  });\n  contents.set(keys.load, content);\n  return content;\n}\n\nfunction createTextResource(\n  source: TextSource,\n  descriptor: ViewerDescriptor,\n  keys: ViewerResourceKeys,\n): ViewerResource {\n  const content = internTextResourceContent(source, keys);\n  const originalDownload = createTextDownloadAction({\n    id: \"download-original\",\n    label: \"Download\",\n    text: source.text,\n    fileName: descriptor.fileName,\n    mimeType: descriptor.mimeType,\n  });\n\n  return resourceBase(source, descriptor, keys, { content, originalDownload });\n}\n\nfunction internTextResourceContent(\n  source: TextSource,\n  keys: ViewerResourceKeys,\n): ViewerResourceContent {\n  const cached = textViewerResourceContentRegistry.get(keys.load);\n  if (cached) return cached;\n\n  const content = resourceContentBase(source, keys, {\n    directUrl: null,\n    payload: { kind: \"text\", text: source.text },\n    readBlob: async () =>\n      new Blob([source.text], {\n        type: \"text/plain;charset=utf-8\",\n      }),\n    readBytes: async () =>\n      typedArrayBuffer(new TextEncoder().encode(source.text)),\n    readText: async ({ maxBytes, maxLines } = {}) =>\n      readBoundedInlineText(source.text, { maxBytes, maxLines }),\n    readStream: async () => new Blob([source.text]).stream(),\n    readRange: async (range) => {\n      validateByteRange(range);\n      const { start, end } = range;\n      const buffer = new TextEncoder().encode(source.text);\n      validateKnownByteRangeStart(start, buffer.byteLength);\n      const slice = buffer.slice(start, end + 1);\n      return {\n        buffer: typedArrayBuffer(slice),\n        contentRange: {\n          start,\n          end: Math.min(end, buffer.byteLength - 1),\n          total: buffer.byteLength,\n        },\n        isComplete: end >= buffer.byteLength - 1,\n      };\n    },\n  });\n  textViewerResourceContentRegistry.set(keys.load, content);\n  pruneTextResourceContentRegistry();\n  return content;\n}\n\nfunction resourceBase(\n  source: ViewerSource,\n  descriptor: ViewerDescriptor,\n  keys: ViewerResourceKeys,\n  options: {\n    content: ViewerResourceContent;\n    originalDownload: ViewerDownloadAction;\n  },\n): ViewerResource {\n  const { content, originalDownload } = options;\n  return Object.freeze({\n    descriptor,\n    sourceKind: source.kind,\n    keys,\n    identityKey: descriptor.identityKey,\n    fileName: descriptor.fileName,\n    mimeType: descriptor.mimeType,\n    content,\n    originalDownload,\n  });\n}\n\nfunction resourceContentBase(\n  source: ViewerSource,\n  keys: ViewerResourceKeys,\n  methods: Omit<ViewerResourceContent, \"key\" | \"sourceKind\" | \"mimeType\">,\n): ViewerResourceContent {\n  return Object.freeze({\n    key: keys.load,\n    sourceKind: source.kind,\n    mimeType: sourceMimeType(source),\n    ...methods,\n  });\n}\n\nfunction typedArrayBuffer(bytes: Uint8Array): ArrayBuffer {\n  return bytes.buffer.slice(\n    bytes.byteOffset,\n    bytes.byteOffset + bytes.byteLength,\n  ) as ArrayBuffer;\n}\n\nasync function fetchResource(\n  input: RequestInfo | URL,\n  init?: RequestInit,\n): Promise<Response> {\n  let response: Response;\n  try {\n    response = await fetch(input, init);\n  } catch (error) {\n    if (isAbortError(error)) {\n      throw new ResourceError({\n        kind: \"aborted\",\n        message: \"Loading was cancelled.\",\n        cause: error,\n      });\n    }\n    throw new ResourceError({\n      kind: \"fetch_failed\",\n      message: \"Could not fetch this resource.\",\n      cause: error,\n    });\n  }\n\n  if (!response.ok && response.status !== 206) {\n    throw new ResourceError({\n      kind: \"http_error\",\n      message: `Failed to load resource: ${response.status}`,\n      status: response.status,\n    });\n  }\n\n  return response;\n}\n\nasync function readBoundedResponseText(\n  response: Response,\n  bounds: { maxBytes?: number; maxLines?: number },\n) {\n  validateFullContentResponse(response);\n\n  const maxBytes = bounds.maxBytes;\n  if (\n    isContentLengthOverLimit(response.headers.get(\"content-length\"), maxBytes)\n  ) {\n    throw tooLarge(\"bytes\");\n  }\n\n  const body = response.body;\n  if (!body) {\n    const buffer = await readResponseArrayBuffer(response);\n    if (maxBytes != null && buffer.byteLength > maxBytes) {\n      throw tooLarge(\"bytes\");\n    }\n    const text = new TextDecoder().decode(buffer);\n    assertLineLimit(text, bounds.maxLines);\n    return text;\n  }\n\n  const reader = body.getReader();\n  const decoder = new TextDecoder();\n  const lineLimitTracker = createLineLimitTracker(bounds.maxLines);\n  let receivedBytes = 0;\n  let text = \"\";\n\n  while (true) {\n    const { done, value } = await readResponseStreamChunk(reader);\n    if (done) break;\n    receivedBytes += value.byteLength;\n    if (maxBytes != null && receivedBytes > maxBytes) {\n      await cancelReaderSilently(reader);\n      throw tooLarge(\"bytes\");\n    }\n    const chunkText = decoder.decode(value, { stream: true });\n    try {\n      lineLimitTracker.push(chunkText);\n    } catch (error) {\n      await cancelReaderSilently(reader);\n      throw error;\n    }\n    text += chunkText;\n  }\n\n  const finalText = decoder.decode();\n  lineLimitTracker.push(finalText);\n  text += finalText;\n  return text;\n}\n\nfunction isContentLengthOverLimit(\n  contentLength: string | null,\n  maxBytes: number | undefined,\n) {\n  if (maxBytes == null || contentLength == null) return false;\n\n  const normalizedLength = contentLength.trim().replace(/^0+(?=\\d)/, \"\");\n  if (!/^\\d+$/.test(normalizedLength)) return false;\n\n  const maxLength = String(maxBytes);\n  return (\n    normalizedLength.length > maxLength.length ||\n    (normalizedLength.length === maxLength.length &&\n      normalizedLength > maxLength)\n  );\n}\n\nfunction validateFullContentResponse(response: Response) {\n  if (response.status !== 206) return;\n\n  const contentRange = parseContentRange(response.headers.get(\"content-range\"));\n  if (\n    contentRange?.total != null &&\n    contentRange.start === 0 &&\n    contentRange.end === contentRange.total - 1\n  ) {\n    return;\n  }\n\n  throw new ResourceError({\n    kind: \"partial_content\",\n    message: \"Full response returned partial content.\",\n    status: response.status,\n  });\n}\n\nasync function readResponseStreamChunk(\n  reader: ReadableStreamDefaultReader<Uint8Array>,\n) {\n  try {\n    return await reader.read();\n  } catch (error) {\n    throw resourceReadError(error);\n  }\n}\n\nasync function readResponseArrayBuffer(response: Response) {\n  try {\n    return await response.arrayBuffer();\n  } catch (error) {\n    throw resourceReadError(error);\n  }\n}\n\nasync function readResponseBlob(response: Response) {\n  try {\n    return await response.blob();\n  } catch (error) {\n    throw resourceReadError(error);\n  }\n}\n\nfunction resourceReadError(error: unknown) {\n  if (isAbortError(error)) {\n    return new ResourceError({\n      kind: \"aborted\",\n      message: \"Loading was cancelled.\",\n      cause: error,\n    });\n  }\n  return new ResourceError({\n    kind: \"fetch_failed\",\n    message: \"Could not read this resource.\",\n    cause: error,\n  });\n}\n\nasync function readBoundedBlobText(\n  blob: Blob,\n  bounds: { maxBytes?: number; maxLines?: number },\n) {\n  if (bounds.maxBytes != null && blob.size > bounds.maxBytes) {\n    throw tooLarge(\"bytes\");\n  }\n  const text = await blob.text();\n  assertLineLimit(text, bounds.maxLines);\n  return text;\n}\n\nfunction readBoundedInlineText(\n  text: string,\n  { maxBytes, maxLines }: { maxBytes?: number; maxLines?: number },\n) {\n  // For inline sources the string *is* the resource, so its UTF-8 byte length\n  // is the authoritative size to measure against maxBytes.\n  if (\n    maxBytes != null &&\n    new TextEncoder().encode(text).byteLength > maxBytes\n  ) {\n    throw tooLarge(\"bytes\");\n  }\n  assertLineLimit(text, maxLines);\n  return text;\n}\n\n// Used after a transferred-byte check has already enforced maxBytes (URL/blob).\n// Re-encoding the decoded text here would double-count: invalid UTF-8 decodes to\n// U+FFFD (3 bytes each), inflating the measured size past the real wire bytes\n// and falsely rejecting small resources as \"too large\".\nfunction assertLineLimit(text: string, maxLines: number | undefined) {\n  if (\n    maxLines != null &&\n    text.split(TEXT_LINE_BREAK_PATTERN).length > maxLines\n  ) {\n    throw tooLarge(\"lines\");\n  }\n}\n\nfunction tooLarge(reason: ResourceTooLargeReason) {\n  return new ResourceError({\n    kind: \"too_large\",\n    tooLargeReason: reason,\n    message: `Resource exceeds ${reason} limit.`,\n  });\n}\n\nasync function cancelReaderSilently(\n  reader: ReadableStreamDefaultReader<Uint8Array>,\n) {\n  try {\n    await reader.cancel();\n  } catch {\n    // Preserve the user-facing load failure; cancellation is best-effort cleanup.\n  }\n}\n\nfunction validateByteRange({ start, end }: ByteRange) {\n  if (\n    !Number.isSafeInteger(start) ||\n    !Number.isSafeInteger(end) ||\n    start < 0 ||\n    end < start\n  ) {\n    throw new ResourceError({\n      kind: \"invalid_range\",\n      message: \"Byte range must use non-negative integer bounds.\",\n    });\n  }\n}\n\nfunction validateKnownByteRangeStart(start: number, total: number) {\n  if (start > 0 && start >= total) {\n    throw new ResourceError({\n      kind: \"invalid_range\",\n      message: \"Byte range starts past the available resource.\",\n    });\n  }\n}\n\nfunction validateUrlRangeResponse({\n  bufferLength,\n  contentRange,\n  range,\n  status,\n}: {\n  bufferLength: number;\n  contentRange: ByteRangeResult[\"contentRange\"];\n  range: ByteRange;\n  status: number;\n}) {\n  if (status === 200) {\n    if (range.start !== 0 || bufferLength > range.end - range.start + 1) {\n      throw new ResourceError({\n        kind: \"invalid_range\",\n        message: \"Full response does not match the requested byte range.\",\n      });\n    }\n    return;\n  }\n  if (status !== 206) {\n    throw new ResourceError({\n      kind: \"invalid_range\",\n      message: \"Range response must return full or partial content.\",\n    });\n  }\n  if (!contentRange) {\n    throw new ResourceError({\n      kind: \"invalid_range\",\n      message: \"Partial content response is missing a valid byte range.\",\n    });\n  }\n  const declaredLength = contentRange.end - contentRange.start + 1;\n  if (\n    contentRange.start !== range.start ||\n    contentRange.end < contentRange.start ||\n    contentRange.end > range.end ||\n    (contentRange.total != null && contentRange.end >= contentRange.total) ||\n    declaredLength !== bufferLength\n  ) {\n    throw new ResourceError({\n      kind: \"invalid_range\",\n      message: \"Response byte range does not match the requested range.\",\n    });\n  }\n}\n\nfunction isByteRangeComplete({\n  bufferLength,\n  contentRange,\n  requestedLength,\n  status,\n}: {\n  bufferLength: number;\n  contentRange: ByteRangeResult[\"contentRange\"];\n  requestedLength: number;\n  status: number;\n}) {\n  if (status === 200) return true;\n  if (contentRange?.total != null) {\n    if (contentRange.total <= 0) return true;\n    return contentRange.end >= contentRange.total - 1;\n  }\n  if (contentRange) return false;\n  return bufferLength < requestedLength;\n}\n\nfunction isStandaloneLineBreak(character: string) {\n  const code = character.charCodeAt(0);\n  return code === 0x0a || code === 0x2028 || code === 0x2029;\n}\n\nfunction createLineLimitTracker(maxLines: number | undefined) {\n  let lineCount = 1;\n  let previousWasCR = false;\n\n  return {\n    push(text: string) {\n      if (maxLines == null || text.length === 0) return;\n\n      for (const character of text) {\n        if (previousWasCR) {\n          previousWasCR = false;\n          if (character === \"\\n\") continue;\n        }\n\n        if (character === \"\\r\") {\n          lineCount += 1;\n          previousWasCR = true;\n        } else if (isStandaloneLineBreak(character)) {\n          // LF, plus LINE/PARAGRAPH SEPARATOR (U+2028/U+2029); none pair with CR.\n          lineCount += 1;\n        }\n\n        if (lineCount > maxLines) {\n          throw tooLarge(\"lines\");\n        }\n      }\n    },\n  };\n}\n\nfunction parseContentRange(value: string | null) {\n  if (!value) return undefined;\n  const match = value.match(/^bytes\\s+(\\d+)-(\\d+)\\/(\\d+|\\*)\\s*$/i);\n  if (!match) return undefined;\n  const start = parseContentRangeNumber(match[1]);\n  const end = parseContentRangeNumber(match[2]);\n  const total =\n    match[3] === \"*\" ? null : parseContentRangeNumber(match[3] ?? \"\");\n  if (start == null || end == null || total === undefined) return undefined;\n  return {\n    start,\n    end,\n    total,\n  };\n}\n\nfunction parseContentRangeNumber(value: string) {\n  const number = Number(value);\n  return Number.isSafeInteger(number) ? number : undefined;\n}\n",
      "type": "registry:lib",
      "target": "@lib/viewer-resource.ts"
    },
    {
      "path": "registry/new-york-v4/lib/viewer-download-actions.ts",
      "content": "export type ViewerDownloadOrigin = \"original\" | \"derived\";\n\nexport type ViewerDownloadPayload =\n  | { kind: \"href\"; href: string }\n  | { kind: \"blob\"; blob: Blob }\n  | { kind: \"text\"; text: string; mimeType?: string }\n  | { kind: \"none\" };\n\nexport interface ViewerDownloadAction {\n  id: string;\n  label: string;\n  fileName: string;\n  origin: ViewerDownloadOrigin;\n  isDisabled?: boolean;\n  getPayload: (options?: {\n    signal?: AbortSignal;\n  }) => ViewerDownloadPayload | Promise<ViewerDownloadPayload>;\n}\n\nexport type ViewerDownloadErrorKind =\n  | \"disabled\"\n  | \"aborted\"\n  | \"payload_failed\"\n  | \"unsupported\";\n\nexport class ViewerDownloadError extends Error {\n  readonly kind: ViewerDownloadErrorKind;\n  readonly actionId: string;\n  override readonly cause?: unknown;\n\n  constructor({\n    actionId,\n    kind,\n    message,\n    cause,\n  }: {\n    actionId: string;\n    kind: ViewerDownloadErrorKind;\n    message: string;\n    cause?: unknown;\n  }) {\n    super(message);\n    this.name = \"ViewerDownloadError\";\n    this.actionId = actionId;\n    this.kind = kind;\n    this.cause = cause;\n  }\n}\n\nexport function createHrefDownloadAction({\n  id,\n  label = \"Download\",\n  href,\n  fileName,\n  origin = \"original\",\n}: {\n  id: string;\n  label?: string;\n  href: string;\n  fileName: string;\n  origin?: ViewerDownloadOrigin;\n}): ViewerDownloadAction {\n  return {\n    id,\n    label,\n    fileName,\n    origin,\n    getPayload: () => ({ kind: \"href\", href }),\n  };\n}\n\nexport function createBlobDownloadAction({\n  id,\n  label = \"Download\",\n  blob,\n  fileName,\n  origin = \"original\",\n}: {\n  id: string;\n  label?: string;\n  blob: Blob;\n  fileName: string;\n  origin?: ViewerDownloadOrigin;\n}): ViewerDownloadAction {\n  return {\n    id,\n    label,\n    fileName,\n    origin,\n    getPayload: () => ({ kind: \"blob\", blob }),\n  };\n}\n\nexport function createTextDownloadAction({\n  id,\n  label = \"Download\",\n  text,\n  fileName,\n  mimeType,\n  origin = \"original\",\n}: {\n  id: string;\n  label?: string;\n  text: string;\n  fileName: string;\n  mimeType?: string;\n  origin?: ViewerDownloadOrigin;\n}): ViewerDownloadAction {\n  return {\n    id,\n    label,\n    fileName,\n    origin,\n    getPayload: () => ({ kind: \"text\", text, mimeType }),\n  };\n}\n\nexport function createDisabledDownloadAction({\n  id,\n  label = \"Download\",\n  fileName,\n  origin = \"original\",\n}: {\n  id: string;\n  label?: string;\n  fileName: string;\n  origin?: ViewerDownloadOrigin;\n}): ViewerDownloadAction {\n  return {\n    id,\n    label,\n    fileName,\n    origin,\n    isDisabled: true,\n    getPayload: () => ({ kind: \"none\" }),\n  };\n}\n",
      "type": "registry:lib",
      "target": "@lib/viewer-download-actions.ts"
    },
    {
      "path": "registry/new-york-v4/lib/viewer-errors.ts",
      "content": "export type ViewerFormat =\n  | \"pdf\"\n  | \"image\"\n  | \"text\"\n  | \"csv\"\n  | \"docx\"\n  | \"xlsx\"\n  | \"pptx\"\n  | \"file\";\n\nexport type ViewerErrorDomain =\n  | \"resource\"\n  | \"format\"\n  | \"state\"\n  | \"unsupported\"\n  | \"unknown\";\n\nexport type ResourceErrorKind =\n  | \"fetch_failed\"\n  | \"http_error\"\n  | \"aborted\"\n  | \"invalid_range\"\n  | \"partial_content\"\n  | \"too_large\"\n  | \"unsupported_capability\"\n  | \"unknown\";\n\nexport type ResourceTooLargeReason = \"bytes\" | \"lines\";\n\nexport class ResourceError extends Error {\n  readonly domain = \"resource\";\n  readonly kind: ResourceErrorKind;\n  readonly status?: number;\n  readonly tooLargeReason?: ResourceTooLargeReason;\n  override readonly cause?: unknown;\n\n  constructor({\n    kind,\n    message,\n    status,\n    tooLargeReason,\n    cause,\n  }: {\n    kind: ResourceErrorKind;\n    message: string;\n    status?: number;\n    tooLargeReason?: ResourceTooLargeReason;\n    cause?: unknown;\n  }) {\n    super(message);\n    this.name = \"ResourceError\";\n    this.kind = kind;\n    this.status = status;\n    this.tooLargeReason = tooLargeReason;\n    this.cause = cause;\n  }\n}\n\nexport type ViewerFormatErrorKind =\n  | \"bounds\"\n  | \"decode_failed\"\n  | \"disposed\"\n  | \"index_out_of_range\"\n  | \"load_failed\"\n  | \"parse_failed\"\n  | \"render_failed\"\n  | \"worker_failed\"\n  | \"unknown\";\n\nexport interface ViewerFormatErrorMapperOptions {\n  kind: ViewerFormatErrorKind;\n  message: string;\n}\n\nexport class ViewerFormatError extends Error {\n  readonly domain = \"format\";\n  readonly format: ViewerFormat;\n  readonly kind: ViewerFormatErrorKind;\n  override readonly cause?: unknown;\n\n  constructor({\n    format,\n    kind,\n    message,\n    cause,\n  }: {\n    format: ViewerFormat;\n    kind: ViewerFormatErrorKind;\n    message: string;\n    cause?: unknown;\n  }) {\n    super(message);\n    this.name = \"ViewerFormatError\";\n    this.format = format;\n    this.kind = kind;\n    this.cause = cause;\n  }\n}\n\nexport type ViewerStateErrorKind =\n  | \"invalid_bounds\"\n  | \"invalid_target\"\n  | \"out_of_range\"\n  | \"stale_resource\"\n  | \"unknown\";\n\nexport class ViewerStateError extends Error {\n  readonly domain = \"state\";\n  readonly format?: ViewerFormat;\n  readonly kind: ViewerStateErrorKind;\n  override readonly cause?: unknown;\n\n  constructor({\n    format,\n    kind,\n    message,\n    cause,\n  }: {\n    format?: ViewerFormat;\n    kind: ViewerStateErrorKind;\n    message: string;\n    cause?: unknown;\n  }) {\n    super(message);\n    this.name = \"ViewerStateError\";\n    this.format = format;\n    this.kind = kind;\n    this.cause = cause;\n  }\n}\n\nexport class ViewerUnsupportedError extends Error {\n  readonly domain = \"unsupported\";\n  readonly format?: ViewerFormat;\n  readonly sourceKind?: string;\n  override readonly cause?: unknown;\n\n  constructor({\n    format,\n    sourceKind,\n    message,\n    cause,\n  }: {\n    format?: ViewerFormat;\n    sourceKind?: string;\n    message: string;\n    cause?: unknown;\n  }) {\n    super(message);\n    this.name = \"ViewerUnsupportedError\";\n    this.format = format;\n    this.sourceKind = sourceKind;\n    this.cause = cause;\n  }\n}\n\nexport interface ViewerErrorInfo {\n  domain: ViewerErrorDomain;\n  format?: ViewerFormat;\n  kind: string;\n  message: string;\n  status?: number;\n  isRetryable: boolean;\n  isDownloadUseful: boolean;\n  userMessage: string;\n  cause?: unknown;\n}\n\nexport interface ViewerErrorContext {\n  format?: ViewerFormat;\n  sourceKind?: \"url\" | \"blob\" | \"text\";\n  canDownload?: boolean;\n  retry?: \"auto\" | \"always\" | \"never\";\n}\n\nexport function isAbortError(error: unknown): boolean {\n  return (\n    (error instanceof DOMException && error.name === \"AbortError\") ||\n    (error instanceof Error && error.name === \"AbortError\")\n  );\n}\n\nexport function isResourceError(error: unknown): error is ResourceError {\n  return (\n    error instanceof ResourceError ||\n    isErrorLike(error, \"ResourceError\", \"resource\")\n  );\n}\n\nexport function isViewerFormatError(\n  error: unknown,\n): error is ViewerFormatError {\n  return (\n    error instanceof ViewerFormatError ||\n    isErrorLike(error, \"ViewerFormatError\", \"format\")\n  );\n}\n\nexport function isViewerStateError(error: unknown): error is ViewerStateError {\n  return (\n    error instanceof ViewerStateError ||\n    isErrorLike(error, \"ViewerStateError\", \"state\")\n  );\n}\n\nexport function isViewerUnsupportedError(\n  error: unknown,\n): error is ViewerUnsupportedError {\n  return (\n    error instanceof ViewerUnsupportedError ||\n    isErrorLike(error, \"ViewerUnsupportedError\", \"unsupported\")\n  );\n}\n\nexport function toViewerErrorInfo(\n  error: unknown,\n  context: ViewerErrorContext = {},\n): ViewerErrorInfo {\n  const canDownload = context.canDownload ?? true;\n\n  if (isResourceError(error)) {\n    return {\n      domain: \"resource\",\n      format: context.format,\n      kind: error.kind,\n      message: error.message,\n      status: error.status,\n      isRetryable: retryable(\n        context,\n        resourceErrorDefaultRetry(error, context),\n      ),\n      isDownloadUseful: canDownload && error.kind !== \"aborted\",\n      userMessage: resourceErrorUserMessage(error),\n      cause: error.cause,\n    };\n  }\n\n  if (isViewerFormatError(error)) {\n    const format = error.format ?? context.format;\n    return {\n      domain: \"format\",\n      format,\n      kind: error.kind,\n      message: error.message,\n      isRetryable: retryable(\n        context,\n        formatErrorDefaultRetry(error, context, format),\n      ),\n      isDownloadUseful: canDownload,\n      userMessage: formatErrorUserMessage(format, error.kind, error),\n      cause: error.cause,\n    };\n  }\n\n  if (isViewerStateError(error)) {\n    return {\n      domain: \"state\",\n      format: error.format ?? context.format,\n      kind: error.kind,\n      message: error.message,\n      isRetryable: retryable(context, false),\n      isDownloadUseful: canDownload,\n      userMessage: stateErrorUserMessage(error.kind),\n      cause: error.cause,\n    };\n  }\n\n  if (isViewerUnsupportedError(error)) {\n    return {\n      domain: \"unsupported\",\n      format: error.format ?? context.format,\n      kind: \"unsupported\",\n      message: error.message,\n      isRetryable: retryable(context, false),\n      isDownloadUseful: canDownload,\n      userMessage: \"This file cannot be previewed here.\",\n      cause: error.cause,\n    };\n  }\n\n  if (isAbortError(error)) {\n    return {\n      domain: \"resource\",\n      format: context.format,\n      kind: \"aborted\",\n      message: \"Loading was cancelled.\",\n      isRetryable: retryable(context, false),\n      isDownloadUseful: false,\n      userMessage: \"Loading was cancelled.\",\n      cause: error,\n    };\n  }\n\n  const message = error instanceof Error ? error.message : String(error);\n  return {\n    domain: \"unknown\",\n    format: context.format,\n    kind: \"unknown\",\n    message,\n    isRetryable: retryable(context, unknownErrorDefaultRetry(context)),\n    isDownloadUseful: canDownload,\n    userMessage: fallbackUserMessage(context.format),\n    cause: error,\n  };\n}\n\nfunction isErrorLike(error: unknown, name: string, domain: ViewerErrorDomain) {\n  if (!error || typeof error !== \"object\") return false;\n  const candidate = error as {\n    name?: unknown;\n    domain?: unknown;\n    kind?: unknown;\n  };\n  return (\n    (candidate.name === name || candidate.domain === domain) &&\n    typeof candidate.kind === \"string\"\n  );\n}\n\nfunction retryable(context: ViewerErrorContext, fallback: boolean) {\n  if (context.retry === \"always\") return true;\n  if (context.retry === \"never\") return false;\n  return fallback;\n}\n\nfunction resourceErrorDefaultRetry(\n  error: ResourceError,\n  context: ViewerErrorContext,\n) {\n  if (error.kind === \"aborted\") return false;\n  if (error.kind === \"invalid_range\") return false;\n  if (error.kind === \"too_large\") return false;\n  if (error.kind === \"unsupported_capability\") return false;\n  return context.sourceKind === \"url\";\n}\n\nfunction formatErrorDefaultRetry(\n  error: ViewerFormatError,\n  context: ViewerErrorContext,\n  format: ViewerFormat | undefined,\n) {\n  if (format === \"text\" && error.kind === \"bounds\") return false;\n  if (error.kind === \"disposed\") return false;\n  if (error.kind === \"index_out_of_range\") return false;\n  if (format === \"docx\") return true;\n  return context.sourceKind === \"url\";\n}\n\nfunction unknownErrorDefaultRetry(context: ViewerErrorContext) {\n  if (context.format === \"docx\") return true;\n  return context.sourceKind === \"url\";\n}\n\nfunction resourceErrorUserMessage(error: ResourceError) {\n  if (error.kind === \"http_error\") {\n    return error.status\n      ? `Failed to load file: ${error.status}.`\n      : \"Couldn't load this file.\";\n  }\n  if (error.kind === \"fetch_failed\") return \"Couldn't load this file.\";\n  if (error.kind === \"aborted\") return \"Loading was cancelled.\";\n  if (error.kind === \"invalid_range\") return \"This source range is invalid.\";\n  if (error.kind === \"too_large\") {\n    return error.tooLargeReason === \"lines\"\n      ? \"This file has too many lines to preview.\"\n      : \"This file is too large to preview.\";\n  }\n  if (error.kind === \"partial_content\") {\n    return \"This source returned partial content and cannot be previewed here.\";\n  }\n  if (error.kind === \"unsupported_capability\") {\n    return \"This source cannot be previewed here.\";\n  }\n  return \"Couldn't load this file.\";\n}\n\nfunction formatErrorUserMessage(\n  format: ViewerFormat | undefined,\n  kind: string,\n  error?: unknown,\n) {\n  if (format === \"pdf\") return \"Couldn't load this PDF.\";\n  if (format === \"image\") {\n    if (kind === \"index_out_of_range\")\n      return \"This image page is out of range.\";\n    if (kind === \"decode_failed\") return \"Couldn't decode this image.\";\n    return \"Couldn't load this image.\";\n  }\n  if (format === \"text\") {\n    if (kind === \"render_failed\") return \"Couldn't render this text file.\";\n    if (kind === \"bounds\") {\n      const boundsError = error as {\n        reason?: unknown;\n        boundName?: unknown;\n      };\n      if (boundsError.reason === \"lines\") {\n        return \"This text file has too many lines to preview.\";\n      }\n      if (boundsError.reason === \"bytes\") {\n        return \"This text file is too large to preview.\";\n      }\n      if (typeof boundsError.boundName === \"string\") {\n        return \"Text viewer bounds are invalid.\";\n      }\n    }\n    return \"Couldn't load this text file.\";\n  }\n  if (format === \"csv\") return \"Couldn't parse this table.\";\n  if (format === \"docx\") return \"Couldn't render this document.\";\n  if (format === \"xlsx\") return \"Couldn't parse this spreadsheet.\";\n  if (format === \"pptx\") {\n    if (kind === \"render_failed\") return \"Couldn't render this slide.\";\n    return \"Couldn't load this presentation.\";\n  }\n  return \"Couldn't load this file.\";\n}\n\nfunction stateErrorUserMessage(kind: ViewerStateErrorKind) {\n  if (kind === \"invalid_bounds\") return \"Viewer bounds are invalid.\";\n  if (kind === \"invalid_target\") return \"The requested target is invalid.\";\n  if (kind === \"out_of_range\") return \"The requested item is out of range.\";\n  if (kind === \"stale_resource\") return \"This viewer state is no longer valid.\";\n  return \"Couldn't load this file.\";\n}\n\nfunction fallbackUserMessage(format: ViewerFormat | undefined) {\n  if (format === \"pdf\") return \"Couldn't load this PDF.\";\n  if (format === \"image\") return \"Couldn't load this image.\";\n  if (format === \"text\") return \"Couldn't load this text file.\";\n  if (format === \"csv\") return \"Couldn't parse this table.\";\n  if (format === \"docx\") return \"Couldn't load this document.\";\n  if (format === \"xlsx\") return \"Couldn't load this spreadsheet.\";\n  if (format === \"pptx\") return \"Couldn't load this presentation.\";\n  return \"Couldn't load this file.\";\n}\n",
      "type": "registry:lib",
      "target": "@lib/viewer-errors.ts"
    },
    {
      "path": "registry/new-york-v4/ui/xlsx-viewer-chrome.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\nimport { Skeleton } from \"@/components/ui/skeleton\";\nimport {\n  VIEWER_CONTROLS_HEIGHT_PX,\n  ViewerControlsSkeleton,\n} from \"@/components/ui/viewer-controls\";\nimport { XlsxGridSkeleton } from \"@/components/ui/xlsx-grid-skeleton\";\nimport { XLSX_SHEET_TABS_HEIGHT_PX } from \"@/components/ui/xlsx-sheet-tabs\";\n\nexport function XlsxViewerFrame({\n  className,\n  bare,\n  children,\n}: {\n  className?: string;\n  bare: boolean;\n  children: React.ReactNode;\n}) {\n  return (\n    <div\n      className={cn(\n        \"flex min-h-0 flex-col overflow-hidden\",\n        bare ? \"bg-muted/20 h-full\" : \"bg-muted/30 rounded-xl border\",\n        className,\n      )}\n      data-slot=\"xlsx-viewer\"\n    >\n      {children}\n    </div>\n  );\n}\n\nexport function XlsxViewerBody({\n  children,\n  fallbackSheetTabs,\n  controls,\n}: {\n  children: React.ReactNode;\n  fallbackSheetTabs: boolean;\n  controls: boolean;\n}) {\n  return (\n    <div\n      className={cn(\n        \"relative flex min-h-0\",\n        fallbackSheetTabs ? \"flex-none\" : \"flex-1\",\n      )}\n      style={\n        fallbackSheetTabs\n          ? {\n              height: `calc(100% - ${\n                XLSX_SHEET_TABS_HEIGHT_PX +\n                (controls ? VIEWER_CONTROLS_HEIGHT_PX : 0)\n              }px)`,\n            }\n          : undefined\n      }\n    >\n      <div className=\"flex min-h-0 min-w-0 flex-1 flex-col\">{children}</div>\n    </div>\n  );\n}\n\nexport function XlsxGridColumn({ children }: { children: React.ReactNode }) {\n  return <div className=\"flex min-h-0 min-w-0 flex-1 flex-col\">{children}</div>;\n}\n\nexport function XlsxViewerFallback({\n  className,\n  fallbackSheetTabs = false,\n  controls = true,\n  bare = false,\n}: {\n  className?: string;\n  fallbackSheetTabs?: boolean;\n  controls?: boolean;\n  bare?: boolean;\n}) {\n  return (\n    <XlsxViewerFrame className={className} bare={bare}>\n      {controls ? (\n        <ViewerControlsSkeleton title subtitle zoom download />\n      ) : null}\n      <XlsxViewerBody controls={controls} fallbackSheetTabs={fallbackSheetTabs}>\n        <XlsxGridSkeleton />\n      </XlsxViewerBody>\n      {fallbackSheetTabs ? <XlsxSheetTabsSkeleton /> : null}\n    </XlsxViewerFrame>\n  );\n}\n\nexport function XlsxSheetTabsSkeleton() {\n  return (\n    <div\n      aria-hidden\n      className=\"bg-card flex flex-shrink-0 items-stretch gap-0.5 overflow-hidden border-t px-1.5 py-1\"\n      data-slot=\"xlsx-viewer-tabs-skeleton\"\n      style={{ height: XLSX_SHEET_TABS_HEIGHT_PX }}\n    >\n      {[96, 144, 136, 128].map((width, index) => (\n        <div\n          key={index}\n          className=\"flex h-[24px] flex-shrink-0 items-center rounded-md px-2.5\"\n          style={{ width }}\n        >\n          <Skeleton className=\"h-3 w-full\" />\n        </div>\n      ))}\n    </div>\n  );\n}\n",
      "type": "registry:ui",
      "target": "@ui/xlsx-viewer-chrome.tsx"
    },
    {
      "path": "registry/new-york-v4/ui/xlsx-viewer-resource.ts",
      "content": "import {\n  isResourceError,\n  isViewerFormatError,\n  ViewerFormatError,\n  type ViewerFormatErrorMapperOptions,\n} from \"@/lib/viewer-errors\";\nimport type { ViewerContentBytes } from \"@/lib/viewer-resource\";\nimport {\n  buildXlsxSourceFromCompact,\n  XlsxSourceCache,\n  type CompactSheet,\n  type XlsxSource,\n} from \"@/lib/xlsx-workbook\";\nimport {\n  type XlsxWorkerRequest,\n  type XlsxWorkerResponse,\n} from \"@/lib/xlsx-worker-protocol\";\n\nconst sourceCache = new XlsxSourceCache({ maxEntries: 4 });\n\nexport function getXlsxSource(\n  content: ViewerContentBytes,\n): Promise<XlsxSource> {\n  return sourceCache.get(content.key, () => buildXlsxSource(content));\n}\n\nasync function buildXlsxSource(\n  content: ViewerContentBytes,\n): Promise<XlsxSource> {\n  try {\n    const buffer = await content.readBytes();\n    return buildXlsxSourceFromCompact(await parseWorkbookInWorker(buffer));\n  } catch (error) {\n    if (isResourceError(error)) throw error;\n    throw toXlsxFormatError(error, {\n      kind: \"parse_failed\",\n      message: \"Failed to parse spreadsheet.\",\n    });\n  }\n}\n\nfunction toXlsxFormatError(\n  error: unknown,\n  options: ViewerFormatErrorMapperOptions,\n): ViewerFormatError {\n  if (isViewerFormatError(error)) return error;\n  return new ViewerFormatError({\n    format: \"xlsx\",\n    kind: options.kind,\n    message: options.message,\n    cause: error,\n  });\n}\n\nfunction parseWorkbookInWorker(buffer: ArrayBuffer): Promise<CompactSheet[]> {\n  return new Promise((resolve, reject) => {\n    if (typeof Worker === \"undefined\") {\n      reject(\n        toXlsxFormatError(undefined, {\n          kind: \"worker_failed\",\n          message: \"Web Workers are unavailable in this environment.\",\n        }),\n      );\n      return;\n    }\n\n    const worker = new Worker(\n      new URL(\"./xlsx-viewer.worker\", import.meta.url),\n      { type: \"module\" },\n    );\n    worker.onmessage = (event: MessageEvent<unknown>) => {\n      worker.terminate();\n      const response = parseXlsxWorkerResponse(event.data);\n      if (!response) {\n        reject(\n          toXlsxFormatError(undefined, {\n            kind: \"parse_failed\",\n            message: \"Spreadsheet worker returned an invalid response.\",\n          }),\n        );\n        return;\n      }\n      if (response.type === \"workbook\") {\n        resolve(response.sheets);\n      } else {\n        reject(\n          toXlsxFormatError(undefined, {\n            kind: \"parse_failed\",\n            message: response.message || \"Failed to parse spreadsheet.\",\n          }),\n        );\n      }\n    };\n    worker.onerror = (event) => {\n      worker.terminate();\n      reject(\n        toXlsxFormatError(event, {\n          kind: \"worker_failed\",\n          message: event.message || \"Spreadsheet worker failed.\",\n        }),\n      );\n    };\n    const request: XlsxWorkerRequest = { type: \"parse_workbook\", buffer };\n    worker.postMessage(request, [buffer]);\n  });\n}\n\nfunction parseXlsxWorkerResponse(value: unknown): XlsxWorkerResponse | null {\n  if (value == null || typeof value !== \"object\") return null;\n  const response = value as Partial<XlsxWorkerResponse>;\n  if (response.type === \"workbook\") {\n    return Array.isArray(response.sheets)\n      ? ({\n          type: \"workbook\",\n          sheets: response.sheets,\n        } satisfies XlsxWorkerResponse)\n      : null;\n  }\n  if (response.type === \"error\") {\n    return {\n      type: \"error\",\n      code: \"parse_failed\",\n      message:\n        typeof response.message === \"string\"\n          ? response.message\n          : \"Failed to parse spreadsheet.\",\n    };\n  }\n  return null;\n}\n",
      "type": "registry:ui",
      "target": "@ui/xlsx-viewer-resource.ts"
    },
    {
      "path": "registry/new-york-v4/ui/xlsx-viewer-session.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { useKeyedMountEffect } from \"@/hooks/use-keyed-mount-effect\";\nimport type { ViewerResource } from \"@/lib/viewer-resource\";\nimport {\n  useViewerControlsRegistration,\n  ViewerControls,\n  type ViewerControlsState,\n} from \"@/components/ui/viewer-controls\";\nimport { XlsxSheetTabs } from \"@/components/ui/xlsx-sheet-tabs\";\n\nimport {\n  XlsxGridColumn,\n  XlsxSheetTabsSkeleton,\n  XlsxViewerBody,\n  XlsxViewerFrame,\n} from \"./xlsx-viewer-chrome\";\nimport { useXlsxDownloadActions } from \"./xlsx-viewer-download\";\nimport { getXlsxSource } from \"./xlsx-viewer-resource\";\nimport { useXlsxScale } from \"./xlsx-viewer-scale\";\nimport { useXlsxScrollController } from \"./xlsx-viewer-scroll\";\nimport { XlsxViewerSheet, XlsxViewerSheetSkeleton } from \"./xlsx-viewer-sheet\";\nimport { useXlsxSheetState } from \"./xlsx-viewer-sheet-state\";\nimport type { XlsxViewerHandle, XlsxViewerProps } from \"./xlsx-viewer-types\";\nimport { joinEffectKey } from \"@/lib/effect-key\";\n\nexport function XlsxViewerSession({\n  resource,\n  className,\n  controls = true,\n  download = true,\n  defaultSheetIndex = 0,\n  onSheetChange,\n  fallbackSheetTabs = false,\n  bare = false,\n  activeCell,\n  isolateStyles = false,\n  forwardedRef,\n}: Omit<XlsxViewerProps, \"source\"> & {\n  resource: ViewerResource;\n  forwardedRef?: React.ForwardedRef<XlsxViewerHandle>;\n}) {\n  const viewportElementRef = React.useRef<HTMLDivElement | null>(null);\n  const content = resource.content;\n  const sourcePromise = React.useMemo(() => getXlsxSource(content), [content]);\n  const {\n    activeSheetIndex,\n    activeSheet,\n    sheets,\n    isReady,\n    reportSource,\n    selectSheet,\n    activateSheet,\n  } = useXlsxSheetState({ defaultSheetIndex, onSheetChange });\n  const { scale, zoomOut, zoomIn, resetZoom } = useXlsxScale();\n  const { scrollRequest, scrollToCell } = useXlsxScrollController({\n    activeSheetIndex,\n    sheets,\n    activateSheet,\n  });\n\n  React.useImperativeHandle(\n    forwardedRef ?? null,\n    () => ({\n      scrollToCell,\n      getViewportElement: () => viewportElementRef.current,\n    }),\n    [scrollToCell],\n  );\n\n  const isReservingFallbackSheetTabs = fallbackSheetTabs && !sheets;\n  const downloadActions = useXlsxDownloadActions({\n    resource,\n    activeSheet,\n    activeSheetIndex,\n    content,\n    sheets,\n  });\n  const controlsDownloadActions = download ? downloadActions : [];\n  useXlsxControlsRegistration({\n    activeSheet,\n    controlsDownloadActions,\n    isReady,\n    resetZoom,\n    scale,\n    zoomIn,\n    zoomOut,\n  });\n\n  return (\n    <XlsxViewerFrame className={className} bare={bare}>\n      {controls ? (\n        <ViewerControls\n          title={isReady ? (activeSheet?.name ?? \"-\") : null}\n          subtitle={\n            isReady && activeSheet\n              ? `${activeSheet.rowCount.toLocaleString()} x ${\n                  activeSheet.columnCount\n                }`\n              : null\n          }\n          loading={!isReady}\n          zoom={{\n            scale,\n            onZoomOut: zoomOut,\n            onZoomIn: zoomIn,\n            onFit: resetZoom,\n            fitLabel: \"Actual size\",\n          }}\n          downloads={controlsDownloadActions}\n        />\n      ) : null}\n\n      <XlsxViewerBody\n        controls={controls}\n        fallbackSheetTabs={isReservingFallbackSheetTabs}\n      >\n        <XlsxGridColumn>\n          <React.Suspense fallback={<XlsxViewerSheetSkeleton />}>\n            <XlsxViewerSheet\n              sourcePromise={sourcePromise}\n              activeSheetIndex={activeSheetIndex}\n              scale={scale}\n              onReportSource={reportSource}\n              activeCell={activeCell}\n              scrollRequest={scrollRequest}\n              isolateStyles={isolateStyles}\n              viewportRef={viewportElementRef}\n            />\n          </React.Suspense>\n        </XlsxGridColumn>\n      </XlsxViewerBody>\n\n      {sheets ? (\n        <XlsxSheetTabs\n          sheets={sheets}\n          activeSheetIndex={activeSheetIndex}\n          onSelectSheet={selectSheet}\n        />\n      ) : fallbackSheetTabs ? (\n        <XlsxSheetTabsSkeleton />\n      ) : null}\n    </XlsxViewerFrame>\n  );\n}\n\nfunction useXlsxControlsRegistration({\n  activeSheet,\n  controlsDownloadActions,\n  isReady,\n  resetZoom,\n  scale,\n  zoomIn,\n  zoomOut,\n}: {\n  activeSheet: { name: string; rowCount: number; columnCount: number } | null;\n  controlsDownloadActions: ViewerControlsState[\"downloads\"];\n  isReady: boolean;\n  resetZoom: () => void;\n  scale: number;\n  zoomIn: () => void;\n  zoomOut: () => void;\n}) {\n  const onControlsChange = useViewerControlsRegistration();\n  const controlsState = React.useMemo<ViewerControlsState>(\n    () => ({\n      title: isReady ? (activeSheet?.name ?? \"-\") : null,\n      subtitle:\n        isReady && activeSheet\n          ? `${activeSheet.rowCount.toLocaleString()} x ${\n              activeSheet.columnCount\n            }`\n          : null,\n      loading: !isReady,\n      zoom: {\n        scale,\n        onZoomOut: zoomOut,\n        onZoomIn: zoomIn,\n        onFit: resetZoom,\n        fitLabel: \"Actual size\",\n      },\n      downloads: controlsDownloadActions,\n    }),\n    [\n      activeSheet,\n      controlsDownloadActions,\n      isReady,\n      resetZoom,\n      scale,\n      zoomIn,\n      zoomOut,\n    ],\n  );\n\n  useKeyedMountEffect(\n    joinEffectKey([\"xlsx-controls\", onControlsChange, controlsState]),\n    () => {\n      if (!onControlsChange) return;\n      onControlsChange(controlsState);\n      return () => onControlsChange(null);\n    },\n  );\n}\n",
      "type": "registry:ui",
      "target": "@ui/xlsx-viewer-session.tsx"
    },
    {
      "path": "registry/new-york-v4/ui/xlsx-viewer-types.ts",
      "content": "import type { ViewerResource } from \"@/lib/viewer-resource\";\nimport type { BlobViewerSource, UrlViewerSource } from \"@/lib/viewer-source\";\n\nimport type { PublicXlsxCellRef } from \"./xlsx-viewer-scroll\";\n\nexport type XlsxDocumentSource = UrlViewerSource | BlobViewerSource;\n\nexport interface XlsxViewerProps {\n  /** Canonical spreadsheet source. */\n  source: XlsxDocumentSource;\n  className?: string;\n  controls?: boolean;\n  /** Show download actions in this viewer's controls/error state. */\n  download?: boolean;\n  /** Sheet shown first. Defaults to 0. */\n  defaultSheetIndex?: number;\n  /** Fired with the active sheet index on tab switch and imperative sheet changes. */\n  onSheetChange?: (index: number) => void;\n  /** Reserve the workbook tab bar while metadata loads. Use for known multi-sheet files. */\n  fallbackSheetTabs?: boolean;\n  /** Drop the outer border/rounded/background so the viewer fills its container. */\n  bare?: boolean;\n  /** Public compatibility coordinates: 0-based row + column on `sheet`. */\n  activeCell?: XlsxCellRef | null;\n  /**\n   * Render the scrolling grid inside a shadow root, isolating it from host page\n   * style invalidation while preserving inherited theme variables.\n   */\n  isolateStyles?: boolean;\n}\n\nexport type XlsxResourceContentProps = Omit<XlsxViewerProps, \"source\"> & {\n  resource: ViewerResource;\n};\n\nexport type XlsxCellRef = PublicXlsxCellRef;\n\nexport interface XlsxViewerHandle {\n  scrollToCell: (\n    sheet: number,\n    row: number,\n    col: number,\n    options?: { behavior?: ScrollBehavior },\n  ) => void;\n  getViewportElement: () => HTMLDivElement | null;\n}\n",
      "type": "registry:ui",
      "target": "@ui/xlsx-viewer-types.ts"
    },
    {
      "path": "registry/new-york-v4/ui/xlsx-viewer-download.ts",
      "content": "import * as React from \"react\";\n\nimport type { ViewerDownloadAction } from \"@/lib/viewer-download-actions\";\nimport type {\n  ViewerResource,\n  ViewerResourceContent,\n} from \"@/lib/viewer-resource\";\nimport type { XlsxSource } from \"@/lib/xlsx-workbook\";\nimport { type XlsxSheetMeta, xlsxColumnLabel } from \"@/lib/xlsx-workbook\";\n\nimport { serializeCsvTable } from \"./csv-viewer-download\";\nimport { getXlsxSource } from \"./xlsx-viewer-resource\";\n\nconst CSV_DIALECT = { delimiter: \",\", hasHeader: true } as const;\nconst MAX_DENSE_CSV_EXPORT_CELLS = 1_000_000;\n\nexport function createXlsxSheetCsvExportAction({\n  fileName,\n  sheetIndex,\n  getSource,\n}: {\n  fileName: string;\n  sheetIndex: number;\n  getSource: () => Promise<XlsxSource>;\n}): ViewerDownloadAction {\n  return {\n    id: \"xlsx-export-sheet\",\n    label: \"Export sheet\",\n    fileName,\n    origin: \"derived\",\n    getPayload: async ({ signal } = {}) => {\n      throwIfAborted(signal);\n      const source = await getSource();\n      throwIfAborted(signal);\n      return {\n        kind: \"text\",\n        text: serializeXlsxSheetAsCsv({ source, sheetIndex, signal }),\n        mimeType: \"text/csv;charset=utf-8\",\n      };\n    },\n  };\n}\n\nexport function xlsxSheetCsvFileName({\n  fileName,\n  sheetName,\n  sheetCount,\n}: {\n  fileName: string;\n  sheetName?: string;\n  sheetCount?: number;\n}) {\n  const baseName = sanitizeFileNamePart(\n    fileName.replace(/\\.[^.\\\\/]+$/, \"\") || \"spreadsheet\",\n    \"spreadsheet\",\n  );\n  if (!sheetName || sheetCount == null || sheetCount <= 1) {\n    return `${baseName}.csv`;\n  }\n  return `${baseName}.${sanitizeFileNamePart(sheetName)}.csv`;\n}\n\nexport function useXlsxDownloadActions({\n  resource,\n  activeSheet,\n  activeSheetIndex,\n  sheets,\n  content,\n}: {\n  resource: ViewerResource;\n  activeSheet: XlsxSheetMeta | null;\n  activeSheetIndex: number;\n  sheets: XlsxSheetMeta[] | null;\n  content: ViewerResourceContent;\n}) {\n  return React.useMemo(() => {\n    const originalDownloadAction = {\n      ...resource.originalDownload,\n      label: activeSheet ? \"Download original\" : \"Download\",\n    };\n    if (!activeSheet || !sheets) return [originalDownloadAction];\n    return [\n      originalDownloadAction,\n      createXlsxSheetCsvExportAction({\n        fileName: xlsxSheetCsvFileName({\n          fileName: resource.fileName,\n          sheetName: activeSheet.name,\n          sheetCount: sheets.length,\n        }),\n        sheetIndex: activeSheetIndex,\n        getSource: () => getXlsxSource(content),\n      }),\n    ];\n  }, [\n    activeSheet,\n    activeSheetIndex,\n    content,\n    resource.fileName,\n    resource.originalDownload,\n    sheets,\n  ]);\n}\n\nfunction serializeXlsxSheetAsCsv({\n  source,\n  sheetIndex,\n  signal,\n}: {\n  source: XlsxSource;\n  sheetIndex: number;\n  signal?: AbortSignal;\n}) {\n  const sheet = source.sheets[sheetIndex];\n  if (!sheet) return \"\";\n  const rowCount = normalizeSheetDimension(sheet.rowCount);\n  const columnCount = normalizeSheetDimension(sheet.columnCount);\n  if (rowCount === 0 || columnCount === 0) return \"\";\n  if (rowCount * columnCount > MAX_DENSE_CSV_EXPORT_CELLS) return \"\";\n\n  const columns = Array.from({ length: columnCount }, (_, columnIndex) =>\n    xlsxColumnLabel(columnIndex),\n  );\n  const sourceRows = Array.from({ length: rowCount }, (_, rowIndex) => {\n    throwIfAborted(signal);\n    return Array.from({ length: columnCount }, (_, columnIndex) => {\n      const text = source.getCell(sheetIndex, rowIndex, columnIndex).text;\n      throwIfAborted(signal);\n      return text;\n    });\n  });\n\n  return serializeCsvTable({ columns, sourceRows, dialect: CSV_DIALECT });\n}\n\nfunction normalizeSheetDimension(value: number) {\n  return Number.isFinite(value) && value > 0 ? Math.floor(value) : 0;\n}\n\nfunction sanitizeFileNamePart(value: string, fallback = \"sheet\") {\n  const sanitized = value.trim().replace(/[\\x00-\\x1f\\x7f/\\\\?%*:|\"<>]+/g, \"-\");\n  return /[^\\s.-]/u.test(sanitized) ? sanitized : fallback;\n}\n\nfunction throwIfAborted(signal: AbortSignal | undefined) {\n  if (signal?.aborted) {\n    throw new DOMException(\"Download was cancelled.\", \"AbortError\");\n  }\n}\n",
      "type": "registry:ui",
      "target": "@ui/xlsx-viewer-download.ts"
    },
    {
      "path": "registry/new-york-v4/ui/csv-viewer-download.ts",
      "content": "import {\n  isTabDelimited,\n  normalizeCsvDelimiter,\n  type CsvDialect,\n} from \"@/lib/csv\";\nimport type { ViewerDownloadAction } from \"@/lib/viewer-download-actions\";\nimport type { ViewerResource } from \"@/lib/viewer-resource\";\n\nexport function escapeDelimitedField(value: string, delimiter: string): string {\n  const text = value ?? \"\";\n  return text.includes(delimiter) || /[\"\\r\\n]/.test(text)\n    ? `\"${text.replace(/\"/g, '\"\"')}\"`\n    : text;\n}\n\nexport function serializeCsvTable({\n  columns,\n  sourceRows,\n  dialect,\n}: {\n  columns: string[];\n  sourceRows: string[][];\n  dialect: CsvDialect;\n}): string {\n  const delimiter =\n    normalizeCsvDelimiter(dialect.delimiter) ?? dialect.delimiter;\n  const lines = [\n    columns\n      .map((value) => escapeDelimitedField(value, delimiter))\n      .join(delimiter),\n  ];\n  for (const sourceRow of sourceRows) {\n    const row = fitExportRow(sourceRow, columns.length);\n    lines.push(\n      row\n        .map((value) => escapeDelimitedField(value, delimiter))\n        .join(delimiter),\n    );\n  }\n  return lines.join(\"\\r\\n\");\n}\n\nfunction fitExportRow(row: string[], columnCount: number): string[] {\n  const out = row.slice(0, columnCount);\n  while (out.length < columnCount) out.push(\"\");\n  return out;\n}\n\nexport function defaultCsvDownloadName(dialect: CsvDialect): string {\n  return isTabDelimited(dialect) ? \"data.tsv\" : \"data.csv\";\n}\n\nexport function createCsvExportAction({\n  columns,\n  sourceRows,\n  dialect,\n  fileName,\n  isDisabled,\n}: {\n  columns: string[];\n  sourceRows: string[][];\n  dialect: CsvDialect;\n  fileName: string;\n  isDisabled?: boolean;\n}): ViewerDownloadAction {\n  return {\n    id: \"csv-export-table\",\n    label: \"Export table\",\n    fileName,\n    origin: \"derived\",\n    isDisabled,\n    getPayload: () => ({\n      kind: \"text\",\n      text: serializeCsvTable({ columns, sourceRows, dialect }),\n      mimeType: isTabDelimited(dialect)\n        ? \"text/tab-separated-values;charset=utf-8\"\n        : \"text/csv;charset=utf-8\",\n    }),\n  };\n}\n\nexport function csvViewerDownloadActions({\n  resource,\n  columns,\n  sourceRows,\n  dialect,\n  fileName,\n  canExportTable,\n}: {\n  resource: ViewerResource | null;\n  columns: string[];\n  sourceRows: string[][];\n  dialect: CsvDialect;\n  fileName: string;\n  canExportTable: boolean;\n}): ViewerDownloadAction[] {\n  const actions: ViewerDownloadAction[] = [];\n  if (resource) {\n    actions.push({\n      ...resource.originalDownload,\n      label: \"Download original\",\n    });\n  }\n  actions.push(\n    createCsvExportAction({\n      columns,\n      sourceRows,\n      dialect,\n      fileName,\n      isDisabled: !canExportTable,\n    }),\n  );\n  return actions;\n}\n",
      "type": "registry:ui",
      "target": "@ui/csv-viewer-download.ts"
    },
    {
      "path": "registry/new-york-v4/ui/xlsx-grid.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { useKeyedMountEffect } from \"@/hooks/use-keyed-mount-effect\";\nimport { xlsxColumnLabel, type XlsxCell } from \"@/lib/xlsx-workbook\";\nimport { fixedGridColumnWidths } from \"@/components/ui/fixed-grid-columns\";\nimport { getFixedGridCanvasStyle } from \"@/components/ui/fixed-grid-layout\";\nimport {\n  FixedGridNativeFindIndex,\n  type FixedGridNativeFindCellAddress,\n} from \"@/components/ui/fixed-grid-native-find-index\";\nimport { FixedGridRowWindow } from \"@/components/ui/fixed-grid-row-window\";\nimport type { GridCellCoordinate } from \"@/components/ui/fixed-grid-selection\";\nimport { buildVirtualGridTemplate } from \"@/components/ui/fixed-grid-template\";\nimport { FixedGridViewport } from \"@/components/ui/fixed-grid-viewport\";\nimport {\n  useFixedGridVirtualization,\n  useFixedRowPool,\n  type FixedGridRowPoolSlot,\n} from \"@/components/ui/fixed-grid-virtualization\";\nimport { HeaderAwareScrollbar } from \"@/components/ui/header-aware-scrollbar\";\nimport {\n  XLSX_BASE_COLUMN_WIDTH,\n  XLSX_BASE_FONT_SIZE,\n  XLSX_BASE_GUTTER_WIDTH,\n  XLSX_BASE_ROW_HEIGHT,\n} from \"@/components/ui/xlsx-grid-constants\";\nimport {\n  Spacer,\n  XlsxGridRow,\n  type XlsxGridColumnItem,\n} from \"@/components/ui/xlsx-grid-row\";\nimport { XLSX_SCROLLBAR_CSS } from \"@/components/ui/xlsx-grid-scrollbar\";\nimport { ScrollerShell } from \"@/components/ui/xlsx-shadow-scope\";\nimport {\n  useXlsxRowPatcher,\n  type XlsxRowPatchState,\n} from \"@/components/ui/xlsx-viewer-row-patcher\";\n\nimport { joinEffectKey } from \"@/lib/effect-key\";\n\nexport { XlsxGridSkeleton } from \"@/components/ui/xlsx-grid-skeleton\";\n\nconst ROW_OVERSCAN = 4;\nconst JUMP_ROW_OVERSCAN = 0;\nconst COLUMN_OVERSCAN = 2;\nconst JUMP_COLUMN_OVERSCAN = 0;\nconst XLSX_NATIVE_FIND_MAX_INDEXED_CELLS = 250_000;\n\nexport interface XlsxScrollRequest {\n  sheetIndex: number;\n  rowIndex: number;\n  columnIndex: number;\n  behavior: ScrollBehavior;\n  nonce: number;\n}\n\ntype XlsxGridCellRef = GridCellCoordinate;\n\nexport function XlsxGrid({\n  rowCount,\n  columnCount,\n  getCell,\n  sheetName,\n  scale,\n  activeCell,\n  scrollRequest,\n  isolateStyles,\n  viewportRef,\n}: {\n  rowCount: number;\n  columnCount: number;\n  sheetName: string;\n  getCell: (rowIndex: number, columnIndex: number) => XlsxCell;\n  scale: number;\n  activeCell?: XlsxGridCellRef | null;\n  scrollRequest?: XlsxScrollRequest | null;\n  isolateStyles: boolean;\n  viewportRef?: React.RefObject<HTMLDivElement | null>;\n}) {\n  const safeRowCount = normalizeGridCount(rowCount);\n  const safeColumnCount = normalizeGridCount(columnCount);\n  const safeScale = normalizeGridScale(scale);\n  const rowHeight = Math.round(XLSX_BASE_ROW_HEIGHT * safeScale);\n  const columnWidth = Math.round(XLSX_BASE_COLUMN_WIDTH * safeScale);\n  const gutterWidth = Math.round(XLSX_BASE_GUTTER_WIDTH * safeScale);\n  const fontSize = XLSX_BASE_FONT_SIZE * safeScale;\n\n  const scrollRef = React.useRef<HTMLDivElement>(null);\n  const rowOffsetRef = React.useRef<HTMLDivElement>(null);\n  const rowWindowRef = React.useRef<HTMLDivElement>(null);\n  const viewportClientHeightRef = React.useRef(0);\n  const setScrollElement = React.useCallback(\n    (element: HTMLDivElement | null) => {\n      scrollRef.current = element;\n      if (viewportRef) viewportRef.current = element;\n    },\n    [viewportRef],\n  );\n  const columnItemsRef = React.useRef<XlsxGridColumnItem[]>([]);\n  const getRowPatchState = React.useCallback(\n    (): XlsxRowPatchState => ({\n      activeCell: activeCell ?? null,\n      columnCount: safeColumnCount,\n      columnItems: columnItemsRef.current,\n      getCell,\n      rowCount: safeRowCount,\n      rowHeight,\n      sheetName,\n      viewportHeight: viewportClientHeightRef.current,\n    }),\n    [activeCell, getCell, rowHeight, safeColumnCount, safeRowCount, sheetName],\n  );\n  const rowPatcher = useXlsxRowPatcher({\n    rowOffsetRef,\n    rowWindowRef,\n    getState: getRowPatchState,\n  });\n  const rowScrollStrategy = React.useMemo(\n    () => ({ handleViewport: rowPatcher.patch }),\n    [rowPatcher],\n  );\n\n  const {\n    virtualRows,\n    totalRowSize,\n    totalColumnSize,\n    columnItems: virtualColumnItems,\n    virtualRowWindow,\n    leftPad,\n    rightPad,\n    scrollToCell,\n    viewportClientHeight,\n  } = useFixedGridVirtualization({\n    rowCount: safeRowCount,\n    columnCount: safeColumnCount,\n    rowSize: rowHeight,\n    columnSize: columnWidth,\n    rowOverscan: ROW_OVERSCAN,\n    columnOverscan: COLUMN_OVERSCAN,\n    jumpRowOverscan: JUMP_ROW_OVERSCAN,\n    jumpColumnOverscan: JUMP_COLUMN_OVERSCAN,\n    minimumRenderedRows: 1,\n    rowScrollStrategy,\n    scrollRef,\n  });\n  viewportClientHeightRef.current = viewportClientHeight;\n\n  const requestNonce = scrollRequest?.nonce;\n  useKeyedMountEffect(\n    scrollRequest ? joinEffectKey([\"xlsx-scroll-request\", requestNonce]) : null,\n    () => {\n      if (!scrollRequest) return;\n      scrollToCell({\n        rowIndex: scrollRequest.rowIndex,\n        columnIndex: scrollRequest.columnIndex,\n        behavior: scrollRequest.behavior,\n        align: \"center\",\n      });\n    },\n  );\n\n  const columnItems = React.useMemo(\n    () =>\n      virtualColumnItems.map((item) => ({\n        key: String(item.index),\n        widthPx: item.widthPx,\n        metadata: { columnIndex: item.index },\n      })) satisfies XlsxGridColumnItem[],\n    [virtualColumnItems],\n  );\n\n  columnItemsRef.current = columnItems;\n\n  const gridTemplate = React.useMemo(\n    () =>\n      buildVirtualGridTemplate({\n        leadingWidth: gutterWidth,\n        leftPad,\n        columnWidths: fixedGridColumnWidths(columnItems),\n        rightPad,\n      }),\n    [gutterWidth, leftPad, columnItems, rightPad],\n  );\n  const totalWidth = gutterWidth + totalColumnSize;\n  const minimumRowPoolSize =\n    Math.ceil(viewportClientHeight / rowHeight) + ROW_OVERSCAN * 2 + 2;\n  const rowPoolSlots = useFixedRowPool({\n    minimumPoolSize: minimumRowPoolSize,\n    rowCount: safeRowCount,\n    virtualRows: virtualRowWindow.items,\n  });\n  const nativeFindCellText = React.useCallback(\n    (rowIndex: number, columnIndex: number) =>\n      getCell(rowIndex, columnIndex).text,\n    [getCell],\n  );\n  const scrollToNativeFindCell = React.useCallback(\n    ({ rowIndex, columnIndex }: FixedGridNativeFindCellAddress) => {\n      scrollToCell({\n        rowIndex,\n        columnIndex,\n        behavior: \"auto\",\n        align: \"center\",\n      });\n    },\n    [scrollToCell],\n  );\n\n  useKeyedMountEffect(\n    joinEffectKey([\n      \"xlsx-row-patcher\",\n      rowPatcher,\n      virtualRows,\n      columnItems,\n      rowHeight,\n      safeRowCount,\n      safeColumnCount,\n      sheetName,\n      getCell,\n      activeCell,\n    ]),\n    () => {\n      rowPatcher.resync(virtualRows);\n    },\n  );\n\n  if (safeRowCount === 0 || safeColumnCount === 0) {\n    return (\n      <div\n        className=\"bg-card text-muted-foreground flex flex-1 items-center justify-center text-xs\"\n        role=\"status\"\n        aria-label={`${sheetName} is empty`}\n      >\n        Empty sheet\n      </div>\n    );\n  }\n\n  return (\n    <div\n      className=\"bg-card flex min-h-0 flex-1 flex-col\"\n      style={{ fontSize }}\n      data-slot=\"xlsx-grid\"\n    >\n      <ScrollerShell\n        isolate={isolateStyles}\n        className=\"relative min-h-0 flex-1\"\n      >\n        <style>{XLSX_SCROLLBAR_CSS}</style>\n        <FixedGridNativeFindIndex\n          rowCount={safeRowCount}\n          columnCount={safeColumnCount}\n          getCellText={nativeFindCellText}\n          onCellMatch={scrollToNativeFindCell}\n          dataSlot=\"xlsx-native-find-index\"\n          maxIndexedCells={XLSX_NATIVE_FIND_MAX_INDEXED_CELLS}\n        />\n        <FixedGridViewport\n          scrollRef={setScrollElement}\n          dataSlot=\"xlsx-body\"\n          role=\"grid\"\n          aria-label={sheetName}\n          aria-rowcount={safeRowCount}\n          aria-colcount={safeColumnCount}\n          tabIndex={0}\n        >\n          <div\n            style={getFixedGridCanvasStyle({\n              width: totalWidth,\n              contain: true,\n            })}\n          >\n            <div\n              className=\"sticky top-0 z-20 grid border-b\"\n              style={{\n                gridTemplateColumns: gridTemplate,\n                height: rowHeight,\n                backgroundColor:\n                  \"color-mix(in oklab, var(--card) 92%, var(--foreground))\",\n              }}\n              aria-hidden\n            >\n              <div\n                className=\"sticky left-0 z-10 border-r bg-[color-mix(in_oklab,var(--card)_94%,var(--foreground))]\"\n                style={{ height: rowHeight }}\n              />\n              <Spacer width={leftPad} />\n              {columnItems.map((item) => (\n                <div\n                  key={item.key}\n                  className=\"text-muted-foreground flex items-center justify-center border-r font-medium last:border-r-0\"\n                >\n                  {xlsxColumnLabel(item.metadata.columnIndex)}\n                </div>\n              ))}\n              <Spacer width={rightPad} />\n            </div>\n\n            <FixedGridRowWindow\n              rowOffsetRef={rowOffsetRef}\n              rowWindowRef={rowWindowRef}\n              totalSize={totalRowSize}\n              virtualRowWindow={virtualRowWindow}\n              viewportHeight={viewportClientHeight}\n              offsetDataSlot=\"xlsx-row-offset\"\n              windowDataSlot=\"xlsx-row-window\"\n            >\n              {rowPoolSlots.map((slot) => (\n                <XlsxGridRowSlot\n                  key={slot.slotIndex}\n                  slot={slot}\n                  rowCount={safeRowCount}\n                  getCell={getCell}\n                  gridTemplate={gridTemplate}\n                  rowHeight={rowHeight}\n                  columnItems={columnItems}\n                  leftPad={leftPad}\n                  rightPad={rightPad}\n                  activeCell={activeCell ?? null}\n                />\n              ))}\n            </FixedGridRowWindow>\n          </div>\n        </FixedGridViewport>\n        <HeaderAwareScrollbar scrollRef={scrollRef} headerHeight={rowHeight} />\n      </ScrollerShell>\n    </div>\n  );\n}\n\nfunction XlsxGridRowSlot({\n  slot,\n  rowCount,\n  getCell,\n  gridTemplate,\n  rowHeight,\n  columnItems,\n  leftPad,\n  rightPad,\n  activeCell,\n}: {\n  slot: FixedGridRowPoolSlot;\n  rowCount: number;\n  getCell: (rowIndex: number, columnIndex: number) => XlsxCell;\n  gridTemplate: string;\n  rowHeight: number;\n  columnItems: XlsxGridColumnItem[];\n  leftPad: number;\n  rightPad: number;\n  activeCell: XlsxGridCellRef | null;\n}) {\n  const fallbackRowIndex =\n    rowCount > 0 ? Math.min(slot.slotIndex, rowCount - 1) : 0;\n  const rowIndex = slot.virtualRow?.index ?? fallbackRowIndex;\n\n  return (\n    <XlsxGridRow\n      rowIndex={rowIndex}\n      getCell={getCell}\n      gridTemplate={gridTemplate}\n      rowHeight={rowHeight}\n      columnItems={columnItems}\n      leftPad={leftPad}\n      rightPad={rightPad}\n      start={slot.virtualRow?.start ?? 0}\n      hidden={slot.isHidden}\n      activeColumnIndex={\n        slot.virtualRow && activeCell?.rowIndex === rowIndex\n          ? activeCell.columnIndex\n          : null\n      }\n    />\n  );\n}\n\nfunction normalizeGridCount(value: number) {\n  return Number.isFinite(value) && value > 0 && value <= Number.MAX_SAFE_INTEGER\n    ? Math.floor(value)\n    : 0;\n}\n\nfunction normalizeGridScale(value: number) {\n  if (!Number.isFinite(value) || value <= 0) return 1;\n  return Math.min(5, Math.max(0.1, value));\n}\n",
      "type": "registry:ui",
      "target": "@ui/xlsx-grid.tsx"
    },
    {
      "path": "registry/new-york-v4/ui/fixed-grid-virtualization.ts",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { useKeyedMountEffect } from \"@/hooks/use-keyed-mount-effect\";\nimport { useKeyedLayoutEffect } from \"@/hooks/use-keyed-layout-effect\";\nimport { joinEffectKey } from \"@/lib/effect-key\";\n\nconst MINIMUM_ROW_WINDOW = 32;\nconst INITIAL_COLUMN_WINDOW = 8;\nconst MAX_VIRTUAL_ITEMS = 10_000;\nconst MAX_EAGER_COLUMN_ITEMS = 10_000;\n\nexport interface FixedGridColumnItem {\n  index: number;\n  widthPx: number;\n}\n\nexport interface FixedGridVirtualItem {\n  index: number;\n  start: number;\n  size: number;\n  end: number;\n}\n\nexport interface FixedGridRowPoolSlot {\n  slotIndex: number;\n  virtualRow: FixedGridVirtualItem | null;\n  isHidden: boolean;\n}\n\nexport interface FixedGridVirtualItemWindow {\n  end: number;\n  items: FixedGridVirtualItem[];\n  size: number;\n  start: number;\n}\n\nexport interface FixedGridScrollTarget {\n  rowIndex: number;\n  columnIndex: number;\n  align?: \"start\" | \"center\" | \"end\" | \"auto\";\n  behavior?: ScrollBehavior;\n}\n\nexport interface FixedRowScrollTarget {\n  rowIndex: number;\n  align?: \"start\" | \"center\" | \"end\" | \"auto\";\n  behavior?: ScrollBehavior;\n}\n\nexport type FixedGridJumpViewportResult = \"handled\" | \"pass\";\n\nexport interface FixedGridRowScrollStrategy {\n  settleAfterMs?: number;\n  handleViewport: (viewport: FixedGridViewport) => FixedGridJumpViewportResult;\n}\n\nexport function useFixedRowPool({\n  minimumPoolSize = 0,\n  rowCount,\n  virtualRows,\n}: {\n  minimumPoolSize?: number;\n  rowCount: number;\n  virtualRows: FixedGridVirtualItem[];\n}): FixedGridRowPoolSlot[] {\n  const poolSizeRef = React.useRef(0);\n  const safeRowCount = fixedItemCount(rowCount);\n  const safeMinimumPoolSize = fixedItemCount(minimumPoolSize);\n  const nextPoolSize = Math.min(\n    safeRowCount,\n    Math.max(poolSizeRef.current, virtualRows.length, safeMinimumPoolSize),\n  );\n  poolSizeRef.current = nextPoolSize;\n\n  return React.useMemo(\n    () =>\n      Array.from({ length: nextPoolSize }, (_, slotIndex) => {\n        const virtualRow = virtualRows[slotIndex] ?? null;\n        return {\n          slotIndex,\n          virtualRow,\n          isHidden: !virtualRow,\n        };\n      }),\n    [nextPoolSize, virtualRows],\n  );\n}\n\nexport function useFixedGridVirtualization({\n  rowCount,\n  columnCount,\n  rowSize,\n  columnSize,\n  rowOverscan,\n  columnOverscan,\n  jumpRowOverscan = rowOverscan,\n  jumpColumnOverscan = columnOverscan,\n  minimumRenderedRows = MINIMUM_ROW_WINDOW,\n  rowScrollStrategy,\n  scrollRef,\n  scrollElement,\n  virtualizeColumns = true,\n}: {\n  rowCount: number;\n  columnCount: number;\n  rowSize: number;\n  columnSize: number;\n  rowOverscan: number;\n  columnOverscan: number;\n  jumpRowOverscan?: number;\n  jumpColumnOverscan?: number;\n  minimumRenderedRows?: number;\n  rowScrollStrategy?: FixedGridRowScrollStrategy;\n  scrollRef: React.RefObject<HTMLElement | null>;\n  scrollElement?: HTMLElement | null;\n  virtualizeColumns?: boolean;\n}) {\n  const resolvedScrollElement = useResolvedScrollElement({\n    scrollRef,\n    scrollElement,\n  });\n  const viewport = useFixedGridViewport(\n    resolvedScrollElement,\n    rowScrollStrategy,\n    {\n      rowCount,\n      columnCount,\n      rowSize,\n      columnSize,\n    },\n  );\n\n  const totalRowSize = fixedTotalSize(rowCount, rowSize);\n  const totalColumnSize = fixedTotalSize(columnCount, columnSize);\n  const activeRowOverscan = viewport.isJumpingRows\n    ? jumpRowOverscan\n    : rowOverscan;\n  const activeColumnOverscan =\n    viewport.isJumpingColumns || viewport.isJumpingRows\n      ? jumpColumnOverscan\n      : columnOverscan;\n\n  const virtualRows = React.useMemo(\n    () =>\n      fixedVirtualItems({\n        count: rowCount,\n        size: rowSize,\n        scrollOffset: viewport.scrollTop,\n        viewportSize: viewport.clientHeight,\n        overscan: activeRowOverscan,\n        minimumVisibleCount: minimumRenderedRows,\n      }),\n    [\n      rowCount,\n      rowSize,\n      viewport.scrollTop,\n      viewport.clientHeight,\n      activeRowOverscan,\n      minimumRenderedRows,\n    ],\n  );\n  const virtualRowWindow = React.useMemo(\n    () => fixedVirtualItemWindow(virtualRows),\n    [virtualRows],\n  );\n\n  const columnWindow = React.useMemo<{\n    columnItems: FixedGridColumnItem[];\n    leftPad: number;\n    rightPad: number;\n  }>(() => {\n    if (!virtualizeColumns) {\n      const safeColumnCount = fixedItemCount(columnCount);\n      const safeColumnSize = fixedItemSize(columnSize);\n      if (safeColumnCount > MAX_EAGER_COLUMN_ITEMS) {\n        return {\n          columnItems: [],\n          leftPad: 0,\n          rightPad: 0,\n        };\n      }\n      return {\n        columnItems: Array.from({ length: safeColumnCount }, (_, index) => ({\n          index,\n          widthPx: safeColumnSize,\n        })),\n        leftPad: 0,\n        rightPad: 0,\n      };\n    }\n\n    const virtualColumns = fixedVirtualItems({\n      count: columnCount,\n      size: columnSize,\n      scrollOffset: viewport.scrollLeft,\n      viewportSize: viewport.clientWidth,\n      overscan: activeColumnOverscan,\n      minimumVisibleCount: INITIAL_COLUMN_WINDOW,\n    });\n\n    return {\n      columnItems: virtualColumns.map((item) => ({\n        index: item.index,\n        widthPx: item.size,\n      })),\n      leftPad: virtualColumns.length ? virtualColumns[0].start : 0,\n      rightPad: virtualColumns.length\n        ? totalColumnSize - virtualColumns[virtualColumns.length - 1].end\n        : 0,\n    };\n  }, [\n    virtualizeColumns,\n    columnCount,\n    columnSize,\n    viewport.scrollLeft,\n    viewport.clientWidth,\n    activeColumnOverscan,\n    totalColumnSize,\n  ]);\n\n  const scrollToCell = React.useCallback(\n    ({\n      rowIndex,\n      columnIndex,\n      align = \"center\",\n      behavior = \"smooth\",\n    }: FixedGridScrollTarget) => {\n      const scrollElement = scrollRef.current;\n      if (!scrollElement) return;\n      const top = fixedScrollOffset({\n        index: rowIndex,\n        itemSize: rowSize,\n        viewportSize: scrollElement.clientHeight,\n        align,\n      });\n      const left = fixedScrollOffset({\n        index: columnIndex,\n        itemSize: columnSize,\n        viewportSize: scrollElement.clientWidth,\n        align,\n      });\n      if (typeof scrollElement.scrollTo === \"function\") {\n        scrollElement.scrollTo({ top, left, behavior });\n      } else {\n        scrollElement.scrollTop = top;\n        scrollElement.scrollLeft = left;\n      }\n    },\n    [columnSize, rowSize, scrollRef],\n  );\n\n  return {\n    virtualRows,\n    virtualRowWindow,\n    totalRowSize,\n    totalColumnSize,\n    scrollToCell,\n    isJumpingRows: viewport.isJumpingRows,\n    isJumpingColumns: viewport.isJumpingColumns,\n    viewportClientHeight: viewport.clientHeight,\n    ...columnWindow,\n  };\n}\n\nexport function useFixedRowVirtualization({\n  rowCount,\n  rowSize,\n  rowOverscan,\n  jumpRowOverscan = rowOverscan,\n  initialViewportHeight = 0,\n  scrollRef,\n  scrollElement,\n}: {\n  rowCount: number;\n  rowSize: number;\n  rowOverscan: number;\n  jumpRowOverscan?: number;\n  initialViewportHeight?: number;\n  scrollRef: React.RefObject<HTMLElement | null>;\n  scrollElement?: HTMLElement | null;\n}) {\n  const resolvedScrollElement = useResolvedScrollElement({\n    scrollRef,\n    scrollElement,\n  });\n  const [range, setRange] = React.useState(() =>\n    initialViewportHeight > 0\n      ? fixedRowRange({\n          rowCount,\n          rowSize,\n          scrollTop: 0,\n          viewportHeight: initialViewportHeight,\n          rowOverscan,\n        })\n      : { start: 0, end: 0 },\n  );\n  const rangeRef = React.useRef(range);\n  const rafRef = React.useRef(0);\n  const totalRowSize = fixedTotalSize(rowCount, rowSize);\n  const [viewportClientHeight, setViewportClientHeight] = React.useState(() =>\n    fixedViewportMetric(initialViewportHeight),\n  );\n\n  const setMeasuredRange = React.useCallback((next: typeof range) => {\n    const current = rangeRef.current;\n    if (current.start === next.start && current.end === next.end) return;\n    rangeRef.current = next;\n    setRange(next);\n  }, []);\n  const setMeasuredViewportHeight = React.useCallback((next: number) => {\n    setViewportClientHeight((current) => (current === next ? current : next));\n  }, []);\n\n  const measure = React.useCallback(() => {\n    const scrollElement = resolvedScrollElement;\n    const safeRowCount = fixedItemCount(rowCount);\n    const safeRowSize = fixedItemSize(rowSize);\n    if (!scrollElement || safeRowCount <= 0 || safeRowSize <= 0) {\n      setMeasuredRange({ start: 0, end: 0 });\n      return;\n    }\n\n    const scrollTop =\n      Number.isFinite(scrollElement.scrollTop) && scrollElement.scrollTop > 0\n        ? scrollElement.scrollTop\n        : 0;\n    const viewportHeight =\n      Number.isFinite(scrollElement.clientHeight) &&\n      scrollElement.clientHeight > 0\n        ? scrollElement.clientHeight\n        : fixedViewportMetric(initialViewportHeight);\n    setMeasuredViewportHeight(viewportHeight);\n    const firstVisibleRow = clamp(\n      Math.floor(scrollTop / safeRowSize),\n      0,\n      safeRowCount - 1,\n    );\n    const visibleRowCount = Math.ceil(viewportHeight / safeRowSize);\n    const previous = rangeRef.current;\n    const isJumping =\n      Math.abs(firstVisibleRow - previous.start) > visibleRowCount * 0.45;\n    const activeOverscan = fixedOverscan(\n      isJumping ? jumpRowOverscan : rowOverscan,\n    );\n    const uncappedStart = Math.max(0, firstVisibleRow - activeOverscan);\n    const uncappedEnd = Math.min(\n      safeRowCount,\n      firstVisibleRow + visibleRowCount + activeOverscan,\n    );\n    const { start, end } = capVirtualRange({\n      uncappedStart,\n      uncappedEnd,\n      visibleStart: firstVisibleRow,\n      visibleEnd: Math.min(safeRowCount, firstVisibleRow + visibleRowCount),\n      maxItems: MAX_VIRTUAL_ITEMS,\n    });\n\n    if (previous.end > safeRowCount || previous.start >= safeRowCount) {\n      setMeasuredRange({ start, end });\n      return;\n    }\n\n    const bufferRows = Math.max(1, Math.floor(activeOverscan / 2));\n    const visibleStart = firstVisibleRow;\n    const visibleEnd = Math.min(\n      safeRowCount,\n      firstVisibleRow + visibleRowCount,\n    );\n    const hasBeforeBuffer =\n      previous.start === 0 || visibleStart >= previous.start + bufferRows;\n    const hasAfterBuffer =\n      previous.end === safeRowCount || visibleEnd <= previous.end - bufferRows;\n\n    if (hasBeforeBuffer && hasAfterBuffer) return;\n    setMeasuredRange({ start, end });\n  }, [\n    jumpRowOverscan,\n    initialViewportHeight,\n    rowCount,\n    rowOverscan,\n    rowSize,\n    resolvedScrollElement,\n    setMeasuredRange,\n    setMeasuredViewportHeight,\n  ]);\n\n  useKeyedLayoutEffect(joinEffectKey([measure]), () => {\n    measure();\n  });\n\n  useKeyedMountEffect(joinEffectKey([resolvedScrollElement, measure]), () => {\n    const scrollElement = resolvedScrollElement;\n    if (!scrollElement) return;\n\n    const scheduleMeasure = () => {\n      if (rafRef.current) return;\n      let didRun = false;\n      const frame = requestAnimationFrame(() => {\n        didRun = true;\n        rafRef.current = 0;\n        measure();\n      });\n      rafRef.current = didRun ? 0 : frame;\n    };\n\n    scrollElement.addEventListener(\"scroll\", scheduleMeasure, {\n      passive: true,\n    });\n    const observer =\n      typeof ResizeObserver !== \"undefined\"\n        ? new ResizeObserver(scheduleMeasure)\n        : null;\n    observer?.observe(scrollElement);\n    return () => {\n      if (rafRef.current) cancelAnimationFrame(rafRef.current);\n      scrollElement.removeEventListener(\"scroll\", scheduleMeasure);\n      observer?.disconnect();\n    };\n  });\n\n  const virtualRows = React.useMemo(\n    () =>\n      Array.from({ length: range.end - range.start }, (_, offset) => {\n        const index = range.start + offset;\n        const size = fixedItemSize(rowSize);\n        const start = index * size;\n        return {\n          index,\n          start,\n          size,\n          end: start + size,\n        };\n      }),\n    [range, rowSize],\n  );\n  const virtualRowWindow = React.useMemo(\n    () => fixedVirtualItemWindow(virtualRows),\n    [virtualRows],\n  );\n\n  const scrollToRow = React.useCallback(\n    ({\n      rowIndex,\n      align = \"center\",\n      behavior = \"smooth\",\n    }: FixedRowScrollTarget) => {\n      const scrollElement = scrollRef.current;\n      if (!scrollElement) return;\n      const top = fixedScrollOffset({\n        index: rowIndex,\n        itemSize: rowSize,\n        viewportSize: scrollElement.clientHeight,\n        align,\n      });\n      if (typeof scrollElement.scrollTo === \"function\") {\n        scrollElement.scrollTo({ top, behavior });\n      } else {\n        scrollElement.scrollTop = top;\n      }\n    },\n    [rowSize, scrollRef],\n  );\n\n  return {\n    virtualRows,\n    virtualRowWindow,\n    totalRowSize,\n    viewportClientHeight,\n    scrollToRow,\n  };\n}\n\nfunction fixedRowRange({\n  rowCount,\n  rowSize,\n  scrollTop,\n  viewportHeight,\n  rowOverscan,\n}: {\n  rowCount: number;\n  rowSize: number;\n  scrollTop: number;\n  viewportHeight: number;\n  rowOverscan: number;\n}) {\n  const safeRowCount = fixedItemCount(rowCount);\n  const safeRowSize = fixedItemSize(rowSize);\n  if (safeRowCount <= 0 || safeRowSize <= 0) return { start: 0, end: 0 };\n\n  const safeScrollTop =\n    Number.isFinite(scrollTop) && scrollTop > 0 ? scrollTop : 0;\n  const safeViewportHeight = fixedViewportMetric(viewportHeight);\n  const firstVisibleRow = clamp(\n    Math.floor(safeScrollTop / safeRowSize),\n    0,\n    safeRowCount - 1,\n  );\n  const visibleRowCount = Math.ceil(safeViewportHeight / safeRowSize);\n  const activeOverscan = fixedOverscan(rowOverscan);\n  const uncappedStart = Math.max(0, firstVisibleRow - activeOverscan);\n  const uncappedEnd = Math.min(\n    safeRowCount,\n    firstVisibleRow + visibleRowCount + activeOverscan,\n  );\n  return capVirtualRange({\n    uncappedStart,\n    uncappedEnd,\n    visibleStart: firstVisibleRow,\n    visibleEnd: Math.min(safeRowCount, firstVisibleRow + visibleRowCount),\n    maxItems: MAX_VIRTUAL_ITEMS,\n  });\n}\n\nexport interface FixedGridViewport {\n  scrollTop: number;\n  scrollLeft: number;\n  clientHeight: number;\n  clientWidth: number;\n  isJumpingRows: boolean;\n  isJumpingColumns: boolean;\n}\n\ninterface FixedGridLayoutMetrics {\n  rowCount: number;\n  columnCount: number;\n  rowSize: number;\n  columnSize: number;\n}\n\ninterface FixedGridReadingAnchor {\n  rowIndex: number;\n  columnIndex: number;\n  rowOffsetPx: number;\n  columnOffsetPx: number;\n}\n\ninterface FixedVirtualWindow {\n  count: number;\n  size: number;\n  scrollOffset: number;\n  viewportSize: number;\n  overscan: number;\n  minimumVisibleCount?: number;\n}\n\nconst emptyFixedGridViewport: FixedGridViewport = {\n  scrollTop: 0,\n  scrollLeft: 0,\n  clientHeight: 0,\n  clientWidth: 0,\n  isJumpingRows: false,\n  isJumpingColumns: false,\n};\n\nfunction useResolvedScrollElement({\n  scrollRef,\n  scrollElement,\n}: {\n  scrollRef: React.RefObject<HTMLElement | null>;\n  scrollElement?: HTMLElement | null;\n}) {\n  const [resolvedScrollElement, setResolvedScrollElement] =\n    React.useState<HTMLElement | null>(scrollElement ?? scrollRef.current);\n\n  useKeyedLayoutEffect(\n    joinEffectKey([\n      scrollRef,\n      scrollRef.current,\n      scrollElement,\n      resolvedScrollElement,\n    ]),\n    () => {\n      const nextScrollElement = scrollElement ?? scrollRef.current;\n      if (resolvedScrollElement !== nextScrollElement) {\n        setResolvedScrollElement(nextScrollElement);\n      }\n    },\n  );\n\n  return resolvedScrollElement;\n}\n\nfunction useFixedGridViewport(\n  scrollElement: HTMLElement | null | undefined,\n  rowScrollStrategy?: FixedGridRowScrollStrategy,\n  layoutMetrics?: FixedGridLayoutMetrics,\n) {\n  const [viewport, setViewport] = React.useState<FixedGridViewport>(\n    emptyFixedGridViewport,\n  );\n  const committedLayoutMetricsRef = React.useRef<FixedGridLayoutMetrics | null>(\n    layoutMetrics ?? null,\n  );\n\n  useKeyedLayoutEffect(\n    joinEffectKey([\n      scrollElement,\n      rowScrollStrategy,\n      layoutMetrics?.rowCount,\n      layoutMetrics?.columnCount,\n      layoutMetrics?.rowSize,\n      layoutMetrics?.columnSize,\n    ]),\n    () => {\n      const previousLayoutMetrics = committedLayoutMetricsRef.current;\n      committedLayoutMetricsRef.current = layoutMetrics ?? null;\n\n      if (!scrollElement) {\n        setViewport((current) =>\n          fixedGridViewportEqual(current, emptyFixedGridViewport)\n            ? current\n            : emptyFixedGridViewport,\n        );\n        return;\n      }\n\n      let frame = 0;\n      let settleTimeout = 0;\n      let lastScrollTop = scrollElement.scrollTop;\n      let lastScrollLeft = scrollElement.scrollLeft;\n\n      if (\n        previousLayoutMetrics &&\n        layoutMetrics &&\n        didFixedGridItemSizeChange(previousLayoutMetrics, layoutMetrics)\n      ) {\n        const anchor = captureFixedGridReadingAnchor({\n          layoutMetrics: previousLayoutMetrics,\n          scrollElement,\n        });\n        restoreFixedGridReadingAnchor({\n          anchor,\n          layoutMetrics,\n          scrollElement,\n        });\n        lastScrollTop = scrollElement.scrollTop;\n        lastScrollLeft = scrollElement.scrollLeft;\n      }\n\n      const commitViewport = (next: FixedGridViewport) => {\n        setViewport((current) => {\n          return fixedGridViewportEqual(current, next) ? current : next;\n        });\n      };\n\n      const commitSettledViewport = () => {\n        commitViewport({\n          scrollTop: fixedViewportMetric(scrollElement.scrollTop),\n          scrollLeft: fixedViewportMetric(scrollElement.scrollLeft),\n          clientHeight: fixedViewportMetric(scrollElement.clientHeight),\n          clientWidth: fixedViewportMetric(scrollElement.clientWidth),\n          isJumpingRows: false,\n          isJumpingColumns: false,\n        });\n      };\n\n      const scheduleSettledViewport = () => {\n        if (settleTimeout) window.clearTimeout(settleTimeout);\n        settleTimeout = window.setTimeout(() => {\n          settleTimeout = 0;\n          // Scrolling has quiesced: re-read the live scroll metrics so the\n          // canonical React window matches where the grid actually came to rest,\n          // then clear jump flags so settled windows use the full overscan.\n          commitSettledViewport();\n        }, rowScrollStrategy?.settleAfterMs ?? 80);\n      };\n\n      const readViewport = () => {\n        frame = 0;\n        const scrollTop = fixedViewportMetric(scrollElement.scrollTop);\n        const scrollLeft = fixedViewportMetric(scrollElement.scrollLeft);\n        const clientHeight = fixedViewportMetric(scrollElement.clientHeight);\n        const clientWidth = fixedViewportMetric(scrollElement.clientWidth);\n        const rowDelta = Math.abs(scrollTop - lastScrollTop);\n        const columnDelta = Math.abs(scrollLeft - lastScrollLeft);\n        lastScrollTop = scrollTop;\n        lastScrollLeft = scrollLeft;\n\n        const next: FixedGridViewport = {\n          scrollTop,\n          scrollLeft,\n          clientHeight,\n          clientWidth,\n          isJumpingRows: rowDelta > clientHeight * 0.45,\n          isJumpingColumns: columnDelta > clientWidth * 0.45,\n        };\n\n        if (\n          rowDelta > 0 &&\n          rowScrollStrategy?.handleViewport(next) === \"handled\"\n        ) {\n          scheduleSettledViewport();\n          return;\n        }\n\n        commitViewport(next);\n        if (next.isJumpingRows || next.isJumpingColumns) {\n          scheduleSettledViewport();\n          return;\n        }\n        if (settleTimeout) {\n          window.clearTimeout(settleTimeout);\n          settleTimeout = 0;\n        }\n      };\n\n      const scheduleRead = () => {\n        if (frame) return;\n        let didRun = false;\n        const nextFrame = requestAnimationFrame(() => {\n          didRun = true;\n          readViewport();\n        });\n        frame = didRun ? 0 : nextFrame;\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) cancelAnimationFrame(frame);\n        if (settleTimeout) window.clearTimeout(settleTimeout);\n        scrollElement.removeEventListener(\"scroll\", scheduleRead);\n        observer?.disconnect();\n      };\n    },\n  );\n\n  return viewport;\n}\n\nfunction didFixedGridItemSizeChange(\n  previous: FixedGridLayoutMetrics,\n  next: FixedGridLayoutMetrics,\n) {\n  return (\n    previous.rowSize !== next.rowSize || previous.columnSize !== next.columnSize\n  );\n}\n\nfunction captureFixedGridReadingAnchor({\n  layoutMetrics,\n  scrollElement,\n}: {\n  layoutMetrics: FixedGridLayoutMetrics;\n  scrollElement: HTMLElement;\n}): FixedGridReadingAnchor {\n  const rowCount = fixedItemCount(layoutMetrics.rowCount);\n  const columnCount = fixedItemCount(layoutMetrics.columnCount);\n  const rowSize = fixedItemSize(layoutMetrics.rowSize);\n  const columnSize = fixedItemSize(layoutMetrics.columnSize);\n  const scrollTop = fixedViewportMetric(scrollElement.scrollTop);\n  const scrollLeft = fixedViewportMetric(scrollElement.scrollLeft);\n  const rowIndex =\n    rowCount > 0 && rowSize > 0\n      ? clamp(Math.floor(scrollTop / rowSize), 0, rowCount - 1)\n      : 0;\n  const columnIndex =\n    columnCount > 0 && columnSize > 0\n      ? clamp(Math.floor(scrollLeft / columnSize), 0, columnCount - 1)\n      : 0;\n\n  return {\n    rowIndex,\n    columnIndex,\n    rowOffsetPx: Math.max(0, scrollTop - rowIndex * rowSize),\n    columnOffsetPx: Math.max(0, scrollLeft - columnIndex * columnSize),\n  };\n}\n\nfunction restoreFixedGridReadingAnchor({\n  anchor,\n  layoutMetrics,\n  scrollElement,\n}: {\n  anchor: FixedGridReadingAnchor;\n  layoutMetrics: FixedGridLayoutMetrics;\n  scrollElement: HTMLElement;\n}) {\n  const rowCount = fixedItemCount(layoutMetrics.rowCount);\n  const columnCount = fixedItemCount(layoutMetrics.columnCount);\n  const rowSize = fixedItemSize(layoutMetrics.rowSize);\n  const columnSize = fixedItemSize(layoutMetrics.columnSize);\n  const rowIndex = rowCount > 0 ? clamp(anchor.rowIndex, 0, rowCount - 1) : 0;\n  const columnIndex =\n    columnCount > 0 ? clamp(anchor.columnIndex, 0, columnCount - 1) : 0;\n\n  scrollElement.scrollTop =\n    rowIndex * rowSize + Math.min(anchor.rowOffsetPx, Math.max(0, rowSize - 1));\n  scrollElement.scrollLeft =\n    columnIndex * columnSize +\n    Math.min(anchor.columnOffsetPx, Math.max(0, columnSize - 1));\n}\n\nexport function fixedVirtualItems({\n  count,\n  size,\n  scrollOffset,\n  viewportSize,\n  overscan,\n  minimumVisibleCount = 1,\n}: FixedVirtualWindow): FixedGridVirtualItem[] {\n  if (!Number.isFinite(count) || !Number.isFinite(size)) return [];\n  const itemCount = Math.floor(count);\n  if (itemCount <= 0 || size <= 0) return [];\n  const safeScrollOffset =\n    Number.isFinite(scrollOffset) && scrollOffset > 0 ? scrollOffset : 0;\n  const safeViewportSize = Number.isFinite(viewportSize) ? viewportSize : 0;\n  const safeOverscan =\n    Number.isFinite(overscan) && overscan > 0 ? Math.floor(overscan) : 0;\n  const safeMinimumVisibleCount =\n    Number.isFinite(minimumVisibleCount) && minimumVisibleCount > 0\n      ? Math.ceil(minimumVisibleCount)\n      : 1;\n  const effectiveViewportSize = Math.max(\n    safeViewportSize,\n    size * safeMinimumVisibleCount,\n  );\n  const visibleStart = clamp(\n    Math.floor(safeScrollOffset / size),\n    0,\n    itemCount - 1,\n  );\n  const visibleEnd = clamp(\n    Math.ceil((safeScrollOffset + effectiveViewportSize) / size),\n    visibleStart,\n    itemCount - 1,\n  );\n  const uncappedStart = Math.max(0, visibleStart - safeOverscan);\n  const uncappedEndInclusive = Math.min(\n    itemCount - 1,\n    visibleEnd + safeOverscan,\n  );\n  const { start, end } = capVirtualRange({\n    uncappedStart,\n    uncappedEnd: uncappedEndInclusive + 1,\n    visibleStart,\n    visibleEnd: visibleEnd + 1,\n    maxItems: MAX_VIRTUAL_ITEMS,\n  });\n  return Array.from({ length: end - start }, (_, offset) => {\n    const index = start + offset;\n    const itemStart = index * size;\n    return {\n      index,\n      start: itemStart,\n      size,\n      end: itemStart + size,\n    };\n  });\n}\n\nexport function fixedVirtualItemWindow(\n  items: readonly FixedGridVirtualItem[],\n): FixedGridVirtualItemWindow {\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\nfunction capVirtualRange({\n  uncappedStart,\n  uncappedEnd,\n  visibleStart,\n  visibleEnd,\n  maxItems,\n}: {\n  uncappedStart: number;\n  uncappedEnd: number;\n  visibleStart: number;\n  visibleEnd: number;\n  maxItems: number;\n}) {\n  const length = uncappedEnd - uncappedStart;\n  if (length <= maxItems) return { start: uncappedStart, end: uncappedEnd };\n\n  const visibleLength = Math.max(0, visibleEnd - visibleStart);\n  if (visibleLength >= maxItems) {\n    return {\n      start: visibleStart,\n      end: visibleStart + maxItems,\n    };\n  }\n\n  const remaining = maxItems - visibleLength;\n  const before = Math.min(\n    visibleStart - uncappedStart,\n    Math.floor(remaining / 2),\n  );\n  let start = visibleStart - before;\n  let end = start + maxItems;\n\n  if (end > uncappedEnd) {\n    end = uncappedEnd;\n    start = Math.max(uncappedStart, end - maxItems);\n  }\n\n  return { start, end };\n}\n\nfunction clamp(value: number, min: number, max: number) {\n  return Math.min(max, Math.max(min, value));\n}\n\nfunction fixedTotalSize(count: number, size: number) {\n  if (\n    !Number.isFinite(count) ||\n    !Number.isFinite(size) ||\n    count <= 0 ||\n    size <= 0\n  ) {\n    return 0;\n  }\n  const totalSize = Math.floor(count) * size;\n  return Number.isFinite(totalSize) ? totalSize : 0;\n}\n\nfunction fixedItemCount(count: number) {\n  return Number.isFinite(count) && count > 0 ? Math.floor(count) : 0;\n}\n\nfunction fixedItemSize(size: number) {\n  return Number.isFinite(size) && size > 0 ? size : 0;\n}\n\nfunction fixedOverscan(overscan: number) {\n  return Number.isFinite(overscan) && overscan > 0 ? Math.floor(overscan) : 0;\n}\n\nfunction fixedViewportMetric(value: number) {\n  return Number.isFinite(value) && value > 0 ? value : 0;\n}\n\nexport function fixedScrollOffset({\n  index,\n  itemSize,\n  viewportSize,\n  align,\n}: {\n  index: number;\n  itemSize: number;\n  viewportSize: number;\n  align: NonNullable<FixedGridScrollTarget[\"align\"]>;\n}) {\n  if (\n    !Number.isSafeInteger(index) ||\n    !Number.isFinite(itemSize) ||\n    !Number.isFinite(viewportSize) ||\n    index < 0 ||\n    itemSize <= 0 ||\n    viewportSize < 0\n  ) {\n    return 0;\n  }\n  const start = index * itemSize;\n  if (align === \"end\") return Math.max(0, start - viewportSize + itemSize);\n  if (align === \"center\") {\n    return Math.max(0, start - viewportSize / 2 + itemSize / 2);\n  }\n  return Math.max(0, start);\n}\n\nfunction fixedGridViewportEqual(\n  left: FixedGridViewport,\n  right: FixedGridViewport,\n) {\n  return (\n    left.scrollTop === right.scrollTop &&\n    left.scrollLeft === right.scrollLeft &&\n    left.clientHeight === right.clientHeight &&\n    left.clientWidth === right.clientWidth &&\n    left.isJumpingRows === right.isJumpingRows &&\n    left.isJumpingColumns === right.isJumpingColumns\n  );\n}\n",
      "type": "registry:ui",
      "target": "@ui/fixed-grid-virtualization.ts"
    },
    {
      "path": "registry/new-york-v4/ui/fixed-grid-row-style.ts",
      "content": "import type * as React from \"react\";\n\nexport function getFixedGridRowStyle({\n  gridTemplate,\n  rowHeight,\n  top,\n  contain = true,\n}: {\n  gridTemplate?: string;\n  rowHeight: number;\n  top: number;\n  contain?: boolean;\n}): React.CSSProperties {\n  const safeRowHeight =\n    Number.isFinite(rowHeight) && rowHeight > 0 ? rowHeight : 0;\n  const safeTop = Number.isFinite(top) && top > 0 ? top : 0;\n  const style: React.CSSProperties = {\n    height: safeRowHeight,\n    minHeight: safeRowHeight,\n    position: \"absolute\",\n    top: 0,\n    left: 0,\n    width: \"100%\",\n    transform: `translate3d(0, ${safeTop}px, 0)`,\n  };\n  if (gridTemplate?.trim()) style.gridTemplateColumns = gridTemplate;\n  if (contain) style.contain = \"layout paint style\";\n  return style;\n}\n",
      "type": "registry:ui",
      "target": "@ui/fixed-grid-row-style.ts"
    },
    {
      "path": "registry/new-york-v4/ui/fixed-grid-template.ts",
      "content": "export function buildVirtualGridTemplate({\n  leadingWidth,\n  leftPad,\n  columnWidths,\n  rightPad,\n}: {\n  leadingWidth: number;\n  leftPad: number;\n  columnWidths: readonly number[];\n  rightPad: number;\n}) {\n  const columns = columnWidths.map(formatTemplateWidth).join(\" \");\n  return [\n    formatTemplateWidth(leadingWidth),\n    formatTemplateWidth(leftPad),\n    columns,\n    formatTemplateWidth(rightPad),\n  ]\n    .filter(Boolean)\n    .join(\" \");\n}\n\nfunction formatTemplateWidth(width: number) {\n  return `${Number.isFinite(width) && width > 0 ? width : 0}px`;\n}\n",
      "type": "registry:ui",
      "target": "@ui/fixed-grid-template.ts"
    },
    {
      "path": "registry/new-york-v4/ui/fixed-grid-columns.ts",
      "content": "export interface FixedGridColumn<Metadata = unknown> {\n  key: string;\n  widthPx: number;\n  metadata?: Metadata;\n}\n\nexport function buildFixedGridColumns<Item, Metadata = unknown>({\n  items,\n  getKey,\n  getWidthPx,\n  getMetadata,\n}: {\n  items: readonly Item[];\n  getKey: (item: Item, index: number) => string;\n  getWidthPx: (item: Item, index: number) => number;\n  getMetadata?: (item: Item, index: number) => Metadata | undefined;\n}): FixedGridColumn<Metadata>[] {\n  return items.map((item, index) => {\n    const metadata = getMetadata?.(item, index);\n    const widthPx = normalizeFixedGridColumnWidth(getWidthPx(item, index));\n    return metadata === undefined\n      ? {\n          key: getKey(item, index),\n          widthPx,\n        }\n      : {\n          key: getKey(item, index),\n          widthPx,\n          metadata,\n        };\n  });\n}\n\nexport function fixedGridColumnWidths(\n  columns: readonly Pick<FixedGridColumn, \"widthPx\">[],\n) {\n  return columns.map((column) => normalizeFixedGridColumnWidth(column.widthPx));\n}\n\nfunction normalizeFixedGridColumnWidth(widthPx: number) {\n  return Number.isFinite(widthPx) && widthPx > 0 ? widthPx : 0;\n}\n",
      "type": "registry:ui",
      "target": "@ui/fixed-grid-columns.ts"
    },
    {
      "path": "registry/new-york-v4/ui/fixed-grid-layout.ts",
      "content": "import type * as React from \"react\";\n\nexport type CssLength = number | string;\ntype CssLengthProperty = \"height\" | \"marginTop\" | \"minWidth\" | \"width\";\n\nexport interface FixedGridInverseRowWindowGeometry {\n  size: number;\n  start: number;\n}\n\nexport function getFixedGridCanvasStyle({\n  width,\n  minWidth = \"100%\",\n  contain = false,\n}: {\n  width?: CssLength;\n  minWidth?: CssLength;\n  contain?: boolean;\n}): React.CSSProperties {\n  return {\n    position: \"relative\",\n    ...cssLengthProperty(\"width\", width),\n    ...cssLengthProperty(\"minWidth\", minWidth),\n    ...(contain ? { contain: \"layout paint style\" } : null),\n  };\n}\n\nexport function getFixedGridRowWindowStyle({\n  height,\n  minWidth,\n}: {\n  height: CssLength;\n  minWidth?: CssLength;\n}): React.CSSProperties {\n  return {\n    position: \"relative\",\n    ...cssLengthProperty(\"height\", height),\n    ...cssLengthProperty(\"minWidth\", minWidth),\n  };\n}\n\nexport function getFixedGridInverseRowOffsetStyle({\n  height,\n  minWidth,\n}: {\n  height: CssLength;\n  minWidth?: CssLength;\n}): React.CSSProperties {\n  return {\n    ...cssLengthProperty(\"height\", height),\n    ...cssLengthProperty(\"minWidth\", minWidth),\n  };\n}\n\nexport function getFixedGridInverseStickyRowWindowStyle({\n  height,\n  minWidth,\n  viewportHeight,\n}: {\n  height: number;\n  minWidth?: CssLength;\n  viewportHeight: number;\n}): React.CSSProperties {\n  const stickyOffset = fixedGridInverseStickyOffset({\n    viewportSize: viewportHeight,\n    windowSize: height,\n  });\n\n  return {\n    position: \"sticky\",\n    ...cssLengthProperty(\"height\", height),\n    ...cssLengthProperty(\"minWidth\", minWidth),\n    top: `${stickyOffset}px`,\n    bottom: `${stickyOffset}px`,\n  };\n}\n\nexport function getFixedGridInverseRowWindowStyles({\n  minWidth,\n  rowMinWidth,\n  totalSize,\n  viewportHeight,\n  window,\n}: {\n  minWidth?: CssLength;\n  rowMinWidth?: CssLength;\n  totalSize: CssLength;\n  viewportHeight: number;\n  window: FixedGridInverseRowWindowGeometry;\n}): {\n  offsetStyle: React.CSSProperties;\n  spacerStyle: React.CSSProperties;\n  windowStyle: React.CSSProperties;\n} {\n  return {\n    offsetStyle: getFixedGridInverseRowOffsetStyle({\n      height: window.start,\n      minWidth: rowMinWidth,\n    }),\n    spacerStyle: getFixedGridRowWindowStyle({\n      height: totalSize,\n      minWidth,\n    }),\n    windowStyle: getFixedGridInverseStickyRowWindowStyle({\n      height: window.size,\n      minWidth: rowMinWidth,\n      viewportHeight,\n    }),\n  };\n}\n\nexport function getFixedGridInverseRowWindowStyle({\n  height,\n  minWidth,\n  top,\n  viewportHeight,\n}: {\n  height: number;\n  minWidth?: CssLength;\n  top: number;\n  viewportHeight: number;\n}): React.CSSProperties {\n  const stickyOffset = fixedGridInverseStickyOffset({\n    viewportSize: viewportHeight,\n    windowSize: height,\n  });\n\n  return {\n    position: \"sticky\",\n    ...cssLengthProperty(\"height\", height),\n    ...cssLengthProperty(\"marginTop\", top),\n    ...cssLengthProperty(\"minWidth\", minWidth),\n    top: `${stickyOffset}px`,\n    bottom: `${stickyOffset}px`,\n  };\n}\n\nexport function fixedGridInverseStickyOffset({\n  viewportSize,\n  windowSize,\n}: {\n  viewportSize: number;\n  windowSize: number;\n}) {\n  const offset = Math.max(\n    0,\n    safeCssNumber(windowSize) - safeCssNumber(viewportSize),\n  );\n  return offset === 0 ? 0 : -offset;\n}\n\nexport function setFixedGridInverseRowWindowGeometry({\n  rowOffsetElement,\n  rowWindowElement,\n  viewportHeight,\n  window,\n}: {\n  rowOffsetElement: HTMLElement;\n  rowWindowElement: HTMLElement;\n  viewportHeight: number;\n  window: FixedGridInverseRowWindowGeometry;\n}) {\n  const offsetStyle = getFixedGridInverseRowOffsetStyle({\n    height: window.start,\n  });\n  const windowStyle = getFixedGridInverseStickyRowWindowStyle({\n    height: window.size,\n    viewportHeight,\n  });\n  patchStyleProperties(rowOffsetElement.style, offsetStyle, [\"height\"]);\n  patchStyleProperties(rowWindowElement.style, windowStyle, [\n    \"position\",\n    \"height\",\n    \"top\",\n    \"bottom\",\n  ]);\n  setStyleValue(rowWindowElement.style, \"margin-top\", \"\");\n}\n\nfunction formatCssLength(value: CssLength | undefined) {\n  if (typeof value === \"string\") return value.trim() ? value : undefined;\n  if (typeof value !== \"number\") return value;\n  return Number.isFinite(value) && value >= 0 ? `${value}px` : undefined;\n}\n\nfunction cssLengthProperty<Property extends CssLengthProperty>(\n  property: Property,\n  value: CssLength | undefined,\n): Pick<React.CSSProperties, Property> | object {\n  const formattedValue = formatCssLength(value);\n  return formattedValue === undefined ? {} : { [property]: formattedValue };\n}\n\nfunction safeCssNumber(value: number) {\n  return Number.isFinite(value) && value > 0 ? value : 0;\n}\n\nfunction patchStyleProperties(\n  style: CSSStyleDeclaration,\n  values: React.CSSProperties,\n  properties: string[],\n) {\n  for (const property of properties) {\n    const value = values[property as keyof React.CSSProperties];\n    setStyleValue(\n      style,\n      property,\n      typeof value === \"number\" ? `${value}px` : value,\n    );\n  }\n}\n\nfunction setStyleValue(\n  style: CSSStyleDeclaration,\n  propertyName: string,\n  value: unknown,\n) {\n  const nextValue = typeof value === \"string\" ? value : \"\";\n  if (style.getPropertyValue(propertyName) !== nextValue) {\n    style.setProperty(propertyName, nextValue);\n  }\n}\n",
      "type": "registry:ui",
      "target": "@ui/fixed-grid-layout.ts"
    },
    {
      "path": "registry/new-york-v4/ui/fixed-grid-selection.ts",
      "content": "export interface GridCellCoordinate {\n  rowIndex: number;\n  columnIndex: number;\n}\n\nexport function isSameGridCell(\n  left: GridCellCoordinate | null | undefined,\n  right: GridCellCoordinate | null | undefined,\n) {\n  return (\n    isValidGridCellCoordinate(left) &&\n    isValidGridCellCoordinate(right) &&\n    left.rowIndex === right.rowIndex &&\n    left.columnIndex === right.columnIndex\n  );\n}\n\nexport function gridCellKey({ rowIndex, columnIndex }: GridCellCoordinate) {\n  if (!isValidGridCellCoordinate({ rowIndex, columnIndex })) return null;\n  return `${rowIndex}:${columnIndex}`;\n}\n\nexport function parseGridCellKey(key: string): GridCellCoordinate | null {\n  const [rowIndexText, columnIndexText, extra] = key.split(\":\");\n  if (extra !== undefined) return null;\n  if (!rowIndexText || !columnIndexText) return null;\n  if (\n    !isUnsignedIntegerText(rowIndexText) ||\n    !isUnsignedIntegerText(columnIndexText)\n  ) {\n    return null;\n  }\n\n  const rowIndex = Number(rowIndexText);\n  const columnIndex = Number(columnIndexText);\n  if (!isValidGridCellCoordinate({ rowIndex, columnIndex })) {\n    return null;\n  }\n\n  return { rowIndex, columnIndex };\n}\n\nfunction isUnsignedIntegerText(value: string) {\n  return /^(0|[1-9]\\d*)$/.test(value);\n}\n\nfunction isValidGridCellCoordinate(\n  coordinate: GridCellCoordinate | null | undefined,\n): coordinate is GridCellCoordinate {\n  return (\n    !!coordinate &&\n    Number.isSafeInteger(coordinate.rowIndex) &&\n    Number.isSafeInteger(coordinate.columnIndex) &&\n    coordinate.rowIndex >= 0 &&\n    coordinate.columnIndex >= 0\n  );\n}\n",
      "type": "registry:ui",
      "target": "@ui/fixed-grid-selection.ts"
    },
    {
      "path": "registry/new-york-v4/ui/fixed-grid-viewport.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nexport interface FixedGridViewportRefs {\n  scrollElement: HTMLDivElement | null;\n}\n\nexport interface FixedGridViewportProps\n  extends Omit<React.HTMLAttributes<HTMLDivElement>, \"children\"> {\n  scrollRef: React.Ref<HTMLDivElement>;\n  dataSlot: string;\n  children: React.ReactNode;\n}\n\nexport function FixedGridViewport({\n  scrollRef,\n  dataSlot,\n  className = \"absolute inset-0 overflow-auto outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-inset\",\n  children,\n  ...props\n}: FixedGridViewportProps) {\n  return (\n    <div ref={scrollRef} data-slot={dataSlot} className={className} {...props}>\n      {children}\n    </div>\n  );\n}\n",
      "type": "registry:ui",
      "target": "@ui/fixed-grid-viewport.tsx"
    },
    {
      "path": "registry/new-york-v4/ui/header-aware-scrollbar.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { useKeyedMountEffect } from \"@/hooks/use-keyed-mount-effect\";\nimport { useKeyedLayoutEffect } from \"@/hooks/use-keyed-layout-effect\";\nimport { joinEffectKey } from \"@/lib/effect-key\";\n\nexport function HeaderAwareScrollbar({\n  scrollRef,\n  headerHeight,\n}: {\n  scrollRef: React.RefObject<HTMLDivElement | null>;\n  headerHeight: number;\n}) {\n  const scrollElement = useResolvedScrollbarElement(scrollRef);\n  const [isVisible, setIsVisible] = React.useState(false);\n  const thumbRef = React.useRef<HTMLDivElement>(null);\n  const thumbMetrics = React.useRef({ height: 0, top: 0 });\n  const drag = React.useRef<{ y: number; scroll: number } | null>(null);\n  const frame = React.useRef(0);\n\n  const measure = React.useCallback(() => {\n    frame.current = 0;\n    if (!scrollElement) {\n      hideThumb(setIsVisible);\n      return;\n    }\n    const { scrollHeight, clientHeight, scrollTop } = scrollElement;\n    if (\n      !Number.isFinite(scrollHeight) ||\n      !Number.isFinite(clientHeight) ||\n      !Number.isFinite(scrollTop) ||\n      !Number.isFinite(headerHeight)\n    ) {\n      hideThumb(setIsVisible);\n      return;\n    }\n    const track = clientHeight - headerHeight;\n    if (scrollHeight <= clientHeight + 1 || track <= 0) {\n      hideThumb(setIsVisible);\n      return;\n    }\n    const height = Math.max(28, (clientHeight / scrollHeight) * track);\n    const maxScroll = scrollHeight - clientHeight;\n    const maxTop = track - height;\n    const top =\n      maxScroll > 0\n        ? clampScrollTop((scrollTop / maxScroll) * maxTop, maxTop)\n        : 0;\n    thumbMetrics.current = { height, top };\n    applyThumbStyle(thumbRef.current, thumbMetrics.current);\n    setIsVisible((current) => (current ? current : true));\n  }, [scrollElement, headerHeight]);\n\n  const scheduleMeasure = React.useCallback(() => {\n    if (frame.current) return;\n    frame.current = requestAnimationFrame(measure);\n  }, [measure]);\n\n  useKeyedMountEffect(\n    joinEffectKey([scrollElement, measure, scheduleMeasure]),\n    () => {\n      if (!scrollElement) {\n        hideThumb(setIsVisible);\n        return;\n      }\n      measure();\n      scrollElement.addEventListener(\"scroll\", scheduleMeasure, {\n        passive: true,\n      });\n      const observer =\n        typeof ResizeObserver !== \"undefined\"\n          ? new ResizeObserver(scheduleMeasure)\n          : null;\n      observer?.observe(scrollElement);\n      return () => {\n        if (frame.current) cancelAnimationFrame(frame.current);\n        scrollElement.removeEventListener(\"scroll\", scheduleMeasure);\n        observer?.disconnect();\n      };\n    },\n  );\n\n  useKeyedLayoutEffect(joinEffectKey([isVisible]), () => {\n    applyThumbStyle(thumbRef.current, thumbMetrics.current);\n  });\n\n  const onPointerDown = (event: React.PointerEvent<HTMLDivElement>) => {\n    if (!scrollElement) return;\n    event.preventDefault();\n    drag.current = { y: event.clientY, scroll: scrollElement.scrollTop };\n    event.currentTarget.setPointerCapture?.(event.pointerId);\n  };\n\n  const onPointerMove = (event: React.PointerEvent<HTMLDivElement>) => {\n    const currentDrag = drag.current;\n    if (!scrollElement || !currentDrag) return;\n    const track = scrollElement.clientHeight - headerHeight;\n    const height = Math.max(\n      28,\n      (scrollElement.clientHeight / scrollElement.scrollHeight) * track,\n    );\n    const denominator = track - height;\n    if (denominator <= 0) return;\n    const maxScroll = scrollElement.scrollHeight - scrollElement.clientHeight;\n    scrollElement.scrollTop = clampScrollTop(\n      currentDrag.scroll +\n        ((event.clientY - currentDrag.y) / denominator) * maxScroll,\n      maxScroll,\n    );\n  };\n\n  const endDrag = (event: React.PointerEvent<HTMLDivElement>) => {\n    drag.current = null;\n    event.currentTarget.releasePointerCapture?.(event.pointerId);\n  };\n\n  if (!isVisible) return null;\n  return (\n    <div\n      aria-hidden\n      className=\"pointer-events-none absolute right-0 z-30 w-2.5\"\n      style={{ top: headerHeight, bottom: 0 }}\n    >\n      <div\n        ref={thumbRef}\n        className=\"bg-foreground/25 hover:bg-foreground/40 pointer-events-auto absolute right-0.5 w-1.5 rounded-full transition-colors\"\n        style={{\n          top: 0,\n          height: thumbMetrics.current.height,\n          transform: `translateY(${thumbMetrics.current.top}px)`,\n        }}\n        onPointerDown={onPointerDown}\n        onPointerMove={onPointerMove}\n        onPointerUp={endDrag}\n        onPointerCancel={endDrag}\n      />\n    </div>\n  );\n}\n\nfunction useResolvedScrollbarElement(\n  scrollRef: React.RefObject<HTMLDivElement | null>,\n) {\n  const [scrollElement, setScrollElement] = React.useState(scrollRef.current);\n\n  useKeyedLayoutEffect(\n    joinEffectKey([scrollRef, scrollRef.current, scrollElement]),\n    () => {\n      const nextScrollElement = scrollRef.current;\n      if (scrollElement !== nextScrollElement) {\n        setScrollElement(nextScrollElement);\n      }\n    },\n  );\n\n  return scrollElement;\n}\n\nfunction hideThumb(setThumb: React.Dispatch<React.SetStateAction<boolean>>) {\n  setThumb((current) => (current ? false : current));\n}\n\nfunction clampScrollTop(value: number, maxScroll: number) {\n  if (!Number.isFinite(value)) return 0;\n  if (!Number.isFinite(maxScroll) || maxScroll <= 0) return 0;\n  return Math.min(maxScroll, Math.max(0, value));\n}\n\nfunction applyThumbStyle(\n  element: HTMLDivElement | null,\n  metrics: { height: number; top: number },\n) {\n  if (!element) return;\n  element.style.height = `${metrics.height}px`;\n  element.style.transform = `translateY(${metrics.top}px)`;\n}\n",
      "type": "registry:ui",
      "target": "@ui/header-aware-scrollbar.tsx"
    },
    {
      "path": "registry/new-york-v4/ui/viewer-scrollbar-css.ts",
      "content": "export function viewerScrollbarCss(slotName: string) {\n  return `\n[data-slot=\"${slotName}\"]::-webkit-scrollbar { width: 10px; height: 10px; }\n[data-slot=\"${slotName}\"]::-webkit-scrollbar:vertical { display: none; }\n[data-slot=\"${slotName}\"]::-webkit-scrollbar-track { background: transparent; }\n[data-slot=\"${slotName}\"]::-webkit-scrollbar-thumb {\n  background-color: color-mix(in oklab, var(--foreground) 22%, transparent);\n  border-radius: 9999px;\n  border: 3px solid transparent;\n  background-clip: content-box;\n}\n[data-slot=\"${slotName}\"]::-webkit-scrollbar-thumb:hover {\n  background-color: color-mix(in oklab, var(--foreground) 38%, transparent);\n}\n`;\n}\n",
      "type": "registry:ui",
      "target": "@ui/viewer-scrollbar-css.ts"
    },
    {
      "path": "registry/new-york-v4/ui/xlsx-grid-constants.ts",
      "content": "export const XLSX_BASE_ROW_HEIGHT = 28;\nexport const XLSX_BASE_COLUMN_WIDTH = 128;\nexport const XLSX_BASE_GUTTER_WIDTH = 52;\nexport const XLSX_BASE_FONT_SIZE = 13;\n",
      "type": "registry:ui",
      "target": "@ui/xlsx-grid-constants.ts"
    },
    {
      "path": "registry/new-york-v4/ui/xlsx-grid-row.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\nimport type { XlsxCell } from \"@/lib/xlsx-workbook\";\nimport type { FixedGridColumn } from \"@/components/ui/fixed-grid-columns\";\nimport { getFixedGridRowStyle } from \"@/components/ui/fixed-grid-row-style\";\nimport type { GridCellCoordinate } from \"@/components/ui/fixed-grid-selection\";\n\nexport type XlsxGridColumnItem = FixedGridColumn<{ columnIndex: number }> & {\n  metadata: { columnIndex: number };\n};\n\nexport const XlsxGridRow = React.memo(function XlsxGridRow({\n  rowIndex,\n  getCell,\n  gridTemplate,\n  rowHeight,\n  columnItems,\n  leftPad,\n  rightPad,\n  start,\n  hidden = false,\n  activeColumnIndex,\n}: {\n  rowIndex: number;\n  getCell: (rowIndex: number, columnIndex: number) => XlsxCell;\n  gridTemplate: string;\n  rowHeight: number;\n  columnItems: XlsxGridColumnItem[];\n  leftPad: number;\n  rightPad: number;\n  start: number;\n  hidden?: boolean;\n  activeColumnIndex?: GridCellCoordinate[\"columnIndex\"] | null;\n}) {\n  const style = getFixedGridRowStyle({\n    gridTemplate,\n    rowHeight,\n    top: start,\n  });\n  return (\n    <div\n      className=\"group hover:bg-muted/40 grid border-b\"\n      style={style}\n      role=\"row\"\n      aria-rowindex={rowIndex + 1}\n      hidden={hidden}\n      data-slot=\"xlsx-row\"\n    >\n      <div\n        aria-hidden\n        data-slot=\"xlsx-row-number\"\n        className=\"bg-card text-muted-foreground sticky left-0 z-[1] flex items-center justify-end border-r px-2 tabular-nums group-hover:bg-[color-mix(in_oklab,var(--card)_97%,var(--foreground))]\"\n      >\n        {rowIndex + 1}\n      </div>\n      <Spacer width={leftPad} />\n      {columnItems.map((item) => {\n        const columnIndex = item.metadata.columnIndex;\n        const cell = getCell(rowIndex, columnIndex);\n        const isActive = activeColumnIndex === columnIndex;\n        return (\n          <div\n            key={item.key}\n            role=\"gridcell\"\n            aria-colindex={columnIndex + 1}\n            data-slot=\"xlsx-cell\"\n            className={cn(\n              \"flex items-center truncate border-r px-2 last:border-r-0\",\n              cell.numeric ? \"justify-end tabular-nums\" : \"justify-start\",\n              isActive && \"bg-primary/12 ring-primary/50 ring-1 ring-inset\",\n            )}\n          >\n            <span className=\"truncate\">{cell.text}</span>\n          </div>\n        );\n      })}\n      <Spacer width={rightPad} />\n    </div>\n  );\n});\n\nexport function Spacer({ width }: { width: number }) {\n  return <div aria-hidden style={{ width }} />;\n}\n",
      "type": "registry:ui",
      "target": "@ui/xlsx-grid-row.tsx"
    },
    {
      "path": "registry/new-york-v4/ui/xlsx-grid-scrollbar.tsx",
      "content": "import { viewerScrollbarCss } from \"./viewer-scrollbar-css\";\n\nexport { HeaderAwareScrollbar } from \"./header-aware-scrollbar\";\n\nexport const XLSX_SCROLLBAR_CSS = viewerScrollbarCss(\"xlsx-body\");\n",
      "type": "registry:ui",
      "target": "@ui/xlsx-grid-scrollbar.tsx"
    },
    {
      "path": "registry/new-york-v4/ui/xlsx-grid-skeleton.tsx",
      "content": "\"use client\";\n\nimport { Skeleton } from \"@/components/ui/skeleton\";\nimport {\n  XLSX_BASE_COLUMN_WIDTH,\n  XLSX_BASE_GUTTER_WIDTH,\n  XLSX_BASE_ROW_HEIGHT,\n} from \"@/components/ui/xlsx-grid-constants\";\n\nexport function XlsxGridSkeleton() {\n  const skeletonColumnCount = 6;\n  const skeletonRowCount = 18;\n  const widths = [70, 45, 88, 56, 62, 78];\n  return (\n    <div\n      className=\"bg-card flex h-full max-h-full min-h-0 flex-1 flex-col overflow-hidden\"\n      data-slot=\"xlsx-grid\"\n      aria-hidden\n    >\n      <div className=\"bg-muted/60 flex border-b\">\n        <div\n          className=\"shrink-0 border-r\"\n          style={{\n            width: XLSX_BASE_GUTTER_WIDTH,\n            height: XLSX_BASE_ROW_HEIGHT,\n          }}\n        />\n        {Array.from({ length: skeletonColumnCount }, (_, columnIndex) => (\n          <div\n            key={columnIndex}\n            className=\"flex shrink-0 items-center justify-center border-r\"\n            style={{\n              width: XLSX_BASE_COLUMN_WIDTH,\n              height: XLSX_BASE_ROW_HEIGHT,\n            }}\n          >\n            <Skeleton className=\"h-3 w-6\" />\n          </div>\n        ))}\n      </div>\n      <div className=\"min-h-0 flex-1 overflow-hidden\">\n        {Array.from({ length: skeletonRowCount }, (_, rowIndex) => (\n          <div\n            key={rowIndex}\n            className=\"flex border-b\"\n            style={{ height: XLSX_BASE_ROW_HEIGHT }}\n          >\n            <div\n              className=\"flex shrink-0 items-center justify-end border-r px-2\"\n              style={{ width: XLSX_BASE_GUTTER_WIDTH }}\n            >\n              <Skeleton className=\"h-3 w-4\" />\n            </div>\n            {Array.from({ length: skeletonColumnCount }, (_, columnIndex) => (\n              <div\n                key={columnIndex}\n                className=\"flex shrink-0 items-center border-r px-2\"\n                style={{ width: XLSX_BASE_COLUMN_WIDTH }}\n              >\n                <Skeleton\n                  className=\"h-3\"\n                  style={{\n                    width: `${widths[(rowIndex + columnIndex) % widths.length]}%`,\n                  }}\n                />\n              </div>\n            ))}\n          </div>\n        ))}\n      </div>\n    </div>\n  );\n}\n",
      "type": "registry:ui",
      "target": "@ui/xlsx-grid-skeleton.tsx"
    },
    {
      "path": "registry/new-york-v4/ui/xlsx-sheet-tabs.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { useKeyedMountEffect } from \"@/hooks/use-keyed-mount-effect\";\nimport { cn } from \"@/lib/utils\";\nimport type { XlsxSheetMeta } from \"@/lib/xlsx-workbook\";\n\nimport { joinEffectKey } from \"@/lib/effect-key\";\n\nexport const XLSX_SHEET_TABS_HEIGHT_PX = 36;\nconst TAB_HEIGHT_PX = 28;\nconst SCROLL_EPSILON_PX = 1;\nconst TAB_MIN_WIDTH_PX = 92;\nconst TAB_MAX_WIDTH_PX = 184;\nconst TAB_GAP_PX = 2;\nconst TAB_SCROLL_INLINE_PADDING_PX = 12;\nconst PREFERRED_VISIBLE_TABS = 6;\nconst LARGE_REVEAL_DISTANCE_MULTIPLIER = 1.25;\nconst OVERFLOW_REVEAL_DESKTOP_PX = 22;\nconst OVERFLOW_REVEAL_MOBILE_PX = 16;\nconst OVERFLOW_REVEAL_MIN_PX = 8;\n\ninterface SheetTabScrollState {\n  canScrollLeft: boolean;\n  canScrollRight: boolean;\n  isOverflowing: boolean;\n  viewportWidth: number;\n}\n\ninterface TabRevealGeometry {\n  activeIndex: number;\n  activeLeft: number;\n  activeWidth: number;\n  scrollLeft: number;\n  scrollWidth: number;\n  sheetCount: number;\n  viewportWidth: number;\n}\n\nfunction scrollTabsTo(\n  scrollElement: HTMLDivElement,\n  left: number,\n  behavior: ScrollBehavior,\n) {\n  const maxScrollLeft = Math.max(\n    0,\n    scrollElement.scrollWidth - scrollElement.clientWidth,\n  );\n  const clampedLeft = Math.min(maxScrollLeft, Math.max(0, left));\n\n  if (typeof scrollElement.scrollTo === \"function\") {\n    scrollElement.scrollTo({ left: clampedLeft, behavior });\n  } else {\n    scrollElement.scrollLeft = clampedLeft;\n  }\n}\n\nfunction resolveOverflowRevealPx(viewportWidth: number, tabWidth: number) {\n  const targetReveal = resolveOverflowRevealTargetPx(viewportWidth);\n  const maxTabReveal = Math.max(\n    OVERFLOW_REVEAL_MIN_PX,\n    Math.floor(tabWidth * 0.35),\n  );\n\n  return Math.max(OVERFLOW_REVEAL_MIN_PX, Math.min(targetReveal, maxTabReveal));\n}\n\nfunction resolveOverflowRevealTargetPx(viewportWidth: number) {\n  return viewportWidth <= 420\n    ? OVERFLOW_REVEAL_MOBILE_PX\n    : OVERFLOW_REVEAL_DESKTOP_PX;\n}\n\nfunction resolveTabRevealScrollLeft({\n  activeIndex,\n  activeLeft,\n  activeWidth,\n  scrollLeft,\n  scrollWidth,\n  sheetCount,\n  viewportWidth,\n}: TabRevealGeometry) {\n  const maxScrollLeft = Math.max(0, scrollWidth - viewportWidth);\n  if (maxScrollLeft <= SCROLL_EPSILON_PX) return 0;\n\n  if (activeIndex <= 0) return 0;\n  if (activeIndex >= sheetCount - 1) return maxScrollLeft;\n\n  const activeRight = activeLeft + activeWidth;\n  const overflowRevealPx = resolveOverflowRevealPx(viewportWidth, activeWidth);\n  const visibleLeft = scrollLeft + overflowRevealPx;\n  const visibleRight = scrollLeft + viewportWidth - overflowRevealPx;\n\n  let nextScrollLeft = scrollLeft;\n\n  if (activeLeft < visibleLeft) {\n    nextScrollLeft = activeLeft - overflowRevealPx;\n  } else if (activeRight > visibleRight) {\n    nextScrollLeft = activeRight - viewportWidth + overflowRevealPx;\n  }\n\n  return Math.min(maxScrollLeft, Math.max(0, nextScrollLeft));\n}\n\nfunction readScrollState(scrollElement: HTMLElement): SheetTabScrollState {\n  const maxScrollLeft = Math.max(\n    0,\n    scrollElement.scrollWidth - scrollElement.clientWidth,\n  );\n\n  return {\n    canScrollLeft: scrollElement.scrollLeft > SCROLL_EPSILON_PX,\n    canScrollRight:\n      scrollElement.scrollLeft < maxScrollLeft - SCROLL_EPSILON_PX,\n    isOverflowing: maxScrollLeft > SCROLL_EPSILON_PX,\n    viewportWidth: scrollElement.clientWidth,\n  };\n}\n\nfunction resolveTabWidth(viewportWidth: number, sheetCount: number) {\n  if (viewportWidth <= 0 || sheetCount <= 0) return undefined;\n\n  const preferredVisibleTabs = Math.min(sheetCount, PREFERRED_VISIBLE_TABS);\n  const isOverflowing = sheetCount > preferredVisibleTabs;\n  const overflowRevealPx = isOverflowing\n    ? resolveOverflowRevealTargetPx(viewportWidth)\n    : 0;\n  const visibleTabs = isOverflowing\n    ? Math.max(\n        1,\n        Math.min(\n          preferredVisibleTabs,\n          Math.floor(\n            (viewportWidth -\n              TAB_SCROLL_INLINE_PADDING_PX -\n              overflowRevealPx +\n              TAB_GAP_PX) /\n              (TAB_MIN_WIDTH_PX + TAB_GAP_PX),\n          ),\n        ),\n      )\n    : preferredVisibleTabs;\n  const gapCount = isOverflowing ? visibleTabs : Math.max(0, sheetCount - 1);\n  const availableWidth = Math.max(\n    0,\n    viewportWidth -\n      TAB_SCROLL_INLINE_PADDING_PX -\n      gapCount * TAB_GAP_PX -\n      overflowRevealPx,\n  );\n\n  return Math.round(\n    Math.min(\n      TAB_MAX_WIDTH_PX,\n      Math.max(TAB_MIN_WIDTH_PX, availableWidth / visibleTabs),\n    ),\n  );\n}\n\nexport function XlsxSheetTabs({\n  sheets,\n  activeSheetIndex,\n  onSelectSheet,\n}: {\n  sheets: XlsxSheetMeta[];\n  activeSheetIndex: number;\n  onSelectSheet: (sheetIndex: number) => void;\n}) {\n  const scrollRef = React.useRef<HTMLDivElement>(null);\n  const listRef = React.useRef<HTMLDivElement>(null);\n  const tabRefs = React.useRef<(HTMLButtonElement | null)[]>([]);\n  const [scrollState, setScrollState] = React.useState<SheetTabScrollState>({\n    canScrollLeft: false,\n    canScrollRight: false,\n    isOverflowing: false,\n    viewportWidth: 0,\n  });\n  const tabWidth = resolveTabWidth(scrollState.viewportWidth, sheets.length);\n\n  const updateScrollState = React.useCallback(() => {\n    const scrollElement = scrollRef.current;\n    if (!scrollElement) return;\n\n    const nextScrollState = readScrollState(scrollElement);\n\n    setScrollState((current) =>\n      current.canScrollLeft === nextScrollState.canScrollLeft &&\n      current.canScrollRight === nextScrollState.canScrollRight &&\n      current.isOverflowing === nextScrollState.isOverflowing &&\n      current.viewportWidth === nextScrollState.viewportWidth\n        ? current\n        : nextScrollState,\n    );\n  }, []);\n\n  const scrollTabsBy = React.useCallback(\n    (delta: number) => {\n      const scrollElement = scrollRef.current;\n      if (!scrollElement) return false;\n\n      const maxScrollLeft = Math.max(\n        0,\n        scrollElement.scrollWidth - scrollElement.clientWidth,\n      );\n      const nextScrollLeft = scrollElement.scrollLeft + delta;\n      const clampedScrollLeft = Math.min(\n        maxScrollLeft,\n        Math.max(0, nextScrollLeft),\n      );\n\n      if (Math.abs(clampedScrollLeft - scrollElement.scrollLeft) <= 0.5) {\n        return false;\n      }\n\n      scrollElement.scrollLeft = clampedScrollLeft;\n      updateScrollState();\n      return true;\n    },\n    [updateScrollState],\n  );\n\n  const onTabsWheel = React.useCallback(\n    (event: WheelEvent) => {\n      const scrollElement = scrollRef.current;\n      if (!scrollElement || !readScrollState(scrollElement).isOverflowing) {\n        return;\n      }\n\n      const dominantDelta =\n        Math.abs(event.deltaX) > Math.abs(event.deltaY)\n          ? event.deltaX\n          : event.deltaY;\n      if (dominantDelta === 0) return;\n\n      if (!scrollTabsBy(dominantDelta)) return;\n      event.preventDefault();\n    },\n    [scrollTabsBy],\n  );\n\n  useKeyedMountEffect(\n    joinEffectKey([\n      \"xlsx-tabs-listeners\",\n      onTabsWheel,\n      sheets.length,\n      updateScrollState,\n    ]),\n    () => {\n      updateScrollState();\n\n      const scrollElement = scrollRef.current;\n      const listElement = listRef.current;\n      if (!scrollElement) return;\n\n      const ResizeObserverCtor = globalThis.ResizeObserver;\n      const resizeObserver =\n        typeof ResizeObserverCtor === \"function\"\n          ? new ResizeObserverCtor(updateScrollState)\n          : null;\n      resizeObserver?.observe(scrollElement);\n      if (listElement) resizeObserver?.observe(listElement);\n\n      scrollElement.addEventListener(\"scroll\", updateScrollState, {\n        passive: true,\n      });\n      scrollElement.addEventListener(\"wheel\", onTabsWheel, { passive: false });\n      return () => {\n        resizeObserver?.disconnect();\n        scrollElement.removeEventListener(\"scroll\", updateScrollState);\n        scrollElement.removeEventListener(\"wheel\", onTabsWheel);\n      };\n    },\n  );\n\n  useKeyedMountEffect(\n    joinEffectKey([\n      \"xlsx-tabs-active\",\n      activeSheetIndex,\n      sheets.length,\n      tabWidth,\n      updateScrollState,\n    ]),\n    () => {\n      const scrollElement = scrollRef.current;\n      const activeTab = tabRefs.current[activeSheetIndex];\n      if (!scrollElement || !activeTab) return;\n\n      const nextLeft = resolveTabRevealScrollLeft({\n        activeIndex: activeSheetIndex,\n        activeLeft: activeTab.offsetLeft,\n        activeWidth: activeTab.offsetWidth,\n        scrollLeft: scrollElement.scrollLeft,\n        scrollWidth: scrollElement.scrollWidth,\n        sheetCount: sheets.length,\n        viewportWidth: scrollElement.clientWidth,\n      });\n\n      if (Math.abs(nextLeft - scrollElement.scrollLeft) > SCROLL_EPSILON_PX) {\n        const distance = Math.abs(nextLeft - scrollElement.scrollLeft);\n        scrollTabsTo(\n          scrollElement,\n          nextLeft,\n          distance >\n            scrollElement.clientWidth * LARGE_REVEAL_DISTANCE_MULTIPLIER\n            ? \"auto\"\n            : \"smooth\",\n        );\n      }\n\n      window.requestAnimationFrame(updateScrollState);\n    },\n  );\n\n  if (sheets.length <= 1) return null;\n\n  const selectSheet = (sheetIndex: number) => {\n    if (sheetIndex !== activeSheetIndex) onSelectSheet(sheetIndex);\n  };\n\n  const onTabKeyDown = (\n    event: React.KeyboardEvent<HTMLButtonElement>,\n    sheetIndex: number,\n  ) => {\n    const lastSheetIndex = sheets.length - 1;\n    let nextSheetIndex: number | null = null;\n\n    if (event.key === \"ArrowLeft\") {\n      nextSheetIndex = sheetIndex > 0 ? sheetIndex - 1 : lastSheetIndex;\n    } else if (event.key === \"ArrowRight\") {\n      nextSheetIndex = sheetIndex < lastSheetIndex ? sheetIndex + 1 : 0;\n    } else if (event.key === \"Home\") {\n      nextSheetIndex = 0;\n    } else if (event.key === \"End\") {\n      nextSheetIndex = lastSheetIndex;\n    }\n\n    if (nextSheetIndex == null) return;\n    event.preventDefault();\n    selectSheet(nextSheetIndex);\n    tabRefs.current[nextSheetIndex]?.focus();\n  };\n\n  return (\n    <div\n      data-slot=\"xlsx-viewer-tabs\"\n      role=\"tablist\"\n      aria-label=\"Workbook sheets\"\n      data-can-scroll-left={scrollState.canScrollLeft}\n      data-can-scroll-right={scrollState.canScrollRight}\n      data-overflowing={scrollState.isOverflowing}\n      style={{ height: XLSX_SHEET_TABS_HEIGHT_PX }}\n      className=\"bg-card relative flex-shrink-0 overflow-hidden border-t\"\n    >\n      <div\n        ref={scrollRef}\n        data-slot=\"xlsx-viewer-tabs-scroll\"\n        className=\"h-full overflow-x-auto overflow-y-hidden overscroll-x-contain px-1.5 py-1 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden\"\n      >\n        <div\n          ref={listRef}\n          data-slot=\"xlsx-viewer-tabs-track\"\n          className=\"flex min-w-max items-stretch gap-0.5\"\n        >\n          {sheets.map((sheet, sheetIndex) => (\n            <button\n              key={`${sheetIndex}:${sheet.name}`}\n              ref={(element) => {\n                tabRefs.current[sheetIndex] = element;\n              }}\n              type=\"button\"\n              role=\"tab\"\n              aria-selected={sheetIndex === activeSheetIndex}\n              tabIndex={sheetIndex === activeSheetIndex ? 0 : -1}\n              onClick={() => selectSheet(sheetIndex)}\n              onKeyDown={(event) => onTabKeyDown(event, sheetIndex)}\n              style={{\n                height: TAB_HEIGHT_PX,\n                ...(tabWidth ? { width: tabWidth } : null),\n              }}\n              title={sheet.name}\n              data-active={sheetIndex === activeSheetIndex}\n              className={cn(\n                \"relative flex flex-shrink-0 items-center justify-center overflow-hidden rounded-md border border-transparent px-2.5 text-xs leading-none font-medium whitespace-nowrap transition-[background-color,border-color,color,box-shadow] outline-none select-none\",\n                \"focus-visible:ring-ring focus-visible:ring-offset-card focus-visible:ring-2 focus-visible:ring-offset-1\",\n                sheetIndex === activeSheetIndex\n                  ? \"border-border/70 bg-background text-foreground shadow-xs\"\n                  : \"text-muted-foreground hover:bg-muted/60 hover:text-foreground\",\n              )}\n            >\n              <span className=\"truncate\">{sheet.name}</span>\n            </button>\n          ))}\n        </div>\n      </div>\n    </div>\n  );\n}\n",
      "type": "registry:ui",
      "target": "@ui/xlsx-sheet-tabs.tsx"
    },
    {
      "path": "registry/new-york-v4/ui/xlsx-shadow-scope.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { createPortal } from \"react-dom\";\n\nimport { useMountEffect } from \"@/hooks/use-mount-effect\";\n\n// Drop every `:has()` style rule from a constructed sheet (recursing into\n// @media/@supports/@layer blocks). The grid's own markup uses no `has-*`\n// variants, so these rules never style it, but Blink would still re-run their\n// invalidation against the grid subtree on each per-scroll mutation.\nfunction stripHasRules(owner: CSSStyleSheet | CSSGroupingRule) {\n  const rules = owner.cssRules;\n  if (!rules) return;\n  for (let index = rules.length - 1; index >= 0; index--) {\n    const rule = rules[index];\n    if ((rule as CSSStyleRule).selectorText?.includes(\":has(\")) {\n      try {\n        owner.deleteRule(index);\n      } catch {\n        // ignore a rule that cannot be removed\n      }\n    } else if ((rule as CSSGroupingRule).cssRules?.length) {\n      stripHasRules(rule as CSSGroupingRule);\n    }\n  }\n}\n\nlet sharedSheets: CSSStyleSheet[] | null = null;\n\nfunction getSharedSheets(): CSSStyleSheet[] {\n  if (sharedSheets) return sharedSheets;\n  const nextSheets: CSSStyleSheet[] = [];\n  for (const stylesheet of Array.from(document.styleSheets)) {\n    let rules: CSSRuleList;\n    try {\n      rules = stylesheet.cssRules;\n    } catch {\n      continue;\n    }\n    let text = \"\";\n    for (const rule of Array.from(rules)) text += rule.cssText + \"\\n\";\n    try {\n      const sheet = new CSSStyleSheet();\n      sheet.replaceSync(text);\n      stripHasRules(sheet);\n      nextSheets.push(sheet);\n    } catch {\n      // skip any sheet that cannot be reconstructed\n    }\n  }\n  sharedSheets = nextSheets;\n  return nextSheets;\n}\n\nfunction ShadowScope({\n  className,\n  style,\n  children,\n}: {\n  className?: string;\n  style?: React.CSSProperties;\n  children: React.ReactNode;\n}) {\n  const hostRef = React.useRef<HTMLDivElement>(null);\n  const [root, setRoot] = React.useState<ShadowRoot | null>(null);\n\n  useMountEffect(() => {\n    const host = hostRef.current;\n    if (!host) return;\n    const shadowRoot = host.shadowRoot ?? host.attachShadow({ mode: \"open\" });\n    try {\n      shadowRoot.adoptedStyleSheets = getSharedSheets();\n    } catch {\n      for (const node of Array.from(\n        document.querySelectorAll('style, link[rel=\"stylesheet\"]'),\n      )) {\n        try {\n          shadowRoot.appendChild(node.cloneNode(true));\n        } catch {\n          // ignore a node that cannot be cloned\n        }\n      }\n    }\n    setRoot(shadowRoot);\n  });\n\n  return (\n    <div ref={hostRef} className={className} style={style}>\n      {root ? createPortal(children, root) : null}\n    </div>\n  );\n}\n\nexport function ScrollerShell({\n  isolate,\n  className,\n  style,\n  children,\n}: {\n  isolate: boolean;\n  className?: string;\n  style?: React.CSSProperties;\n  children: React.ReactNode;\n}) {\n  if (isolate) {\n    return (\n      <ShadowScope className={className} style={style}>\n        {children}\n      </ShadowScope>\n    );\n  }\n  return (\n    <div className={className} style={style}>\n      {children}\n    </div>\n  );\n}\n",
      "type": "registry:ui",
      "target": "@ui/xlsx-shadow-scope.tsx"
    },
    {
      "path": "registry/new-york-v4/ui/xlsx-viewer-scroll.ts",
      "content": "import * as React from \"react\";\n\nimport { useKeyedMountEffect } from \"@/hooks/use-keyed-mount-effect\";\nimport {\n  resolveXlsxSheetChange,\n  type XlsxSheetMeta,\n} from \"@/lib/xlsx-workbook\";\n\nimport { joinEffectKey } from \"@/lib/effect-key\";\n\nexport interface PublicXlsxCellRef {\n  sheet: number;\n  row: number;\n  col: number;\n}\n\nexport interface InternalXlsxCellRef {\n  sheetIndex: number;\n  rowIndex: number;\n  columnIndex: number;\n}\n\nexport type PendingXlsxScrollTarget = InternalXlsxCellRef & {\n  behavior: ScrollBehavior;\n};\n\nexport type XlsxScrollRequest = PendingXlsxScrollTarget & {\n  nonce: number;\n};\n\nexport interface ResolvedXlsxScrollTarget {\n  sheetIndex: number;\n  request: PendingXlsxScrollTarget;\n  changed: boolean;\n}\n\nexport function toInternalCellRef(\n  cellRef: PublicXlsxCellRef | null | undefined,\n): InternalXlsxCellRef | null {\n  if (!isValidPublicCellRef(cellRef)) return null;\n  return {\n    sheetIndex: cellRef.sheet,\n    rowIndex: cellRef.row,\n    columnIndex: cellRef.col,\n  };\n}\n\nexport function isValidPublicCellRef(\n  cellRef: PublicXlsxCellRef | null | undefined,\n): cellRef is PublicXlsxCellRef {\n  return (\n    cellRef != null &&\n    Number.isSafeInteger(cellRef.sheet) &&\n    Number.isSafeInteger(cellRef.row) &&\n    Number.isSafeInteger(cellRef.col) &&\n    cellRef.sheet >= 0 &&\n    cellRef.row >= 0 &&\n    cellRef.col >= 0\n  );\n}\n\nexport function isValidLoadedScrollTarget(\n  target: InternalXlsxCellRef,\n  sheets: XlsxSheetMeta[],\n): boolean {\n  const sheet = sheets[target.sheetIndex];\n  const rowCount = normalizeSheetDimension(sheet?.rowCount);\n  const columnCount = normalizeSheetDimension(sheet?.columnCount);\n  return (\n    !!sheet &&\n    Number.isSafeInteger(target.rowIndex) &&\n    Number.isSafeInteger(target.columnIndex) &&\n    target.rowIndex >= 0 &&\n    target.columnIndex >= 0 &&\n    target.rowIndex < rowCount &&\n    target.columnIndex < columnCount\n  );\n}\n\nexport function resolveLoadedScrollTarget({\n  activeSheetIndex,\n  target,\n  sheets,\n}: {\n  activeSheetIndex: number;\n  target: PendingXlsxScrollTarget;\n  sheets: XlsxSheetMeta[];\n}): ResolvedXlsxScrollTarget | null {\n  if (!isValidLoadedScrollTarget(target, sheets)) return null;\n\n  const change = resolveXlsxSheetChange({\n    activeSheet: activeSheetIndex,\n    requestedSheet: target.sheetIndex,\n    sheetCount: sheets.length,\n  });\n  if (!change.accepted) return null;\n\n  return {\n    sheetIndex: change.sheetIndex,\n    request: target,\n    changed: change.changed,\n  };\n}\n\nexport function useXlsxScrollController({\n  activeSheetIndex,\n  sheets,\n  activateSheet,\n}: {\n  activeSheetIndex: number;\n  sheets: XlsxSheetMeta[] | null;\n  activateSheet: (sheetIndex: number) => void;\n}) {\n  const [scrollRequest, setScrollRequest] =\n    React.useState<XlsxScrollRequest | null>(null);\n  const [pendingScrollTarget, setPendingScrollTarget] =\n    React.useState<PendingXlsxScrollTarget | null>(null);\n  const scrollNonce = React.useRef(0);\n\n  const issueLoadedScrollTarget = React.useCallback(\n    (target: PendingXlsxScrollTarget) => {\n      if (!sheets) return false;\n\n      const resolved = resolveLoadedScrollTarget({\n        activeSheetIndex,\n        target,\n        sheets,\n      });\n      if (!resolved) return false;\n\n      if (resolved.changed) {\n        activateSheet(resolved.sheetIndex);\n      }\n\n      scrollNonce.current += 1;\n      setScrollRequest({\n        ...resolved.request,\n        nonce: scrollNonce.current,\n      });\n      return true;\n    },\n    [activateSheet, activeSheetIndex, sheets],\n  );\n\n  const pendingScrollKey =\n    pendingScrollTarget && sheets\n      ? joinEffectKey([\n          \"xlsx-pending-scroll\",\n          issueLoadedScrollTarget,\n          pendingScrollTarget,\n          sheets,\n        ])\n      : null;\n  useKeyedMountEffect(pendingScrollKey, () => {\n    if (!pendingScrollTarget || !sheets) return;\n    setPendingScrollTarget(null);\n    issueLoadedScrollTarget(pendingScrollTarget);\n  });\n\n  const scrollToCell = React.useCallback(\n    (\n      sheet: number,\n      row: number,\n      col: number,\n      options?: { behavior?: ScrollBehavior },\n    ) => {\n      const target = toInternalCellRef({ sheet, row, col });\n      if (!target) return;\n\n      const pendingTarget = {\n        ...target,\n        behavior: options?.behavior ?? \"smooth\",\n      };\n      if (!sheets) {\n        setPendingScrollTarget(pendingTarget);\n        return;\n      }\n\n      issueLoadedScrollTarget(pendingTarget);\n    },\n    [issueLoadedScrollTarget, sheets],\n  );\n\n  return {\n    scrollRequest,\n    scrollToCell,\n  };\n}\n\nfunction normalizeSheetDimension(value: number | undefined) {\n  return value != null && Number.isFinite(value) && value > 0\n    ? Math.floor(value)\n    : 0;\n}\n",
      "type": "registry:ui",
      "target": "@ui/xlsx-viewer-scroll.ts"
    },
    {
      "path": "registry/new-york-v4/ui/xlsx-viewer.worker.ts",
      "content": "// Web Worker that owns the SheetJS parse so it never blocks the UI thread.\n// @e965/xlsx is a patched, zero-dependency republish of SheetJS. The main thread\n// transfers the file bytes in; the worker parses the workbook and flattens it\n// into sparse compact sheets before transferring typed-array buffers back.\nimport * as XLSX from \"@e965/xlsx\";\n\nimport {\n  compactWorkbookTransferBuffers,\n  flattenSheetJsWorkbook,\n} from \"@/lib/xlsx-sheetjs-flattener\";\nimport {\n  isSpreadsheetContainer,\n  XlsxWorkerError,\n  type XlsxWorkerRequest,\n  type XlsxWorkerResponse,\n} from \"@/lib/xlsx-worker-protocol\";\n\n// Cast the worker global to `Worker` so `onmessage`/`postMessage` type-check\n// under the DOM lib without pulling in the conflicting \"webworker\" lib.\nconst ctx = self as unknown as Worker;\n\nctx.onmessage = (event: MessageEvent<XlsxWorkerRequest>) => {\n  try {\n    if (event.data.type !== \"parse_workbook\") {\n      throw new XlsxWorkerError(\n        \"parse_failed\",\n        \"Unsupported spreadsheet request\",\n      );\n    }\n\n    // SheetJS sniffs arbitrary bytes into a stub sheet rather than failing, so\n    // reject anything that is not a real spreadsheet container up front.\n    if (!isSpreadsheetContainer(event.data.buffer)) {\n      throw new XlsxWorkerError(\n        \"parse_failed\",\n        \"File is not a recognized spreadsheet (.xlsx or .xls).\",\n      );\n    }\n\n    // cellDates formats dates as text in `.w`; we never need styles or formulae.\n    const workbook = XLSX.read(event.data.buffer, {\n      type: \"array\",\n      cellDates: true,\n    });\n    const sheets = flattenSheetJsWorkbook(workbook);\n    const response: XlsxWorkerResponse = { type: \"workbook\", sheets };\n    ctx.postMessage(response, compactWorkbookTransferBuffers(sheets));\n  } catch (error) {\n    const response: XlsxWorkerResponse = {\n      type: \"error\",\n      code: error instanceof XlsxWorkerError ? error.code : \"parse_failed\",\n      message:\n        error instanceof Error ? error.message : \"Failed to parse spreadsheet\",\n    };\n    ctx.postMessage(response);\n  }\n};\n",
      "type": "registry:ui",
      "target": "@ui/xlsx-viewer.worker.ts"
    },
    {
      "path": "registry/new-york-v4/ui/use-is-client.ts",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nconst emptySubscribe = () => () => {};\nconst getClientSnapshot = () => true;\nconst getServerSnapshot = () => false;\n\n/**\n * SSR gate: `false` on the server (and during hydration's first pass),\n * `true` on the client.\n *\n * This must stay a synchronous external-store read, NOT the\n * `useState(false)` + mount-effect flip. The flip pattern makes every\n * viewer mount its Suspense boundary in a later update; when two such\n * boundaries suspend on pending resources in the same flush as other\n * commit-phase updates (viewer sidebar/geometry registration), React 19's\n * retry lanes desynchronize and re-attempt each other's boundary on every\n * commit — an unbounded synchronous suspend/retry loop that starves the\n * event loop (jsdom tests OOM; browsers busy-spin until the resource\n * resolves). With the store read, client renders suspend on mount, which\n * never enters that loop. Regression-guarded in\n * tests/pdf-viewer-thumbnails.test.tsx (\"shares one document resource…\").\n */\nexport function useIsClient(): boolean {\n  return React.useSyncExternalStore(\n    emptySubscribe,\n    getClientSnapshot,\n    getServerSnapshot,\n  );\n}\n",
      "type": "registry:ui",
      "target": "@ui/use-is-client.ts"
    },
    {
      "path": "registry/new-york-v4/ui/xlsx-viewer-sheet-state.ts",
      "content": "import * as React from \"react\";\n\nimport {\n  resolveXlsxSheetChange,\n  type XlsxSheetMeta,\n  type XlsxSource,\n} from \"@/lib/xlsx-workbook\";\n\nexport function useXlsxSheetState({\n  defaultSheetIndex,\n  onSheetChange,\n}: {\n  defaultSheetIndex: number;\n  onSheetChange?: (index: number) => void;\n}) {\n  const [activeSheetIndex, setActiveSheetIndex] = React.useState(\n    normalizeInitialSheetIndex(defaultSheetIndex),\n  );\n  const [sheets, setSheets] = React.useState<XlsxSheetMeta[] | null>(null);\n\n  const reportSource = React.useCallback((source: XlsxSource) => {\n    setSheets(source.sheets);\n    setActiveSheetIndex((sheetIndex) =>\n      clampSheetIndex(sheetIndex, source.sheets.length),\n    );\n  }, []);\n\n  const selectSheet = React.useCallback(\n    (sheetIndex: number) => {\n      const change = resolveXlsxSheetChange({\n        activeSheet: activeSheetIndex,\n        requestedSheet: sheetIndex,\n        sheetCount: sheets?.length,\n      });\n      if (!change.accepted) return false;\n      if (change.changed) {\n        setActiveSheetIndex(change.sheetIndex);\n        onSheetChange?.(change.sheetIndex);\n      }\n      return true;\n    },\n    [activeSheetIndex, onSheetChange, sheets],\n  );\n\n  const activateSheet = React.useCallback(\n    (sheetIndex: number) => {\n      if (sheetIndex === activeSheetIndex) return;\n      setActiveSheetIndex(sheetIndex);\n      onSheetChange?.(sheetIndex);\n    },\n    [activeSheetIndex, onSheetChange],\n  );\n\n  return {\n    activeSheetIndex,\n    activeSheet: sheets?.[activeSheetIndex] ?? null,\n    sheets,\n    isReady: sheets != null,\n    reportSource,\n    selectSheet,\n    activateSheet,\n  };\n}\n\nexport function normalizeInitialSheetIndex(value: number) {\n  return Number.isSafeInteger(value) && value > 0 ? value : 0;\n}\n\nexport function clampSheetIndex(value: number, sheetCount: number) {\n  if (!Number.isSafeInteger(value) || value < 0) return 0;\n  return Math.min(value, Math.max(0, sheetCount - 1));\n}\n",
      "type": "registry:ui",
      "target": "@ui/xlsx-viewer-sheet-state.ts"
    },
    {
      "path": "registry/new-york-v4/ui/xlsx-viewer-scale.ts",
      "content": "import * as React from \"react\";\n\nconst XLSX_MIN_SCALE = 0.1;\nconst XLSX_MAX_SCALE = 5;\nconst XLSX_ZOOM_STEP = 1.2;\n\nexport function useXlsxScale() {\n  const [scale, setScale] = React.useState(1);\n\n  return {\n    scale,\n    zoomOut: React.useCallback(\n      () => setScale((value) => clampXlsxScale(value / XLSX_ZOOM_STEP)),\n      [],\n    ),\n    zoomIn: React.useCallback(\n      () => setScale((value) => clampXlsxScale(value * XLSX_ZOOM_STEP)),\n      [],\n    ),\n    resetZoom: React.useCallback(() => setScale(1), []),\n  };\n}\n\nexport function clampXlsxScale(value: number) {\n  return Math.min(XLSX_MAX_SCALE, Math.max(XLSX_MIN_SCALE, value));\n}\n",
      "type": "registry:ui",
      "target": "@ui/xlsx-viewer-scale.ts"
    },
    {
      "path": "registry/new-york-v4/ui/xlsx-viewer-active-cell.ts",
      "content": "import type {\n  PublicXlsxCellRef,\n  XlsxScrollRequest,\n} from \"./xlsx-viewer-scroll\";\nimport { toInternalCellRef } from \"./xlsx-viewer-scroll\";\n\nexport interface XlsxGridCellRef {\n  rowIndex: number;\n  columnIndex: number;\n}\n\nexport function resolveXlsxActiveCell(\n  activeCell: PublicXlsxCellRef | null | undefined,\n  sheetIndex: number,\n): XlsxGridCellRef | null {\n  const target = toInternalCellRef(activeCell);\n  if (!target || target.sheetIndex !== sheetIndex) return null;\n  return {\n    rowIndex: target.rowIndex,\n    columnIndex: target.columnIndex,\n  };\n}\n\nexport function resolveXlsxScrollRequestForSheet(\n  scrollRequest: XlsxScrollRequest | null | undefined,\n  sheetIndex: number,\n) {\n  return scrollRequest?.sheetIndex === sheetIndex ? scrollRequest : null;\n}\n",
      "type": "registry:ui",
      "target": "@ui/xlsx-viewer-active-cell.ts"
    },
    {
      "path": "registry/new-york-v4/ui/xlsx-viewer-sheet.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { useKeyedMountEffect } from \"@/hooks/use-keyed-mount-effect\";\nimport type { XlsxSource } from \"@/lib/xlsx-workbook\";\nimport { XlsxGrid, XlsxGridSkeleton } from \"@/components/ui/xlsx-grid\";\n\nimport {\n  resolveXlsxActiveCell,\n  resolveXlsxScrollRequestForSheet,\n} from \"./xlsx-viewer-active-cell\";\nimport type { XlsxCellRef } from \"./xlsx-viewer-types\";\nimport type { XlsxScrollRequest } from \"./xlsx-viewer-scroll\";\nimport { clampSheetIndex } from \"./xlsx-viewer-sheet-state\";\nimport { joinEffectKey } from \"@/lib/effect-key\";\n\nexport function XlsxViewerSheet({\n  sourcePromise,\n  activeSheetIndex,\n  scale,\n  onReportSource,\n  activeCell,\n  scrollRequest,\n  isolateStyles,\n  viewportRef,\n}: {\n  sourcePromise: Promise<XlsxSource>;\n  activeSheetIndex: number;\n  scale: number;\n  onReportSource: (source: XlsxSource) => void;\n  activeCell?: XlsxCellRef | null;\n  scrollRequest?: XlsxScrollRequest | null;\n  isolateStyles: boolean;\n  viewportRef?: React.RefObject<HTMLDivElement | null>;\n}) {\n  const source = React.use(sourcePromise);\n\n  useKeyedMountEffect(\n    joinEffectKey([\"xlsx-report-source\", source, onReportSource]),\n    () => {\n      onReportSource(source);\n    },\n  );\n\n  const sheetIndex = clampSheetIndex(activeSheetIndex, source.sheets.length);\n  const sheet = source.sheets[sheetIndex];\n  const getCell = React.useCallback(\n    (rowIndex: number, columnIndex: number) =>\n      source.getCell(sheetIndex, rowIndex, columnIndex),\n    [source, sheetIndex],\n  );\n\n  return (\n    <XlsxGrid\n      key={sheetIndex}\n      rowCount={sheet?.rowCount ?? 0}\n      columnCount={sheet?.columnCount ?? 0}\n      sheetName={sheet?.name ?? `Sheet ${sheetIndex + 1}`}\n      getCell={getCell}\n      scale={scale}\n      activeCell={resolveXlsxActiveCell(activeCell, sheetIndex)}\n      scrollRequest={resolveXlsxScrollRequestForSheet(\n        scrollRequest,\n        sheetIndex,\n      )}\n      isolateStyles={isolateStyles}\n      viewportRef={viewportRef}\n    />\n  );\n}\n\nexport function XlsxViewerSheetSkeleton() {\n  return <XlsxGridSkeleton />;\n}\n",
      "type": "registry:ui",
      "target": "@ui/xlsx-viewer-sheet.tsx"
    },
    {
      "path": "registry/new-york-v4/ui/viewer-error.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { RotateCcw } from \"lucide-react\";\n\nimport { cn } from \"@/lib/utils\";\nimport type { ViewerDownloadAction } from \"@/lib/viewer-download-actions\";\nimport {\n  toViewerErrorInfo,\n  type ViewerErrorContext,\n  type ViewerFormat,\n} from \"@/lib/viewer-errors\";\n\nimport { Button } from \"./button\";\nimport { ViewerDownloadButton } from \"./viewer-download\";\n\nexport interface ViewerErrorStateProps extends ViewerErrorContext {\n  error: unknown;\n  download?: ViewerDownloadAction | null;\n  className?: string;\n  bare?: boolean;\n  variant?: \"card\" | \"document\" | \"inline\";\n  onRetry?: () => void;\n}\n\nexport function ViewerErrorState({\n  error,\n  format,\n  sourceKind,\n  canDownload,\n  retry,\n  download,\n  className,\n  bare = false,\n  variant = \"card\",\n  onRetry,\n}: ViewerErrorStateProps) {\n  const info = toViewerErrorInfo(error, {\n    format,\n    sourceKind,\n    canDownload: canDownload ?? Boolean(download && !download.isDisabled),\n    retry,\n  });\n  const showRetry = info.isRetryable && onRetry;\n  const showDownload =\n    info.isDownloadUseful && download != null && !download.isDisabled;\n\n  return (\n    <div\n      className={cn(errorStateClassName({ bare, variant }), className)}\n      data-error-domain={info.domain}\n      data-error-format={info.format}\n      data-error-kind={info.kind}\n      data-error-message={info.message}\n      data-slot=\"viewer-error\"\n      role=\"alert\"\n    >\n      <p>{info.userMessage}</p>\n      {showRetry || showDownload ? (\n        <div className=\"flex items-center gap-2\">\n          {showRetry ? (\n            <Button variant=\"outline\" size=\"sm\" onClick={onRetry}>\n              <RotateCcw className=\"mr-1.5 size-4\" />\n              Retry\n            </Button>\n          ) : null}\n          {showDownload ? (\n            <ViewerDownloadButton\n              action={download}\n              variant={showRetry ? \"ghost\" : \"outline\"}\n              size=\"sm\"\n              className=\"\"\n              showLabel\n            />\n          ) : null}\n        </div>\n      ) : null}\n    </div>\n  );\n}\n\nexport interface ViewerErrorBoundaryProps extends ViewerErrorContext {\n  children: React.ReactNode;\n  resetKey?: unknown;\n  download?: ViewerDownloadAction | null;\n  className?: string;\n  bare?: boolean;\n  variant?: \"card\" | \"document\" | \"inline\";\n  mapError?: (error: unknown) => unknown;\n  onRetry?: (error: unknown) => void;\n  onCaughtError?: (error: unknown, errorInfo: React.ErrorInfo) => void;\n}\n\nexport class ViewerErrorBoundary extends React.Component<\n  ViewerErrorBoundaryProps,\n  { error: unknown | null; retryKey: number }\n> {\n  state: Readonly<{ error: unknown | null; retryKey: number }> = {\n    error: null,\n    retryKey: 0,\n  };\n\n  componentDidUpdate(previousProps: ViewerErrorBoundaryProps) {\n    if (\n      previousProps.resetKey !== this.props.resetKey &&\n      this.state.error != null\n    ) {\n      this.setState({ error: null });\n    }\n  }\n\n  static getDerivedStateFromError(error: unknown) {\n    return { error };\n  }\n\n  componentDidCatch(error: unknown, errorInfo: React.ErrorInfo) {\n    this.props.onCaughtError?.(error, errorInfo);\n  }\n\n  render() {\n    if (this.state.error != null) {\n      const error = this.props.mapError\n        ? this.props.mapError(this.state.error)\n        : this.state.error;\n\n      return (\n        <ViewerErrorState\n          error={error}\n          format={this.props.format}\n          sourceKind={this.props.sourceKind}\n          canDownload={this.props.canDownload}\n          retry={this.props.retry}\n          download={this.props.download}\n          className={this.props.className}\n          bare={this.props.bare}\n          variant={this.props.variant}\n          onRetry={() => {\n            this.setState((state) => ({\n              error: null,\n              retryKey: state.retryKey + 1,\n            }));\n            this.props.onRetry?.(this.state.error);\n          }}\n        />\n      );\n    }\n\n    return (\n      <React.Fragment key={this.state.retryKey}>\n        {this.props.children}\n      </React.Fragment>\n    );\n  }\n}\n\nfunction errorStateClassName({\n  bare,\n  variant,\n}: {\n  bare: boolean;\n  variant: \"card\" | \"document\" | \"inline\";\n}) {\n  if (variant === \"inline\") {\n    return \"flex h-24 flex-col items-center justify-center gap-3 px-3 text-center text-xs text-muted-foreground\";\n  }\n  return cn(\n    \"flex min-h-64 flex-col items-center justify-center gap-3 p-6 text-center text-sm text-muted-foreground\",\n    bare ? \"bg-muted/20\" : \"rounded-xl border bg-muted/30\",\n    variant === \"document\" && \"min-h-full\",\n  );\n}\n\nexport type { ViewerFormat };\n",
      "type": "registry:ui",
      "target": "@ui/viewer-error.tsx"
    },
    {
      "path": "registry/new-york-v4/ui/xlsx-viewer-row-patcher.ts",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport type { XlsxCell } from \"@/lib/xlsx-workbook\";\nimport { setFixedGridInverseRowWindowGeometry } from \"@/components/ui/fixed-grid-layout\";\nimport type { GridCellCoordinate } from \"@/components/ui/fixed-grid-selection\";\nimport {\n  fixedVirtualItemWindow,\n  fixedVirtualItems,\n} from \"@/components/ui/fixed-grid-virtualization\";\nimport type {\n  FixedGridJumpViewportResult,\n  FixedGridViewport,\n  FixedGridVirtualItem,\n} from \"@/components/ui/fixed-grid-virtualization\";\nimport type { XlsxGridColumnItem } from \"@/components/ui/xlsx-grid-row\";\n\nexport interface XlsxRowPatchState {\n  activeCell: GridCellCoordinate | null;\n  columnCount: number;\n  columnItems: XlsxGridColumnItem[];\n  getCell: (rowIndex: number, columnIndex: number) => XlsxCell;\n  rowCount: number;\n  rowHeight: number;\n  sheetName: string;\n  viewportHeight: number;\n}\n\nexport interface XlsxRowPatcher {\n  patch: (viewport: FixedGridViewport) => FixedGridJumpViewportResult;\n  resync: (virtualRows: FixedGridVirtualItem[]) => void;\n  invalidate: () => void;\n}\n\ninterface XlsxCellHandle {\n  element: HTMLElement;\n  textNode: Text | null;\n  columnIndex: number | null;\n  isNumeric: boolean;\n  isActive: boolean;\n}\n\ninterface XlsxRowHandle {\n  element: HTMLElement;\n  rowNumberTextNode: Text | null;\n  cells: XlsxCellHandle[];\n  isHidden: boolean;\n  rowIndex: number | null;\n  transform: string;\n}\n\ninterface XlsxRowHandleCache {\n  rowWindow: HTMLDivElement;\n  rows: XlsxRowHandle[];\n  activeCellKey: string;\n  columnCount: number;\n  columnKey: string;\n  getCell: XlsxRowPatchState[\"getCell\"];\n  rowCount: number;\n  rowHeight: number;\n  scrollLeft: number;\n  sheetName: string;\n}\n\nconst PATCH_ROW_OVERSCAN = 0;\nconst MINIMUM_PATCH_VISIBLE_ROWS = 1;\nconst TEXT_NODE = 3;\n\nconst ACTIVE_CLASSES = [\n  \"bg-primary/12\",\n  \"ring-1\",\n  \"ring-primary/50\",\n  \"ring-inset\",\n];\n\nexport function useXlsxRowPatcher({\n  rowOffsetRef,\n  rowWindowRef,\n  getState,\n}: {\n  rowOffsetRef: React.RefObject<HTMLDivElement | null>;\n  rowWindowRef: React.RefObject<HTMLDivElement | null>;\n  getState: () => XlsxRowPatchState;\n}): XlsxRowPatcher {\n  const rowHandleCacheRef = React.useRef<XlsxRowHandleCache | null>(null);\n\n  const invalidate = React.useCallback(() => {\n    rowHandleCacheRef.current = null;\n  }, []);\n\n  const resync = React.useCallback(\n    (virtualRows: FixedGridVirtualItem[]) => {\n      const rowWindow = rowWindowRef.current;\n      if (!rowWindow) return;\n      const rowOffset = rowOffsetRef.current;\n      if (!rowOffset) return;\n      const state = getState();\n      const cache = readRowHandles(rowWindow, state, {\n        scrollLeft: 0,\n      });\n      const window = fixedVirtualItemWindow(virtualRows);\n      if (cache.rows.length > 0) {\n        setFixedGridInverseRowWindowGeometry({\n          rowOffsetElement: rowOffset,\n          rowWindowElement: rowWindow,\n          viewportHeight: state.viewportHeight,\n          window,\n        });\n        patchRows(cache.rows, window.items, state);\n      }\n      rowHandleCacheRef.current = null;\n    },\n    [getState, rowOffsetRef, rowWindowRef],\n  );\n\n  const patch = React.useCallback(\n    (viewport: FixedGridViewport): FixedGridJumpViewportResult => {\n      const state = getState();\n      if (!canPatchRows(viewport, state)) return \"pass\";\n\n      const rowWindow = rowWindowRef.current;\n      if (!rowWindow) return \"pass\";\n      const rowOffset = rowOffsetRef.current;\n      if (!rowOffset) return \"pass\";\n\n      const cache =\n        rowHandleCacheRef.current?.rowWindow === rowWindow\n          ? rowHandleCacheRef.current\n          : readRowHandles(rowWindow, state, viewport);\n      rowHandleCacheRef.current = cache;\n\n      if (!canUseCache(cache, state, viewport)) return \"pass\";\n      if (cache.rows.length === 0) return \"pass\";\n\n      const nextRows = fixedVirtualItems({\n        count: state.rowCount,\n        size: state.rowHeight,\n        scrollOffset: viewport.scrollTop,\n        viewportSize: viewport.clientHeight,\n        overscan: PATCH_ROW_OVERSCAN,\n        minimumVisibleCount: MINIMUM_PATCH_VISIBLE_ROWS,\n      });\n      if (nextRows.length === 0 || nextRows.length > cache.rows.length) {\n        return \"pass\";\n      }\n\n      if (\n        !canPatchRowHandles(\n          cache.rows,\n          nextRows.length,\n          state.columnItems.length,\n        )\n      ) {\n        return \"pass\";\n      }\n\n      const window = fixedVirtualItemWindow(nextRows);\n      setFixedGridInverseRowWindowGeometry({\n        rowOffsetElement: rowOffset,\n        rowWindowElement: rowWindow,\n        viewportHeight: viewport.clientHeight,\n        window,\n      });\n      patchRows(cache.rows, window.items, state);\n\n      return \"handled\";\n    },\n    [getState, rowOffsetRef, rowWindowRef],\n  );\n\n  return React.useMemo(\n    () => ({ invalidate, patch, resync }),\n    [invalidate, patch, resync],\n  );\n}\n\nfunction patchRows(\n  rowHandles: XlsxRowHandle[],\n  virtualRows: ReturnType<typeof fixedVirtualItems>,\n  state: XlsxRowPatchState,\n) {\n  for (let handleIndex = 0; handleIndex < rowHandles.length; handleIndex++) {\n    const rowHandle = rowHandles[handleIndex];\n    const virtualRow = virtualRows[handleIndex];\n    if (!virtualRow) {\n      setRowHidden(rowHandle, true);\n      continue;\n    }\n\n    const rowIndex = virtualRow.index;\n    const transform = `translate3d(0, ${virtualRow.start}px, 0)`;\n\n    setRowHidden(rowHandle, false);\n    setRowTransform(rowHandle, transform);\n\n    if (rowHandle.rowIndex !== rowIndex) {\n      rowHandle.rowIndex = rowIndex;\n      rowHandle.element.setAttribute(\"aria-rowindex\", String(rowIndex + 1));\n      setTextNodeValue(rowHandle.rowNumberTextNode, String(rowIndex + 1));\n    }\n\n    patchCells(rowHandle, rowIndex, state);\n  }\n}\n\nfunction patchCells(\n  rowHandle: XlsxRowHandle,\n  rowIndex: number,\n  state: XlsxRowPatchState,\n) {\n  for (let cellIndex = 0; cellIndex < state.columnItems.length; cellIndex++) {\n    const cellHandle = rowHandle.cells[cellIndex];\n    const columnIndex = state.columnItems[cellIndex]?.metadata.columnIndex;\n    if (!cellHandle || typeof columnIndex !== \"number\") continue;\n\n    const cell = state.getCell(rowIndex, columnIndex);\n    const isActive =\n      state.activeCell?.rowIndex === rowIndex &&\n      state.activeCell.columnIndex === columnIndex;\n\n    setTextNodeValue(cellHandle.textNode, cell.text);\n    if (cellHandle.columnIndex !== columnIndex) {\n      cellHandle.columnIndex = columnIndex;\n      cellHandle.element.setAttribute(\"aria-colindex\", String(columnIndex + 1));\n    }\n    setCellNumeric(cellHandle, cell.numeric);\n    setCellActive(cellHandle, isActive);\n  }\n}\n\nfunction canPatchRows(viewport: FixedGridViewport, state: XlsxRowPatchState) {\n  return (\n    state.rowCount > 0 &&\n    state.rowHeight > 0 &&\n    state.columnItems.length > 0 &&\n    !viewport.isJumpingColumns\n  );\n}\n\nfunction canUseCache(\n  cache: XlsxRowHandleCache,\n  state: XlsxRowPatchState,\n  viewport: FixedGridViewport,\n) {\n  return (\n    cache.activeCellKey === activeCellKey(state.activeCell) &&\n    cache.columnCount === state.columnCount &&\n    cache.columnKey === columnKey(state.columnItems) &&\n    cache.getCell === state.getCell &&\n    cache.rowCount === state.rowCount &&\n    cache.rowHeight === state.rowHeight &&\n    cache.scrollLeft === viewport.scrollLeft &&\n    cache.sheetName === state.sheetName\n  );\n}\n\nfunction canPatchRowHandles(\n  rowHandles: XlsxRowHandle[],\n  visibleRowCount: number,\n  cellCount: number,\n) {\n  for (let handleIndex = 0; handleIndex < visibleRowCount; handleIndex++) {\n    const rowHandle = rowHandles[handleIndex];\n    if (!rowHandle?.rowNumberTextNode) return false;\n    for (let cellIndex = 0; cellIndex < cellCount; cellIndex++) {\n      if (!rowHandle.cells[cellIndex]?.textNode) return false;\n    }\n  }\n  return true;\n}\n\nfunction readRowHandles(\n  rowWindow: HTMLDivElement,\n  state: XlsxRowPatchState,\n  viewport: Pick<FixedGridViewport, \"scrollLeft\">,\n): XlsxRowHandleCache {\n  const rows = Array.from(\n    rowWindow.querySelectorAll<HTMLElement>('[data-slot=\"xlsx-row\"]'),\n  ).map((element) => {\n    const rowNumber = element.querySelector<HTMLElement>(\n      '[data-slot=\"xlsx-row-number\"]',\n    );\n    const cells = Array.from(\n      element.querySelectorAll<HTMLElement>('[data-slot=\"xlsx-cell\"]'),\n    ).map((cell) => ({\n      element: cell,\n      textNode: firstTextNode(cell.firstElementChild ?? cell),\n      columnIndex: numberAttribute(cell, \"aria-colindex\", -1),\n      isNumeric: cell.classList.contains(\"tabular-nums\"),\n      isActive: cell.classList.contains(\"ring-primary/50\"),\n    }));\n\n    return {\n      element,\n      rowNumberTextNode: firstTextNode(rowNumber),\n      cells,\n      isHidden: element.hidden,\n      rowIndex: numberAttribute(element, \"aria-rowindex\", -1),\n      transform: element.style.transform,\n    };\n  });\n\n  return {\n    rowWindow,\n    rows,\n    activeCellKey: activeCellKey(state.activeCell),\n    columnCount: state.columnCount,\n    columnKey: columnKey(state.columnItems),\n    getCell: state.getCell,\n    rowCount: state.rowCount,\n    rowHeight: state.rowHeight,\n    scrollLeft: viewport.scrollLeft,\n    sheetName: state.sheetName,\n  };\n}\n\nfunction firstTextNode(element: Element | null): Text | null {\n  const node = element?.firstChild;\n  return node?.nodeType === TEXT_NODE ? (node as Text) : null;\n}\n\nfunction numberAttribute(\n  element: HTMLElement,\n  attribute: string,\n  offset: number,\n) {\n  const value = Number(element.getAttribute(attribute));\n  return Number.isFinite(value) ? value + offset : null;\n}\n\nfunction activeCellKey(activeCell: GridCellCoordinate | null) {\n  return activeCell ? `${activeCell.rowIndex}:${activeCell.columnIndex}` : \"\";\n}\n\nfunction columnKey(columnItems: XlsxGridColumnItem[]) {\n  return columnItems\n    .map((item) => `${item.metadata.columnIndex}:${item.widthPx}`)\n    .join(\"|\");\n}\n\nfunction setTextNodeValue(textNode: Text | null, value: string) {\n  if (textNode && textNode.nodeValue !== value) textNode.nodeValue = value;\n}\n\nfunction setRowTransform(row: XlsxRowHandle, transform: string) {\n  if (row.transform === transform) return;\n  row.element.style.transform = transform;\n  row.transform = transform;\n}\n\nfunction setRowHidden(row: XlsxRowHandle, isHidden: boolean) {\n  if (row.isHidden === isHidden) return;\n  row.element.hidden = isHidden;\n  row.isHidden = isHidden;\n}\n\nfunction setCellNumeric(cell: XlsxCellHandle, isNumeric: boolean) {\n  if (cell.isNumeric === isNumeric) return;\n  cell.element.classList.toggle(\"justify-end\", isNumeric);\n  cell.element.classList.toggle(\"tabular-nums\", isNumeric);\n  cell.element.classList.toggle(\"justify-start\", !isNumeric);\n  cell.isNumeric = isNumeric;\n}\n\nfunction setCellActive(cell: XlsxCellHandle, isActive: boolean) {\n  if (cell.isActive === isActive) return;\n  for (const className of ACTIVE_CLASSES) {\n    cell.element.classList.toggle(className, isActive);\n  }\n  cell.isActive = isActive;\n}\n",
      "type": "registry:ui",
      "target": "@ui/xlsx-viewer-row-patcher.ts"
    },
    {
      "path": "registry/new-york-v4/ui/fixed-grid-row-window.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport {\n  getFixedGridInverseRowWindowStyles,\n  type CssLength,\n  type FixedGridInverseRowWindowGeometry,\n} from \"./fixed-grid-layout\";\n\ntype FixedGridRowWindowElement = keyof React.JSX.IntrinsicElements;\ntype FixedGridRowWindowElementProps = React.HTMLAttributes<HTMLElement> & {\n  \"data-slot\"?: string;\n};\n\nexport interface FixedGridRowWindowProps extends Omit<\n  FixedGridRowWindowElementProps,\n  \"children\"\n> {\n  as?: FixedGridRowWindowElement;\n  children?: React.ReactNode;\n  minWidth?: CssLength;\n  offsetAs?: FixedGridRowWindowElement;\n  offsetClassName?: string;\n  offsetDataSlot?: string;\n  offsetProps?: FixedGridRowWindowElementProps;\n  rowMinWidth?: CssLength;\n  rowOffsetRef?: React.Ref<HTMLElement>;\n  rowWindowRef?: React.Ref<HTMLElement>;\n  totalSize: CssLength;\n  viewportHeight: number;\n  virtualRowWindow: FixedGridInverseRowWindowGeometry;\n  windowAs?: FixedGridRowWindowElement;\n  windowClassName?: string;\n  windowDataSlot?: string;\n  windowProps?: FixedGridRowWindowElementProps;\n}\n\nexport function FixedGridRowWindow({\n  as = \"div\",\n  children,\n  minWidth,\n  offsetAs = \"div\",\n  offsetClassName,\n  offsetDataSlot,\n  offsetProps,\n  rowMinWidth,\n  rowOffsetRef,\n  rowWindowRef,\n  style,\n  totalSize,\n  viewportHeight,\n  virtualRowWindow,\n  windowAs = \"div\",\n  windowClassName,\n  windowDataSlot,\n  windowProps,\n  className,\n  ...props\n}: FixedGridRowWindowProps) {\n  const { offsetStyle, spacerStyle, windowStyle } =\n    getFixedGridInverseRowWindowStyles({\n      minWidth,\n      rowMinWidth,\n      totalSize,\n      viewportHeight,\n      window: virtualRowWindow,\n    });\n  const offsetElementProps = {\n    ...offsetProps,\n    ...(offsetDataSlot ? { \"data-slot\": offsetDataSlot } : null),\n    \"aria-hidden\": offsetProps?.[\"aria-hidden\"] ?? true,\n    className: offsetClassName ?? offsetProps?.className,\n    ref: rowOffsetRef,\n    style: { ...offsetProps?.style, ...offsetStyle },\n  };\n  const windowElementProps = {\n    ...windowProps,\n    ...(windowDataSlot ? { \"data-slot\": windowDataSlot } : null),\n    className: windowClassName ?? windowProps?.className,\n    ref: rowWindowRef,\n    style: { ...windowProps?.style, ...windowStyle },\n  };\n\n  return React.createElement(\n    as,\n    {\n      ...props,\n      className,\n      style: { ...style, ...spacerStyle },\n    },\n    React.createElement(offsetAs, offsetElementProps),\n    React.createElement(windowAs, windowElementProps, children),\n  );\n}\n",
      "type": "registry:ui",
      "target": "@ui/fixed-grid-row-window.tsx"
    },
    {
      "path": "registry/new-york-v4/ui/fixed-grid-native-find-index.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { useKeyedLayoutEffect } from \"@/hooks/use-keyed-layout-effect\";\nimport { useKeyedMountEffect } from \"@/hooks/use-keyed-mount-effect\";\nimport { joinEffectKey } from \"@/lib/effect-key\";\n\nexport interface FixedGridNativeFindCellAddress {\n  rowIndex: number;\n  columnIndex: number;\n}\n\nexport interface FixedGridNativeFindChunk {\n  startRowIndex: number;\n  endRowIndex: number;\n  text: string;\n}\n\ninterface FixedGridNativeFindBuild {\n  chunks: FixedGridNativeFindChunk[];\n  indexedCellCount: number;\n}\n\ntype FixedGridNativeFindIdleWindow = Window &\n  typeof globalThis & {\n    cancelIdleCallback?: Window[\"cancelIdleCallback\"];\n    requestIdleCallback?: Window[\"requestIdleCallback\"];\n  };\n\nconst DEFAULT_MAX_CELLS_PER_CHUNK = 512;\nconst DEFAULT_MAX_INDEXED_CELLS = 250_000;\nconst NATIVE_FIND_IDLE_TIMEOUT_MS = 400;\nconst NATIVE_FIND_FALLBACK_DELAY_MS = 80;\n\nexport function FixedGridNativeFindIndex({\n  rowCount,\n  columnCount,\n  getCellText,\n  onCellMatch,\n  dataSlot,\n  enabled = true,\n  maxCellsPerChunk = DEFAULT_MAX_CELLS_PER_CHUNK,\n  maxIndexedCells = DEFAULT_MAX_INDEXED_CELLS,\n}: {\n  rowCount: number;\n  columnCount: number;\n  getCellText: (rowIndex: number, columnIndex: number) => string;\n  onCellMatch: (cell: FixedGridNativeFindCellAddress) => void;\n  dataSlot: string;\n  enabled?: boolean;\n  maxCellsPerChunk?: number;\n  maxIndexedCells?: number;\n}) {\n  const [isReady, setIsReady] = React.useState(false);\n\n  useKeyedMountEffect(\n    joinEffectKey([\n      \"fixed-grid-native-find-ready\",\n      enabled,\n      rowCount,\n      columnCount,\n      getCellText,\n      maxCellsPerChunk,\n      maxIndexedCells,\n    ]),\n    () => {\n      setIsReady(false);\n      if (\n        !enabled ||\n        !canBuildFixedGridNativeFindIndex({\n          rowCount,\n          columnCount,\n          maxIndexedCells,\n        })\n      ) {\n        return;\n      }\n      if (typeof window === \"undefined\") return;\n\n      const show = () => setIsReady(true);\n      const browserWindow = window as FixedGridNativeFindIdleWindow;\n      if (\n        browserWindow.requestIdleCallback &&\n        browserWindow.cancelIdleCallback\n      ) {\n        const idleId = browserWindow.requestIdleCallback(show, {\n          timeout: NATIVE_FIND_IDLE_TIMEOUT_MS,\n        });\n        return () => browserWindow.cancelIdleCallback?.(idleId);\n      }\n\n      const timeoutId = browserWindow.setTimeout(\n        show,\n        NATIVE_FIND_FALLBACK_DELAY_MS,\n      );\n      return () => browserWindow.clearTimeout(timeoutId);\n    },\n  );\n\n  const index = React.useMemo<FixedGridNativeFindBuild | null>(() => {\n    if (!enabled || !isReady) return null;\n    return buildFixedGridNativeFindIndex({\n      rowCount,\n      columnCount,\n      getCellText,\n      maxCellsPerChunk,\n      maxIndexedCells,\n    });\n  }, [\n    columnCount,\n    enabled,\n    getCellText,\n    isReady,\n    maxCellsPerChunk,\n    maxIndexedCells,\n    rowCount,\n  ]);\n\n  if (!index || index.chunks.length === 0) return null;\n\n  return (\n    <div\n      aria-hidden=\"true\"\n      className=\"pointer-events-none h-0 w-0 overflow-hidden opacity-0\"\n      data-native-find-indexed-cells={index.indexedCellCount}\n      data-native-find-indexed-chunks={index.chunks.length}\n      data-native-find-indexed-columns={normalizeNativeFindCount(columnCount)}\n      data-native-find-indexed-rows={normalizeNativeFindCount(rowCount)}\n      data-slot={dataSlot}\n    >\n      {index.chunks.map((chunk) => (\n        <FixedGridNativeFindEntry\n          key={chunk.startRowIndex}\n          chunk={chunk}\n          columnCount={columnCount}\n          getCellText={getCellText}\n          onCellMatch={onCellMatch}\n        />\n      ))}\n    </div>\n  );\n}\n\nfunction FixedGridNativeFindEntry({\n  chunk,\n  columnCount,\n  getCellText,\n  onCellMatch,\n}: {\n  chunk: FixedGridNativeFindChunk;\n  columnCount: number;\n  getCellText: (rowIndex: number, columnIndex: number) => string;\n  onCellMatch: (cell: FixedGridNativeFindCellAddress) => void;\n}) {\n  const ref = React.useRef<HTMLSpanElement | null>(null);\n\n  useKeyedLayoutEffect(\n    joinEffectKey([\n      \"fixed-grid-native-find-entry\",\n      chunk.startRowIndex,\n      chunk.endRowIndex,\n      chunk.text,\n      columnCount,\n      getCellText,\n      onCellMatch,\n    ]),\n    () => {\n      const element = ref.current;\n      if (!element) return;\n      element.setAttribute(\"hidden\", \"until-found\");\n\n      const scrollToSelectedCell = () => {\n        onCellMatch(\n          resolveFixedGridNativeFindCell({\n            chunk,\n            columnCount,\n            getCellText,\n            offset: selectedTextOffsetIn(element),\n          }),\n        );\n      };\n\n      const handleBeforeMatch = () => {\n        scrollToSelectedCell();\n        if (typeof requestAnimationFrame === \"function\") {\n          requestAnimationFrame(() => {\n            scrollToSelectedCell();\n            element.setAttribute(\"hidden\", \"until-found\");\n          });\n          return;\n        }\n        element.setAttribute(\"hidden\", \"until-found\");\n      };\n\n      element.addEventListener(\"beforematch\", handleBeforeMatch);\n      return () => {\n        element.removeEventListener(\"beforematch\", handleBeforeMatch);\n      };\n    },\n  );\n\n  return (\n    <span\n      ref={ref}\n      className=\"block h-px w-px overflow-hidden whitespace-pre\"\n      data-native-find-end-row={chunk.endRowIndex}\n      data-native-find-start-row={chunk.startRowIndex}\n    >\n      {chunk.text || \" \"}\n    </span>\n  );\n}\n\nexport function buildFixedGridNativeFindIndex({\n  rowCount,\n  columnCount,\n  getCellText,\n  maxCellsPerChunk = DEFAULT_MAX_CELLS_PER_CHUNK,\n  maxIndexedCells = DEFAULT_MAX_INDEXED_CELLS,\n}: {\n  rowCount: number;\n  columnCount: number;\n  getCellText: (rowIndex: number, columnIndex: number) => string;\n  maxCellsPerChunk?: number;\n  maxIndexedCells?: number;\n}): FixedGridNativeFindBuild | null {\n  const safeRowCount = normalizeNativeFindCount(rowCount);\n  const safeColumnCount = normalizeNativeFindCount(columnCount);\n  if (safeRowCount === 0 || safeColumnCount === 0) {\n    return { chunks: [], indexedCellCount: 0 };\n  }\n\n  const indexedCellCount = safeRowCount * safeColumnCount;\n  if (!isNativeFindIndexedCellCountAllowed(indexedCellCount, maxIndexedCells)) {\n    return null;\n  }\n\n  const rowsPerChunk = nativeFindRowsPerChunk({\n    columnCount: safeColumnCount,\n    maxCellsPerChunk,\n  });\n  const chunks: FixedGridNativeFindChunk[] = [];\n  for (\n    let startRowIndex = 0;\n    startRowIndex < safeRowCount;\n    startRowIndex += rowsPerChunk\n  ) {\n    const endRowIndex = Math.min(safeRowCount, startRowIndex + rowsPerChunk);\n    chunks.push({\n      startRowIndex,\n      endRowIndex,\n      text: fixedGridNativeFindChunkText({\n        startRowIndex,\n        endRowIndex,\n        columnCount: safeColumnCount,\n        getCellText,\n      }),\n    });\n  }\n\n  return { chunks, indexedCellCount };\n}\n\nfunction canBuildFixedGridNativeFindIndex({\n  rowCount,\n  columnCount,\n  maxIndexedCells,\n}: {\n  rowCount: number;\n  columnCount: number;\n  maxIndexedCells: number;\n}) {\n  const safeRowCount = normalizeNativeFindCount(rowCount);\n  const safeColumnCount = normalizeNativeFindCount(columnCount);\n  if (safeRowCount === 0 || safeColumnCount === 0) return false;\n  return isNativeFindIndexedCellCountAllowed(\n    safeRowCount * safeColumnCount,\n    maxIndexedCells,\n  );\n}\n\nfunction isNativeFindIndexedCellCountAllowed(\n  indexedCellCount: number,\n  maxIndexedCells: number,\n) {\n  return (\n    Number.isSafeInteger(indexedCellCount) &&\n    indexedCellCount <= normalizeNativeFindCellLimit(maxIndexedCells)\n  );\n}\n\nfunction fixedGridNativeFindChunkText({\n  startRowIndex,\n  endRowIndex,\n  columnCount,\n  getCellText,\n}: {\n  startRowIndex: number;\n  endRowIndex: number;\n  columnCount: number;\n  getCellText: (rowIndex: number, columnIndex: number) => string;\n}) {\n  const parts: string[] = [];\n  for (let rowIndex = startRowIndex; rowIndex < endRowIndex; rowIndex += 1) {\n    for (let columnIndex = 0; columnIndex < columnCount; columnIndex += 1) {\n      if (parts.length > 0) parts.push(columnIndex === 0 ? \"\\n\" : \"\\t\");\n      parts.push(getCellText(rowIndex, columnIndex));\n    }\n  }\n  return parts.join(\"\");\n}\n\nfunction resolveFixedGridNativeFindCell({\n  chunk,\n  columnCount,\n  getCellText,\n  offset,\n}: {\n  chunk: FixedGridNativeFindChunk;\n  columnCount: number;\n  getCellText: (rowIndex: number, columnIndex: number) => string;\n  offset: number | null;\n}): FixedGridNativeFindCellAddress {\n  if (offset == null || offset < 0) {\n    return { rowIndex: chunk.startRowIndex, columnIndex: 0 };\n  }\n\n  let cursor = 0;\n  for (\n    let rowIndex = chunk.startRowIndex;\n    rowIndex < chunk.endRowIndex;\n    rowIndex += 1\n  ) {\n    for (let columnIndex = 0; columnIndex < columnCount; columnIndex += 1) {\n      if (cursor > 0) cursor += 1;\n      const text = getCellText(rowIndex, columnIndex);\n      const cellStart = cursor;\n      const cellEnd = cellStart + text.length;\n      if (text.length > 0 && offset >= cellStart && offset <= cellEnd) {\n        return { rowIndex, columnIndex };\n      }\n      cursor = cellEnd;\n    }\n  }\n\n  return { rowIndex: chunk.startRowIndex, columnIndex: 0 };\n}\n\nfunction selectedTextOffsetIn(element: HTMLElement): number | null {\n  const selection =\n    element.ownerDocument.getSelection?.() ??\n    (typeof window === \"undefined\" ? null : window.getSelection());\n  if (!selection || selection.rangeCount === 0) return null;\n\n  const selectedRange = selection.getRangeAt(0);\n  if (!element.contains(selectedRange.startContainer)) return null;\n\n  const prefixRange = element.ownerDocument.createRange();\n  try {\n    prefixRange.selectNodeContents(element);\n    prefixRange.setEnd(selectedRange.startContainer, selectedRange.startOffset);\n    return prefixRange.toString().length;\n  } finally {\n    prefixRange.detach();\n  }\n}\n\nfunction nativeFindRowsPerChunk({\n  columnCount,\n  maxCellsPerChunk,\n}: {\n  columnCount: number;\n  maxCellsPerChunk: number;\n}) {\n  const safeCellLimit = Math.max(\n    1,\n    normalizeNativeFindCellLimit(maxCellsPerChunk),\n  );\n  return Math.max(1, Math.min(128, Math.floor(safeCellLimit / columnCount)));\n}\n\nfunction normalizeNativeFindCount(value: number) {\n  return Number.isSafeInteger(value) && value > 0 ? Math.floor(value) : 0;\n}\n\nfunction normalizeNativeFindCellLimit(value: number) {\n  return Number.isSafeInteger(value) && value > 0 ? Math.floor(value) : 0;\n}\n",
      "type": "registry:ui",
      "target": "@ui/fixed-grid-native-find-index.tsx"
    }
  ],
  "type": "registry:ui"
}
