{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "pdf-viewer",
  "title": "PDF Viewer",
  "description": "A pdfjs-backed PDF viewer: continuous scroll, controlled or uncontrolled zoom, rotate, fit-to-width, download, a per-page overlay slot for bounding-box citations, a PdfHighlight overlay, and imperative scrollToPage / scrollToPageArea handles for driving source highlights.",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "@retab/file-viewer",
    "@retab/pdf-document-resource",
    "@retab/utils",
    "button",
    "@retab/scroll-area",
    "separator",
    "@retab/skeleton",
    "dropdown-menu",
    "@retab/spinner",
    "@retab/viewer-controls"
  ],
  "files": [
    {
      "path": "registry/new-york-v4/ui/pdf-viewer.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nimport {\n  FileViewer,\n  FileViewerContent,\n  FileViewerHeader,\n  FileViewerControls,\n  FileViewerTitle,\n  FileViewerInset,\n  FileViewerProvider,\n  FileViewerViewport,\n} from \"./file-viewer\";\nimport {\n  PdfResourceContent,\n  type PdfResourceContentProps,\n  type PdfViewerContentProps,\n} from \"./pdf-viewer-content\";\nimport {\n  PdfViewerPages,\n  PdfViewerProvider,\n  type PdfDocumentSource,\n} from \"./pdf-viewer-context\";\nimport type { PdfViewerHandle } from \"./pdf-viewer-types\";\n\nexport type {\n  PageOverlayProps,\n  PdfPageAreaTarget,\n  PdfPageRenderSource,\n  PdfPageRenderStatus,\n  PdfPageRenderTiming,\n  PdfViewerHandle,\n  PdfViewerPerformanceOptions,\n} from \"./pdf-viewer-types\";\nexport {\n  PdfViewerPages,\n  PdfViewerProvider,\n  usePdfViewerThumbnails,\n  type PdfDocumentSource,\n} from \"./pdf-viewer-context\";\nexport {\n  PdfResourceContent,\n  type PdfResourceContentProps,\n  type PdfViewerContentProps,\n} from \"./pdf-viewer-content\";\nexport {\n  PdfThumbnailRail,\n  PdfViewerThumbnails,\n  type PdfThumbnailRailProps,\n  type PdfViewerThumbnailsProps,\n} from \"./pdf-viewer-thumbnails\";\n\nexport interface PdfHighlightProps extends React.ComponentProps<\"div\"> {\n  /** Normalized box, each field a percentage [0, 100] of the page. */\n  area: { left: number; top: number; width: number; height: number };\n}\n\nexport function PdfHighlight({\n  area,\n  className,\n  style,\n  ...props\n}: PdfHighlightProps) {\n  return (\n    <div\n      data-slot=\"pdf-highlight\"\n      className={cn(\n        \"border-primary/70 bg-primary/12 pointer-events-none absolute z-10 rounded-[2px] border shadow-[0_4px_16px_rgb(0_0_0_/_8%)]\",\n        className,\n      )}\n      style={{\n        left: `${area.left}%`,\n        top: `${area.top}%`,\n        width: `${area.width}%`,\n        height: `${area.height}%`,\n        ...style,\n      }}\n      {...props}\n    />\n  );\n}\n\nexport interface PdfViewerProps extends PdfViewerContentProps {\n  /** Canonical PDF source. URL sources preserve PDF.js range-loading behavior. */\n  source: PdfDocumentSource;\n}\n\nexport const PdfViewer = React.forwardRef<PdfViewerHandle, PdfViewerProps>(\n  function PdfViewer(props, ref) {\n    const {\n      source,\n      className,\n      bare = false,\n      controls = true,\n      download = true,\n      ...pagesProps\n    } = props;\n    return (\n      <FileViewerProvider source={source}>\n        <FileViewer\n          className={cn(\n            \"h-full\",\n            bare && \"rounded-none border-0 bg-transparent\",\n            className,\n          )}\n        >\n          <PdfViewerProvider>\n            <FileViewerHeader>\n              <FileViewerTitle className=\"flex-1\" />\n              {controls ? <FileViewerControls className=\"ms-auto\" /> : null}\n            </FileViewerHeader>\n            <FileViewerContent>\n              <FileViewerInset>\n                <FileViewerViewport>\n                  <PdfViewerPages\n                    {...pagesProps}\n                    bare\n                    className=\"h-full\"\n                    download={download}\n                    ref={ref}\n                  />\n                </FileViewerViewport>\n              </FileViewerInset>\n            </FileViewerContent>\n          </PdfViewerProvider>\n        </FileViewer>\n      </FileViewerProvider>\n    );\n  },\n);\n",
      "type": "registry:ui",
      "target": "@ui/pdf-viewer.tsx"
    },
    {
      "path": "registry/new-york-v4/ui/pdf-viewer-content.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { clearPdfDocumentResource } from \"@/lib/pdf-document-resource\";\nimport { cn } from \"@/lib/utils\";\nimport type { ViewerResource } from \"@/lib/viewer-resource\";\nimport { ScrollArea } from \"@/components/ui/scroll-area\";\n\nimport {\n  usePdfDocumentControlsRegistration,\n  usePdfDocumentControlsState,\n} from \"./pdf-viewer-document-controls\";\nimport { usePdfDocumentLayout } from \"./pdf-viewer-document-layout\";\nimport { usePdfDocumentResource } from \"./pdf-viewer-document-resource\";\nimport { usePdfDocumentRuntime } from \"./pdf-viewer-document-runtime\";\nimport { PdfDocumentPagesLayer } from \"./pdf-viewer-pages-layer\";\nimport { PdfViewerFallback } from \"./pdf-viewer-states\";\nimport type {\n  PageOverlayProps,\n  PdfPageSize,\n  PdfPageRenderTiming,\n  PdfViewerHandle,\n  PdfViewerPerformanceOptions,\n} from \"./pdf-viewer-types\";\nimport { useIsClient } from \"./use-is-client\";\nimport { ViewerControls } from \"./viewer-controls\";\nimport { ViewerErrorBoundary } from \"./viewer-error\";\n\nexport type PdfViewerContentProps = {\n  className?: string;\n  /** Controlled rendered scale; when omitted the viewer fits page width until manually zoomed. */\n  scale?: number;\n  /** Initial uncontrolled scale. Leave unset for fit-to-width. */\n  defaultScale?: number;\n  /** Page geometry used by the loading skeleton before PDF metadata resolves. */\n  fallbackPageSize?: PdfPageSize;\n  /** Called when controls request a scale change. `null` means fit width. */\n  onScaleChange?: (scale: number | null) => void;\n  controls?: boolean;\n  /** Show download actions in this viewer's controls/error state. */\n  download?: boolean;\n  /** Render absolutely-positioned overlays (e.g. bbox citations) on each page. */\n  renderPageOverlay?: (props: PageOverlayProps) => React.ReactNode;\n  /** Fired with the 1-based page nearest the top of the viewport as you scroll. */\n  onVisiblePageChange?: (page: number) => void;\n  /** Fired with scroll progress in [0, 1] (for a fine-grained scroll cursor). */\n  onScrollProgressChange?: (progress: number) => void;\n  /** Reports page render work for profiling and benchmark surfaces. */\n  onPageRenderTiming?: (timing: PdfPageRenderTiming) => void;\n  /** Retained for callers that still pass benchmark switches; the simple renderer ignores them. */\n  performanceOptions?: PdfViewerPerformanceOptions;\n  /** Drop the outer border/rounded/background so the viewer fills its container. */\n  bare?: boolean;\n};\n\nexport type PdfResourceContentProps = PdfViewerContentProps & {\n  resource: ViewerResource;\n};\n\nexport const PdfResourceContent = React.forwardRef<\n  PdfViewerHandle,\n  PdfResourceContentProps\n>(function PdfResourceContent(props, ref) {\n  const resource = props.resource;\n  const isClient = useIsClient();\n\n  if (!isClient) {\n    return (\n      <PdfViewerFallback\n        className={props.className}\n        bare={props.bare}\n        controls={props.controls}\n        fallbackPageSize={props.fallbackPageSize}\n      />\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=\"pdf\"\n      onRetry={() => clearPdfDocumentResource(resource.content)}\n      resetKey={resource.keys.resource}\n      sourceKind={resource.sourceKind}\n    >\n      <React.Suspense\n        fallback={\n          <PdfViewerFallback\n            className={props.className}\n            bare={props.bare}\n            controls={props.controls}\n            fallbackPageSize={props.fallbackPageSize}\n          />\n        }\n      >\n        <PdfViewerInner {...props} forwardedRef={ref} resource={resource} />\n      </React.Suspense>\n    </ViewerErrorBoundary>\n  );\n});\n\nfunction PdfViewerInner({\n  resource,\n  className,\n  scale: controlledScale,\n  defaultScale,\n  onScaleChange,\n  controls = true,\n  download = true,\n  renderPageOverlay,\n  onVisiblePageChange,\n  onScrollProgressChange,\n  onPageRenderTiming,\n  performanceOptions,\n  bare = false,\n  forwardedRef,\n}: PdfResourceContentProps & {\n  forwardedRef?: React.ForwardedRef<PdfViewerHandle>;\n}) {\n  const content = resource.content;\n  const document = usePdfDocumentResource(content);\n  const layout = usePdfDocumentLayout({\n    controlledScale,\n    defaultScale,\n    document,\n    onScaleChange,\n  });\n  const runtime = usePdfDocumentRuntime({\n    document,\n    documentKey: content.key,\n    layout,\n    onPageRenderTiming,\n    onScrollProgressChange,\n    onVisiblePageChange,\n    performanceOptions,\n    renderPageOverlay,\n  });\n  const controlsState = usePdfDocumentControlsState({\n    currentPage: runtime.currentPage,\n    download,\n    downloadAction: resource.originalDownload,\n    fitWidth: runtime.zoomControls.fitWidth,\n    pageCount: document.numPages,\n    resolvedScale: layout.resolvedScale,\n    rotateClockwise: layout.rotateClockwise,\n    zoomIn: runtime.zoomControls.zoomIn,\n    zoomOut: runtime.zoomControls.zoomOut,\n  });\n\n  usePdfDocumentControlsRegistration(controlsState);\n  React.useImperativeHandle(\n    forwardedRef ?? null,\n    () => runtime.handle,\n    [runtime.handle],\n  );\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=\"pdf-viewer\"\n    >\n      {controls ? <ViewerControls {...controlsState} /> : null}\n\n      <div className=\"flex min-h-0 flex-1\">\n        <div className=\"relative flex min-h-0 min-w-0 flex-1 flex-col\">\n          <div className=\"relative flex min-h-0 flex-1 flex-col\">\n            <ScrollArea\n              className=\"min-h-0 flex-1\"\n              viewportRef={runtime.setViewportElement}\n              viewportProps={{\n                onScroll: runtime.handleViewportScroll,\n                style: { overflowAnchor: \"none\" },\n              }}\n            >\n              <PdfDocumentPagesLayer {...runtime.pagesLayerProps} />\n            </ScrollArea>\n          </div>\n        </div>\n      </div>\n    </div>\n  );\n}\n",
      "type": "registry:ui",
      "target": "@ui/pdf-viewer-content.tsx"
    },
    {
      "path": "registry/new-york-v4/ui/pdf-viewer-document-controls.ts",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport type { ViewerResource } from \"@/lib/viewer-resource\";\nimport { joinEffectKey } from \"@/lib/effect-key\";\nimport { useKeyedMountEffect } from \"@/hooks/use-keyed-mount-effect\";\n\nimport {\n  useViewerControlsRegistration,\n  type ViewerControlsState,\n} from \"./viewer-controls\";\n\nexport function usePdfDocumentControlsState({\n  currentPage,\n  download,\n  downloadAction,\n  fitWidth,\n  pageCount,\n  resolvedScale,\n  rotateClockwise,\n  zoomIn,\n  zoomOut,\n}: {\n  currentPage: number;\n  download: boolean;\n  downloadAction: ViewerResource[\"originalDownload\"];\n  fitWidth: () => void;\n  pageCount: number;\n  resolvedScale: number;\n  rotateClockwise: () => void;\n  zoomIn: () => void;\n  zoomOut: () => void;\n}): ViewerControlsState {\n  return React.useMemo<ViewerControlsState>(\n    () => ({\n      position: {\n        kind: \"page\",\n        current: currentPage,\n        total: pageCount,\n      },\n      zoom: {\n        scale: resolvedScale,\n        onZoomOut: zoomOut,\n        onZoomIn: zoomIn,\n        onFit: fitWidth,\n      },\n      rotate: { onRotate: rotateClockwise },\n      downloads: download ? [downloadAction] : [],\n    }),\n    [\n      currentPage,\n      download,\n      downloadAction,\n      fitWidth,\n      pageCount,\n      resolvedScale,\n      rotateClockwise,\n      zoomIn,\n      zoomOut,\n    ],\n  );\n}\n\nexport function usePdfDocumentControlsRegistration(\n  controlsState: ViewerControlsState,\n) {\n  const onControlsChange = useViewerControlsRegistration();\n\n  useKeyedMountEffect(joinEffectKey([onControlsChange, controlsState]), () => {\n    if (!onControlsChange) return;\n    onControlsChange(controlsState);\n    return () => onControlsChange(null);\n  });\n}\n",
      "type": "registry:ui",
      "target": "@ui/pdf-viewer-document-controls.ts"
    },
    {
      "path": "registry/new-york-v4/ui/pdf-viewer-document-layout.ts",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport {\n  useOptionalFileViewerRendererEnvironment,\n  useOptionalFileViewerRendererFrame,\n} from \"./file-viewer-renderer-frame\";\nimport type { FileViewerDocumentSurfaceMotionResolver } from \"./file-viewer-motion-kernel\";\nimport {\n  resolveFileViewerRendererLayoutInlineSize,\n  type FileViewerRendererFrame,\n} from \"./file-viewer-renderer-contract\";\nimport { createFileViewerFitWidthSurfaceMotionResolver } from \"./file-viewer-fit-width-motion\";\nimport {\n  createPdfPageLayout,\n  type PdfPageLayoutModel,\n} from \"./pdf-viewer-layout\";\nimport { usePdfPageSizes } from \"./pdf-viewer-page-sizes\";\nimport {\n  getPdfFitWidthScale,\n  getPdfPageDevicePixelRatio,\n  PDF_PAGE_HORIZONTAL_PADDING,\n  useMeasuredElementWidth,\n  usePdfScale,\n} from \"./pdf-viewer-scale\";\nimport {\n  usePdfDocumentRotation,\n  usePdfFirstPageSize,\n  type PdfDocument,\n} from \"./pdf-viewer-document-resource\";\nimport type { PdfPageSize } from \"./pdf-viewer-types\";\nimport type { ViewerDocumentTransition } from \"./viewer-types\";\n\nexport type PdfDocumentLayoutState = {\n  containerRef: React.RefCallback<HTMLDivElement>;\n  displayScale: number;\n  firstPageSize: PdfPageSize;\n  fitWidth: () => void;\n  isFitWidth: boolean;\n  pageDevicePixelRatio: number;\n  pageLayout: PdfPageLayoutModel;\n  renderScale: number;\n  resolvedScale: number;\n  rotateClockwise: () => void;\n  rotation: number;\n  resolveSurfaceMotionStyle: FileViewerDocumentSurfaceMotionResolver;\n  /**\n   * Stage pixels the settled stage grows per pane pixel. Fit-width fits the\n   * FIRST page while the stage spans the WIDEST page, so a mixed-width\n   * document grows its stage faster than the pane (maxBase / fitBase > 1);\n   * the fit-width motion model needs this slope or its first frame lands at\n   * a fractionally wrong scale.\n   */\n  stageInlineSlope: number;\n  setPageSize: ReturnType<typeof usePdfPageSizes>[\"setPageSize\"];\n  transition: ViewerDocumentTransition;\n  rendererFrame: FileViewerRendererFrame;\n  zoomIn: () => void;\n  zoomOut: () => void;\n};\n\nexport function usePdfDocumentLayout({\n  controlledScale,\n  defaultScale,\n  document,\n  onScaleChange,\n}: {\n  controlledScale?: number;\n  defaultScale?: number;\n  document: PdfDocument;\n  onScaleChange?: (scale: number | null) => void;\n}): PdfDocumentLayoutState {\n  const firstPageSize = usePdfFirstPageSize(document);\n  const rendererEnvironment = useOptionalFileViewerRendererEnvironment();\n  const { ref: containerRef, width: containerWidth } = useMeasuredElementWidth({\n    enabled: !rendererEnvironment.usesShellGeometry,\n  });\n  const rendererFrame = useOptionalFileViewerRendererFrame({\n    fallbackInlineSize: containerWidth,\n  });\n  const { rotation, rotateClockwise } = usePdfDocumentRotation(document);\n  const fitPageWidth =\n    rotation % 180 === 0 ? firstPageSize.width : firstPageSize.height;\n  const logicalInlineSize = resolveFileViewerRendererLayoutInlineSize({\n    fallbackInlineSize: containerWidth,\n    rendererFrame,\n  });\n  const { resolvedScale, isFitWidth, zoomIn, zoomOut, fitWidth } = usePdfScale({\n    controlledScale,\n    defaultScale,\n    onScaleChange,\n    containerWidth: logicalInlineSize,\n    pageWidth: fitPageWidth,\n    resetKey: document,\n  });\n  const fitWidthDisplayScale = isFitWidth\n    ? getPdfFitWidthScale(logicalInlineSize, fitPageWidth)\n    : resolvedScale;\n  const displayScale = isFitWidth ? fitWidthDisplayScale : resolvedScale;\n  const renderScale = getPdfPreparedFitWidthRenderScale({\n    enabled: isFitWidth,\n    fallbackScale: resolvedScale,\n    pageWidth: fitPageWidth,\n    rasterInlineSize: getPdfPreparedFitWidthRasterInlineSize(rendererFrame),\n  });\n  const transition = React.useMemo(\n    () =>\n      getPdfDocumentTransition({\n        isFitWidth,\n        rendererTransition: rendererFrame.documentTransition,\n      }),\n    [isFitWidth, rendererFrame.documentTransition],\n  );\n  const { pageSizeByNumber, setPageSize } = usePdfPageSizes(document);\n  const pageLayout = React.useMemo(\n    () =>\n      createPdfPageLayout({\n        pageCount: document.numPages,\n        defaultPageSize: firstPageSize,\n        pageSizeByNumber,\n        scale: displayScale,\n        rotation,\n      }),\n    [\n      document.numPages,\n      displayScale,\n      firstPageSize,\n      pageSizeByNumber,\n      rotation,\n    ],\n  );\n  const pageDevicePixelRatio = getPdfPageDevicePixelRatio({\n    devicePixelRatio:\n      (typeof window !== \"undefined\" ? window.devicePixelRatio : 1) || 1,\n    mode: \"settled\",\n  });\n  // The page layout is already at the motion's target width (commit-then-\n  // relax), so the resolver reprojects that settled stage to the in-flight\n  // visual width with one uniform transform.\n  const stageInlineSlope =\n    isFitWidth && displayScale > 0 && fitPageWidth > 0\n      ? pageLayout.maxPageWidth / displayScale / fitPageWidth\n      : 1;\n  const resolveSurfaceMotionStyle =\n    React.useMemo<FileViewerDocumentSurfaceMotionResolver>(\n      () =>\n        createFileViewerFitWidthSurfaceMotionResolver({\n          // The pages layer centres the surface with auto margins whatever the\n          // frame's align is (a camera view splits leftover space evenly), so\n          // the margin model must say \"center\" too — an align-derived model\n          // over a centred surface is exactly the mismatch that pops the\n          // narrowing leg.\n          align: \"center\",\n          direction: rendererFrame.direction,\n          isFitWidth,\n          stageInlineSize: pageLayout.maxPageWidth,\n          stageOuterInlinePadding: PDF_PAGE_HORIZONTAL_PADDING,\n          stageInlineSlope,\n        }),\n      [\n        isFitWidth,\n        pageLayout.maxPageWidth,\n        rendererFrame.direction,\n        stageInlineSlope,\n      ],\n    );\n\n  return {\n    containerRef,\n    displayScale,\n    firstPageSize,\n    fitWidth,\n    isFitWidth,\n    pageDevicePixelRatio,\n    pageLayout,\n    renderScale,\n    resolvedScale,\n    rotateClockwise,\n    rotation,\n    resolveSurfaceMotionStyle,\n    setPageSize,\n    stageInlineSlope,\n    transition,\n    rendererFrame,\n    zoomIn,\n    zoomOut,\n  };\n}\n\nfunction getPdfDocumentTransition({\n  isFitWidth,\n  rendererTransition,\n}: {\n  isFitWidth: boolean;\n  rendererTransition: ViewerDocumentTransition;\n}): ViewerDocumentTransition {\n  if (isFitWidth && rendererTransition.source === \"viewer-shell\") {\n    return rendererTransition;\n  }\n\n  return {\n    layoutPolicy: \"live\",\n    scrollPolicy: \"preserve\",\n    source: \"none\",\n    transitionId: null,\n    visualPolicy: \"none\",\n  };\n}\n\nfunction getPdfPreparedFitWidthRenderScale({\n  enabled,\n  fallbackScale,\n  pageWidth,\n  rasterInlineSize,\n}: {\n  enabled: boolean;\n  fallbackScale: number;\n  pageWidth: number;\n  rasterInlineSize: number | null;\n}) {\n  if (!enabled || rasterInlineSize == null) return fallbackScale;\n\n  // Layout commits the target width at slide start; this prepares the\n  // backing canvas for the largest width the motion touches so the already-\n  // rendered bitmap only ever CSS-rescales — never blanks — while the\n  // target-resolution render is pending.\n  return getPdfFitWidthScale(rasterInlineSize, pageWidth);\n}\n\nfunction getPdfPreparedFitWidthRasterInlineSize(\n  rendererFrame: FileViewerRendererFrame,\n) {\n  if (!rendererFrame.canToggleSidebar) return rendererFrame.rasterInlineSize;\n\n  const inlineSize = Math.max(\n    rendererFrame.rasterInlineSize ?? 0,\n    rendererFrame.shellInlineSize ?? 0,\n  );\n\n  return inlineSize > 0 ? inlineSize : null;\n}\n",
      "type": "registry:ui",
      "target": "@ui/pdf-viewer-document-layout.ts"
    },
    {
      "path": "registry/new-york-v4/ui/pdf-viewer-document-resource.ts",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport {\n  readPdfDocumentResource,\n  readPdfPageResource,\n  releasePdfDocumentResource,\n  retainPdfDocumentResource,\n} from \"@/lib/pdf-document-resource\";\nimport type { ViewerResource } from \"@/lib/viewer-resource\";\nimport { joinEffectKey } from \"@/lib/effect-key\";\nimport { useKeyedMountEffect } from \"@/hooks/use-keyed-mount-effect\";\n\nimport type { PdfPageSize } from \"./pdf-viewer-types\";\n\nexport type PdfDocument = ReturnType<typeof readPdfDocumentResource>;\nexport type PdfDocumentContent = ViewerResource[\"content\"];\n\nexport function usePdfDocumentResource(content: PdfDocumentContent) {\n  const document = readPdfDocumentResource(content);\n\n  useKeyedMountEffect(joinEffectKey([content, document]), () => {\n    retainPdfDocumentResource(content, document);\n    return () => releasePdfDocumentResource(content, document);\n  });\n\n  return document;\n}\n\nexport function usePdfFirstPageSize(document: PdfDocument): PdfPageSize {\n  const firstPage = readPdfPageResource(document, 1);\n\n  return React.useMemo<PdfPageSize>(() => {\n    const viewport = firstPage.getViewport({ scale: 1 });\n    return { width: viewport.width, height: viewport.height };\n  }, [firstPage]);\n}\n\nexport function usePdfDocumentRotation(document: PdfDocument) {\n  const [rotationState, setRotationState] = React.useState<{\n    document: PdfDocument;\n    value: number;\n  }>(() => ({ document, value: 0 }));\n  const rotation = Object.is(rotationState.document, document)\n    ? rotationState.value\n    : 0;\n  const rotateClockwise = React.useCallback(() => {\n    setRotationState((state) => ({\n      document,\n      value:\n        ((Object.is(state.document, document) ? state.value : 0) + 90) % 360,\n    }));\n  }, [document]);\n\n  return { rotation, rotateClockwise };\n}\n",
      "type": "registry:ui",
      "target": "@ui/pdf-viewer-document-resource.ts"
    },
    {
      "path": "registry/new-york-v4/ui/pdf-viewer-document-runtime.ts",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { joinEffectKey } from \"@/lib/effect-key\";\nimport { useKeyedLayoutEffect } from \"@/hooks/use-keyed-layout-effect\";\nimport { useKeyedMountEffect } from \"@/hooks/use-keyed-mount-effect\";\nimport { useMountEffect } from \"@/hooks/use-mount-effect\";\n\nimport {\n  findPdfPageByOffset,\n  getPdfPageLayout,\n  getPdfPhysicalScrollHeight,\n} from \"./pdf-viewer-layout\";\nimport type { PdfDocumentLayoutState } from \"./pdf-viewer-document-layout\";\nimport type { PdfDocument } from \"./pdf-viewer-document-resource\";\nimport type { PdfDocumentPagesLayerProps } from \"./pdf-viewer-pages-layer\";\nimport {\n  FILE_VIEWER_FIT_WIDTH_ANCHOR_BLOCK_PROPERTY,\n  captureFileViewerFitWidthAnchorScreenOffset,\n  resolveFileViewerFitWidthMotionAnchorBlock,\n} from \"./file-viewer-fit-width-motion\";\nimport {\n  FILE_VIEWER_BEFORE_LAYOUT_MOTION_EVENT,\n  readFileViewerBeforeLayoutMotionFrame,\n} from \"./file-viewer-elements\";\nimport type { FileViewerMotionFrame } from \"./file-viewer-motion-plan\";\nimport { usePdfRenderedPageCache } from \"./pdf-viewer-render-cache\";\nimport {\n  PDF_SCROLLING_PAGE_RENDER_CONCURRENCY,\n  usePdfPageRenderScheduler,\n} from \"./pdf-viewer-render-scheduler\";\nimport { PDF_READING_MARKER_RATIO, usePdfScroll } from \"./pdf-viewer-scroll\";\nimport {\n  notePdfZoomMotionPageRender,\n  PDF_ZOOM_MOTION_TOTAL_MS,\n} from \"./pdf-viewer-zoom-motion\";\nimport type {\n  PageOverlayProps,\n  PdfPageRenderTiming,\n  PdfViewerHandle,\n  PdfViewerPerformanceOptions,\n} from \"./pdf-viewer-types\";\nimport { usePdfViewerTelemetry } from \"./pdf-viewer-telemetry\";\nimport { usePdfPageVirtualization } from \"./pdf-viewer-virtualization\";\n\nconst PDF_SHELL_MOTION_RENDER_RESUME_DELAY_MS = 1400;\n\nexport type PdfDocumentRuntimeState = {\n  currentPage: number;\n  handle: PdfViewerHandle;\n  handleViewportScroll: () => void;\n  pagesLayerProps: PdfDocumentPagesLayerProps;\n  setViewportElement: (element: HTMLDivElement | null) => void;\n  zoomControls: {\n    fitWidth: () => void;\n    zoomIn: () => void;\n    zoomOut: () => void;\n  };\n};\n\nexport function usePdfDocumentRuntime({\n  document,\n  documentKey,\n  layout,\n  onPageRenderTiming,\n  onScrollProgressChange,\n  onVisiblePageChange,\n  performanceOptions,\n  renderPageOverlay,\n}: {\n  document: PdfDocument;\n  documentKey: string;\n  layout: PdfDocumentLayoutState;\n  onPageRenderTiming?: (timing: PdfPageRenderTiming) => void;\n  onScrollProgressChange?: (progress: number) => void;\n  onVisiblePageChange?: (page: number) => void;\n  performanceOptions?: PdfViewerPerformanceOptions;\n  renderPageOverlay?: (props: PageOverlayProps) => React.ReactNode;\n}): PdfDocumentRuntimeState {\n  const {\n    captureZoomIntent,\n    currentPage,\n    viewportElement,\n    setViewportElement,\n    measureScroll,\n    handleScroll,\n    scrollToPage,\n    scrollToPageArea,\n    getViewportElement,\n    getScrollMetrics,\n  } = usePdfScroll({\n    isLayoutTransitioning: layout.rendererFrame.isTransitioning,\n    pageCount: document.numPages,\n    layout: layout.pageLayout,\n    transition: layout.transition,\n    resetKey: document,\n    onVisiblePageChange,\n    onScrollProgressChange,\n  });\n  // Toolbar zoom steps re-anchor the viewport center and relax a FLIP over\n  // the commit (pdf-viewer-zoom-motion). The sequence must flip in the zoom\n  // gesture's own render so the visual clip is already released when the\n  // enlarged opening frame paints; rapid steps re-arm the release timer.\n  const [zoomMotionSequence, setZoomMotionSequence] = React.useState(0);\n  const isZoomTransitioning = zoomMotionSequence > 0;\n  useKeyedMountEffect(joinEffectKey([zoomMotionSequence]), () => {\n    if (zoomMotionSequence === 0) return;\n    const timeout = setTimeout(\n      () => setZoomMotionSequence(0),\n      PDF_ZOOM_MOTION_TOTAL_MS,\n    );\n    return () => clearTimeout(timeout);\n  });\n  const beginZoomMotion = React.useCallback(() => {\n    captureZoomIntent();\n    setZoomMotionSequence((sequence) => sequence + 1);\n  }, [captureZoomIntent]);\n  const layoutFitWidth = layout.fitWidth;\n  const layoutZoomIn = layout.zoomIn;\n  const layoutZoomOut = layout.zoomOut;\n  const zoomControls = React.useMemo(\n    () => ({\n      fitWidth: () => {\n        beginZoomMotion();\n        layoutFitWidth();\n      },\n      zoomIn: () => {\n        beginZoomMotion();\n        layoutZoomIn();\n      },\n      zoomOut: () => {\n        beginZoomMotion();\n        layoutZoomOut();\n      },\n    }),\n    [beginZoomMotion, layoutFitWidth, layoutZoomIn, layoutZoomOut],\n  );\n  const documentSurfaceElementRef = React.useRef<HTMLElement | null>(null);\n  // The transform's anchor line, in the visual stage's own (physical scroll)\n  // coordinates. The idle write is the live reading-marker offset; the\n  // slide-start write derives the EXACT fixed point of the commit that just\n  // ran (see writeMotionAnchorBlockOffset), so the transform's first frame\n  // reproduces the pre-toggle screen even where the rebase clamped.\n  const preMotionAnchorRef = React.useRef<{\n    pageNumber: number;\n    screenRelTop: number;\n  } | null>(null);\n  const motionProbePageNumber = layout.rendererFrame.isTransitioning\n    ? (preMotionAnchorRef.current?.pageNumber ?? currentPage)\n    : currentPage;\n  const lastAnchorBlockRef = React.useRef<number | null>(null);\n  const writeAnchorBlockOffsetPx = React.useCallback((anchorBlock: number) => {\n    const documentSurfaceElement = documentSurfaceElementRef.current;\n    if (!documentSurfaceElement) return;\n    const safeAnchorBlock = Number.isFinite(anchorBlock) ? anchorBlock : 0;\n    lastAnchorBlockRef.current = safeAnchorBlock;\n    documentSurfaceElement.style.setProperty(\n      FILE_VIEWER_FIT_WIDTH_ANCHOR_BLOCK_PROPERTY,\n      `${safeAnchorBlock}px`,\n    );\n  }, []);\n  const writeDocumentAnchorBlockOffset = React.useCallback(() => {\n    const metrics = getScrollMetrics();\n    writeAnchorBlockOffsetPx(\n      Math.max(0, metrics.physicalScrollTop) +\n        Math.max(0, metrics.viewportHeight) * PDF_READING_MARKER_RATIO,\n    );\n  }, [getScrollMetrics, writeAnchorBlockOffsetPx]);\n  // The transform must pin the exact screen line the slide-start commit\n  // preserved, or the first sliding frame drifts off the pre-toggle screen.\n  // Rather than assuming anything about the rebase or the layout models,\n  // measure the fixed point: capture the marker page's (transform-aware)\n  // on-screen top against the OLD layout model before the commit, then solve\n  // for the stage anchor that puts the same page top back on that screen line\n  // under the NEW layout model at the motion's first-frame scale. Exact in\n  // both direct and rebased (paged) scroll spaces, at clamps, across\n  // measured/estimated page-size differences, and through mid-flight\n  // retargets (the capture applies the in-flight transform it was seen under).\n  const writeMotionAnchorBlockOffset = React.useCallback(() => {\n    const metrics = getScrollMetrics();\n    const preMotionAnchor = preMotionAnchorRef.current;\n    const newPageLayout = preMotionAnchor\n      ? getPdfPageLayout(layout.pageLayout, preMotionAnchor.pageNumber)\n      : null;\n    const logicalDelta = metrics.scrollTop - metrics.physicalScrollTop;\n    const anchorBlock =\n      preMotionAnchor && newPageLayout\n        ? resolveFileViewerFitWidthMotionAnchorBlock({\n            fromInlineSize: layout.rendererFrame.fromInlineSize,\n            probeScreenOffset: preMotionAnchor.screenRelTop,\n            probeStageOffset: newPageLayout.offsetTop - logicalDelta,\n            scrollTop: metrics.physicalScrollTop,\n            stageInlineSize: layout.pageLayout.maxPageWidth,\n            // The PDF layout is proportional on both axes, so the block\n            // slope IS the inline slope.\n            stageBlockSlope: layout.stageInlineSlope,\n            toInlineSize: layout.rendererFrame.toInlineSize,\n          })\n        : null;\n\n    if (anchorBlock == null) {\n      writeDocumentAnchorBlockOffset();\n      return;\n    }\n    writeAnchorBlockOffsetPx(anchorBlock);\n  }, [\n    getScrollMetrics,\n    layout.pageLayout,\n    layout.rendererFrame.fromInlineSize,\n    layout.rendererFrame.toInlineSize,\n    layout.stageInlineSlope,\n    writeAnchorBlockOffsetPx,\n    writeDocumentAnchorBlockOffset,\n  ]);\n  const measureBeforeLayoutMotion = React.useCallback(\n    (liveFrame: FileViewerMotionFrame | null) => {\n      const metrics = getScrollMetrics();\n      const markerOffset =\n        metrics.scrollTop +\n        Math.max(0, metrics.viewportHeight) * PDF_READING_MARKER_RATIO;\n      const pageNumber = findPdfPageByOffset(layout.pageLayout, markerOffset);\n      const pageLayout = getPdfPageLayout(layout.pageLayout, pageNumber);\n      const logicalDelta = metrics.scrollTop - metrics.physicalScrollTop;\n      preMotionAnchorRef.current = pageLayout\n        ? {\n            pageNumber,\n            screenRelTop: captureFileViewerFitWidthAnchorScreenOffset({\n              lastAnchorBlock: lastAnchorBlockRef.current,\n              liveFrame,\n              probeStageOffset: pageLayout.offsetTop - logicalDelta,\n              scrollTop: metrics.physicalScrollTop,\n              stageInlineSize: layout.pageLayout.maxPageWidth,\n              stageBlockSlope: layout.stageInlineSlope,\n            }),\n          }\n        : null;\n      measureScroll();\n    },\n    [\n      getScrollMetrics,\n      layout.pageLayout,\n      layout.stageInlineSlope,\n      measureScroll,\n    ],\n  );\n  const measureBeforeLayoutMotionRef = React.useRef(measureBeforeLayoutMotion);\n  measureBeforeLayoutMotionRef.current = measureBeforeLayoutMotion;\n  const handleBeforeLayoutMotion = React.useCallback((event: Event) => {\n    measureBeforeLayoutMotionRef.current(\n      readFileViewerBeforeLayoutMotionFrame(event),\n    );\n  }, []);\n  const setDocumentSurfaceElement = React.useCallback(\n    (element: HTMLElement | null) => {\n      const previousElement = documentSurfaceElementRef.current;\n      if (previousElement === element) return;\n      previousElement?.removeEventListener(\n        FILE_VIEWER_BEFORE_LAYOUT_MOTION_EVENT,\n        handleBeforeLayoutMotion,\n      );\n      documentSurfaceElementRef.current = element;\n      if (!element) return;\n      element.addEventListener(\n        FILE_VIEWER_BEFORE_LAYOUT_MOTION_EVENT,\n        handleBeforeLayoutMotion,\n      );\n      writeDocumentAnchorBlockOffset();\n    },\n    [handleBeforeLayoutMotion, writeDocumentAnchorBlockOffset],\n  );\n  // While a zoom relax is in flight, the raster world freezes at its\n  // pre-zoom settled state: renderScale holds (no page re-registers a new\n  // render signature), the scheduler pauses, and the virtualized page window\n  // holds. pdf.js raster work is main-thread heavy — letting it land\n  // mid-flight is exactly what stutters the relax's clock. Everything\n  // sharpens at settle instead (the zoom analogue of the shell motion's\n  // render pause, on the relax's much shorter envelope).\n  const settledZoomRenderScaleRef = React.useRef(layout.renderScale);\n  if (!isZoomTransitioning) {\n    settledZoomRenderScaleRef.current = layout.renderScale;\n  }\n  const effectiveRenderScale = isZoomTransitioning\n    ? settledZoomRenderScaleRef.current\n    : layout.renderScale;\n  const [shouldHoldPageWindowForRaster, setShouldHoldPageWindowForRaster] =\n    React.useState(false);\n  const {\n    scrollPageOffset,\n    visiblePageNumbers,\n    renderPageNumbers,\n    warmPageNumbers,\n    preloadPageNumbers,\n    measureVisiblePages,\n  } = usePdfPageVirtualization({\n    getScrollMetrics,\n    isLayoutTransitioning: layout.rendererFrame.isTransitioning,\n    layout: layout.pageLayout,\n    resetKey: document,\n    shouldHoldPageWindow: shouldHoldPageWindowForRaster,\n    transition: layout.transition,\n    viewportElement,\n  });\n  const renderedPageCache = usePdfRenderedPageCache(document);\n  const shouldUseRenderedPageCache =\n    performanceOptions?.renderedPageCache !== false;\n  const isShellMotionActive =\n    layout.rendererFrame.phase !== \"idle\" ||\n    layout.transition.source === \"viewer-shell\";\n  const isPageRenderingPaused =\n    usePdfShellMotionRenderPause(isShellMotionActive) || isZoomTransitioning;\n  const {\n    activePageNumbers: activeRenderPageNumbers,\n    isRenderQueueIdle,\n    onPageRenderTiming: handleScheduledPageRenderTiming,\n  } = usePdfPageRenderScheduler({\n    isPaused: isPageRenderingPaused,\n    pageNumbers: visiblePageNumbers,\n    warmPageNumbers:\n      performanceOptions?.directionAwarePreRender === false\n        ? []\n        : warmPageNumbers,\n    lowPriorityPageNumbers:\n      performanceOptions?.directionAwarePreRender === false\n        ? []\n        : preloadPageNumbers,\n    scale: effectiveRenderScale,\n    rotation: layout.rotation,\n    devicePixelRatio: layout.pageDevicePixelRatio,\n    resetKey: document,\n    maxRunning: PDF_SCROLLING_PAGE_RENDER_CONCURRENCY,\n    maxLowPriorityRunning:\n      performanceOptions?.directionAwarePreRender === false ? 0 : 1,\n  });\n  useKeyedMountEffect(\n    joinEffectKey([isShellMotionActive, isRenderQueueIdle, isZoomTransitioning]),\n    () => {\n      const nextShouldHold =\n        isShellMotionActive ||\n        isZoomTransitioning ||\n        (shouldHoldPageWindowForRaster && !isRenderQueueIdle);\n      setShouldHoldPageWindowForRaster((previous) =>\n        previous === nextShouldHold ? previous : nextShouldHold,\n      );\n    },\n  );\n  const handlePageRenderTiming = React.useCallback(\n    (timing: PdfPageRenderTiming) => {\n      // Attribute raster work to a live zoom flight — with the flight-time\n      // holds above this stays at 0, and the flight recorder proves it.\n      notePdfZoomMotionPageRender(timing);\n      handleScheduledPageRenderTiming(timing);\n      onPageRenderTiming?.(timing);\n    },\n    [handleScheduledPageRenderTiming, onPageRenderTiming],\n  );\n  const readSettleSnapshot = React.useCallback(() => {\n    const metrics = getScrollMetrics();\n    const viewportElement = getViewportElement();\n\n    return [\n      metrics.scrollTop,\n      metrics.physicalScrollTop,\n      metrics.scrollPageOffset,\n      metrics.viewportHeight,\n      viewportElement?.scrollHeight ?? 0,\n      viewportElement?.clientHeight ?? 0,\n      layout.pageLayout.totalHeight,\n      layout.pageLayout.maxPageWidth,\n    ];\n  }, [\n    getScrollMetrics,\n    getViewportElement,\n    layout.pageLayout.maxPageWidth,\n    layout.pageLayout.totalHeight,\n  ]);\n  usePdfViewerTelemetry({\n    activeRenderPageNumbers,\n    displayScale: layout.displayScale,\n    documentKey,\n    getDocumentSurfaceElement: () => documentSurfaceElementRef.current,\n    getScrollMetrics,\n    getViewportElement,\n    layout: layout.pageLayout,\n    pageDevicePixelRatio: layout.pageDevicePixelRatio,\n    renderPageNumbers,\n    renderScale: effectiveRenderScale,\n    rendererFrame: layout.rendererFrame,\n    visiblePageNumbers,\n  });\n  const scrollInteractionElementRef = React.useRef<HTMLDivElement | null>(null);\n  const setScrollInteractionElement = React.useCallback(\n    (element: HTMLDivElement | null) => {\n      scrollInteractionElementRef.current = element;\n    },\n    [],\n  );\n  const suspendScrollInteractions = usePdfScrollInteractionSuspension(\n    scrollInteractionElementRef,\n  );\n\n  useMountEffect(() => () => {\n    documentSurfaceElementRef.current?.removeEventListener(\n      FILE_VIEWER_BEFORE_LAYOUT_MOTION_EVENT,\n      handleBeforeLayoutMotion,\n    );\n    documentSurfaceElementRef.current = null;\n  });\n\n  // displayScale now changes at slide START (commit-then-relax), so this\n  // idle re-measure must not clobber the motion's fixed-point anchor; the\n  // transitioning effect below owns the anchor for the whole motion.\n  useKeyedMountEffect(\n    joinEffectKey([\n      document.numPages,\n      layout.displayScale,\n      layout.rendererFrame.isTransitioning,\n      layout.rotation,\n      measureScroll,\n      writeDocumentAnchorBlockOffset,\n      viewportElement,\n    ]),\n    () => {\n      measureScroll();\n      if (layout.rendererFrame.isTransitioning) return;\n      writeDocumentAnchorBlockOffset();\n    },\n  );\n\n  // The before-layout-motion event fires before the layout jump and captures\n  // the pre-toggle scroll; this effect runs inside the slide-start commit\n  // AFTER the scroll rebase (hook order puts the scroll layout effect first),\n  // so it can pin the transform to the rebase's exact fixed point before the\n  // first frame paints.\n  useKeyedLayoutEffect(\n    joinEffectKey([\n      layout.rendererFrame.documentTransition.transitionId,\n      layout.rendererFrame.isTransitioning,\n      writeMotionAnchorBlockOffset,\n    ]),\n    () => {\n      if (!layout.rendererFrame.isTransitioning) return;\n      writeMotionAnchorBlockOffset();\n    },\n  );\n\n  const handleViewportScroll = React.useCallback(() => {\n    suspendScrollInteractions();\n    const result = handleScroll();\n    if (result?.source === \"internal\") return;\n    // While the shell motion is in flight the anchor is the motion's fixed\n    // point; a user scroll mid-slide must not retarget it to the marker.\n    if (!layout.rendererFrame.isTransitioning) {\n      writeDocumentAnchorBlockOffset();\n    }\n    measureVisiblePages();\n  }, [\n    handleScroll,\n    layout.rendererFrame.isTransitioning,\n    measureVisiblePages,\n    suspendScrollInteractions,\n    writeDocumentAnchorBlockOffset,\n  ]);\n\n  const handle = React.useMemo<PdfViewerHandle>(\n    () => ({\n      scrollToPage,\n      scrollToPageArea,\n      getViewportElement,\n    }),\n    [getViewportElement, scrollToPage, scrollToPageArea],\n  );\n\n  return {\n    currentPage,\n    handle,\n    handleViewportScroll,\n    pagesLayerProps: {\n      activeRenderPageNumbers,\n      containerRef: layout.containerRef,\n      devicePixelRatio: layout.pageDevicePixelRatio,\n      document,\n      documentAlign: layout.rendererFrame.align,\n      documentKey,\n      isFitWidth: layout.isFitWidth,\n      isLayoutTransitioning: layout.rendererFrame.isTransitioning,\n      isZoomTransitioning,\n      layout: layout.pageLayout,\n      motionProbePageNumber,\n      onPageRenderTiming: handlePageRenderTiming,\n      physicalScrollHeight: getPdfPhysicalScrollHeight({\n        totalHeight: layout.pageLayout.totalHeight,\n        viewportHeight: viewportElement?.clientHeight ?? 0,\n      }),\n      readSettleSnapshot,\n      renderCache: shouldUseRenderedPageCache ? renderedPageCache : undefined,\n      renderPageNumbers,\n      renderPageOverlay,\n      renderScale: effectiveRenderScale,\n      resolveSurfaceMotionStyle: layout.resolveSurfaceMotionStyle,\n      rotation: layout.rotation,\n      scale: layout.displayScale,\n      scrollPageOffset,\n      setDocumentSurfaceElement,\n      setScrollInteractionElement,\n      setPageSize: layout.setPageSize,\n      viewportHeight: viewportElement?.clientHeight ?? 0,\n      visiblePageNumbers,\n    },\n    setViewportElement,\n    zoomControls,\n  };\n}\n\nfunction usePdfShellMotionRenderPause(isShellMotionActive: boolean) {\n  const [isSettling, setIsSettling] = React.useState(false);\n\n  useKeyedMountEffect(joinEffectKey([isShellMotionActive, isSettling]), () => {\n    if (isShellMotionActive) {\n      setIsSettling(true);\n      return;\n    }\n\n    if (!isSettling) return;\n\n    const timer = setTimeout(() => {\n      setIsSettling(false);\n    }, PDF_SHELL_MOTION_RENDER_RESUME_DELAY_MS);\n\n    return () => clearTimeout(timer);\n  });\n\n  return isShellMotionActive || isSettling;\n}\n\nfunction usePdfScrollInteractionSuspension(\n  scrollInteractionElementRef: React.RefObject<HTMLElement | null>,\n) {\n  const restoreTimeoutRef = React.useRef<number | null>(null);\n  const scrollElementRef = React.useRef<HTMLElement | null>(null);\n\n  const suspendScrollInteractions = React.useCallback(() => {\n    const scrollElement = scrollInteractionElementRef.current;\n    if (!scrollElement) return;\n\n    if (restoreTimeoutRef.current !== null) {\n      window.clearTimeout(restoreTimeoutRef.current);\n    }\n    scrollElementRef.current = scrollElement;\n    scrollElement.style.pointerEvents = \"none\";\n    if (isMobileSafari()) {\n      scrollElement.style.overflowX = \"hidden\";\n    }\n    restoreTimeoutRef.current = window.setTimeout(() => {\n      restoreTimeoutRef.current = null;\n      restorePdfScrollInteractions(scrollElementRef.current);\n      scrollElementRef.current = null;\n    }, 120);\n  }, [scrollInteractionElementRef]);\n\n  useMountEffect(() => () => {\n    if (restoreTimeoutRef.current !== null) {\n      window.clearTimeout(restoreTimeoutRef.current);\n      restoreTimeoutRef.current = null;\n    }\n    restorePdfScrollInteractions(scrollElementRef.current);\n    scrollElementRef.current = null;\n  });\n\n  return suspendScrollInteractions;\n}\n\nfunction restorePdfScrollInteractions(element: HTMLElement | null) {\n  if (!element) return;\n  element.style.removeProperty(\"pointer-events\");\n  element.style.removeProperty(\"overflow-x\");\n}\n\nfunction isMobileSafari() {\n  if (typeof navigator === \"undefined\") return false;\n  const userAgent = navigator.userAgent;\n  return (\n    /Safari/i.test(userAgent) &&\n    /Mobile/i.test(userAgent) &&\n    !/CriOS|FxiOS|EdgiOS/i.test(userAgent)\n  );\n}\n",
      "type": "registry:ui",
      "target": "@ui/pdf-viewer-document-runtime.ts"
    },
    {
      "path": "registry/new-york-v4/ui/pdf-viewer-pages-layer.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { useKeyedLayoutEffect } from \"@/hooks/use-keyed-layout-effect\";\nimport { joinEffectKey } from \"@/lib/effect-key\";\nimport { cn } from \"@/lib/utils\";\n\nimport type { FileViewerDocumentAlign } from \"./file-viewer-renderer-contract\";\nimport type {\n  FileViewerDocumentSurfaceMotionResolver,\n  FileViewerDocumentSurfaceSettleSnapshotReader,\n} from \"./file-viewer-motion-kernel\";\nimport { useOptionalFileViewerRendererEnvironment } from \"./file-viewer-renderer-frame\";\nimport {\n  getPdfRenderedPageWindow,\n  type PdfPageLayoutModel,\n} from \"./pdf-viewer-layout\";\nimport { PdfPage } from \"./pdf-viewer-page\";\nimport type { PdfRenderedPageCache } from \"./pdf-viewer-render-cache\";\nimport { PageSkeleton } from \"./pdf-viewer-states\";\nimport type {\n  PageOverlayProps,\n  PdfPageRenderTiming,\n  PdfPageSize,\n} from \"./pdf-viewer-types\";\nimport type { PdfDocument } from \"./pdf-viewer-document-resource\";\nimport { PDF_PAGE_HORIZONTAL_PADDING } from \"./pdf-viewer-scale\";\n\nconst PDF_PAGE_RING_OUTSET_PX = 1;\n\nexport type PdfDocumentPagesLayerProps = {\n  activeRenderPageNumbers: readonly number[];\n  containerRef: React.RefCallback<HTMLDivElement>;\n  devicePixelRatio: number;\n  document: PdfDocument;\n  documentAlign: FileViewerDocumentAlign | null;\n  documentKey: string;\n  /** The pages are at their fit-width scale (the shell motion's domain). */\n  isFitWidth: boolean;\n  isLayoutTransitioning: boolean;\n  /** A toolbar zoom step's FLIP relax is in flight (pdf-viewer-zoom-motion). */\n  isZoomTransitioning: boolean;\n  layout: PdfPageLayoutModel;\n  /** The page the motion kernel should probe for on-screen telemetry. */\n  motionProbePageNumber: number;\n  onPageRenderTiming?: (timing: PdfPageRenderTiming) => void;\n  physicalScrollHeight: number;\n  renderCache?: PdfRenderedPageCache;\n  renderPageNumbers: readonly number[];\n  readSettleSnapshot: FileViewerDocumentSurfaceSettleSnapshotReader;\n  renderPageOverlay?: (props: PageOverlayProps) => React.ReactNode;\n  renderScale: number;\n  resolveSurfaceMotionStyle: FileViewerDocumentSurfaceMotionResolver;\n  rotation: number;\n  scale: number;\n  scrollPageOffset: number;\n  setDocumentSurfaceElement: React.RefCallback<HTMLElement>;\n  setScrollInteractionElement: React.RefCallback<HTMLDivElement>;\n  setPageSize: (pageNumber: number, size: PdfPageSize) => void;\n  viewportHeight: number;\n  visiblePageNumbers: readonly number[];\n};\n\nexport function PdfDocumentPagesLayer({\n  activeRenderPageNumbers,\n  containerRef,\n  devicePixelRatio,\n  document,\n  documentAlign,\n  documentKey,\n  isFitWidth,\n  isLayoutTransitioning,\n  isZoomTransitioning,\n  layout,\n  motionProbePageNumber,\n  onPageRenderTiming,\n  physicalScrollHeight,\n  renderCache,\n  renderPageNumbers,\n  readSettleSnapshot,\n  renderPageOverlay,\n  renderScale,\n  resolveSurfaceMotionStyle,\n  rotation,\n  scale,\n  scrollPageOffset,\n  setDocumentSurfaceElement,\n  setScrollInteractionElement,\n  setPageSize,\n  viewportHeight,\n  visiblePageNumbers,\n}: PdfDocumentPagesLayerProps) {\n  const visiblePageNumberSet = React.useMemo(\n    () => new Set(visiblePageNumbers),\n    [visiblePageNumbers],\n  );\n  const { registerDocumentSurface } =\n    useOptionalFileViewerRendererEnvironment();\n  const [visualStageElement, setVisualStageElementState] =\n    React.useState<HTMLElement | null>(null);\n  const setVisualStageElement = React.useCallback(\n    (element: HTMLElement | null) => {\n      setVisualStageElementState((previousElement) =>\n        previousElement === element ? previousElement : element,\n      );\n      setDocumentSurfaceElement(element);\n    },\n    [setDocumentSurfaceElement],\n  );\n  const motionProbePageNumberRef = React.useRef(motionProbePageNumber);\n  motionProbePageNumberRef.current = motionProbePageNumber;\n  // Probe the marker page's element; while a motion is in flight the marker\n  // page may sit outside the rendered window, so fall back to any visible\n  // page, then any rendered page.\n  const getMotionProbeElement = React.useCallback(() => {\n    if (!visualStageElement) return null;\n    const pageNumber = motionProbePageNumberRef.current;\n    return (\n      visualStageElement.querySelector<HTMLElement>(\n        `[data-slot=\"pdf-page\"][data-page=\"${pageNumber}\"]`,\n      ) ??\n      visualStageElement.querySelector<HTMLElement>(\n        '[data-slot=\"pdf-page-slot\"][data-visible] [data-slot=\"pdf-page\"]',\n      ) ??\n      visualStageElement.querySelector<HTMLElement>('[data-slot=\"pdf-page\"]')\n    );\n  }, [visualStageElement]);\n  const documentSurfaceKey = visualStageElement\n    ? joinEffectKey([\n        registerDocumentSurface,\n        getMotionProbeElement,\n        readSettleSnapshot,\n        resolveSurfaceMotionStyle,\n        visualStageElement,\n      ])\n    : null;\n  useKeyedLayoutEffect(documentSurfaceKey, () => {\n    if (!visualStageElement) return;\n    return registerDocumentSurface({\n      element: visualStageElement,\n      getMotionProbeElement,\n      readSettleSnapshot,\n      resolveMotionStyle: resolveSurfaceMotionStyle,\n    });\n  });\n  const activeRenderPageNumberSet = React.useMemo(\n    () => new Set(activeRenderPageNumbers),\n    [activeRenderPageNumbers],\n  );\n  const renderedWindow = React.useMemo(\n    () =>\n      getPdfRenderedPageWindow({\n        layout,\n        pageNumbers: renderPageNumbers,\n        physicalScrollHeight,\n        scrollPageOffset,\n        viewportHeight,\n      }),\n    [\n      layout,\n      physicalScrollHeight,\n      renderPageNumbers,\n      scrollPageOffset,\n      viewportHeight,\n    ],\n  );\n  const isInsideDocumentFrame = documentAlign !== null;\n  // Shell motion counter-transforms the visual stage; a zoom step relaxes a\n  // FLIP on the visual clip itself. Either way the in-flight paint is larger\n  // than the committed box on at least one leg, so the clip must let go for\n  // the duration.\n  const isVisualClipReleased = isLayoutTransitioning || isZoomTransitioning;\n  const scrollRangeStyle = {\n    contain: \"layout size style\",\n    height: physicalScrollHeight,\n    minWidth: layout.maxPageWidth,\n    width: layout.maxPageWidth,\n  } satisfies React.CSSProperties;\n  const visualClipStyle = {\n    // The motion surface commits the target page width before the first\n    // paint, then counter-scales back to the previous width. A target-sized\n    // paint clip would cut off that enlarged opening frame even though the\n    // page geometry itself is continuous. Relinquish paint containment only\n    // while the counter-transform is active; identity fits the clip again at\n    // settle, so restoring it cannot move a pixel.\n    contain: isVisualClipReleased ? \"style\" : \"paint style\",\n    left: -PDF_PAGE_RING_OUTSET_PX,\n    overflow: isVisualClipReleased ? \"visible\" : \"clip\",\n    paddingInline: PDF_PAGE_RING_OUTSET_PX,\n    right: -PDF_PAGE_RING_OUTSET_PX,\n  } satisfies React.CSSProperties;\n  const visualStageStyle = {\n    height: physicalScrollHeight,\n    minWidth: layout.maxPageWidth,\n    width: layout.maxPageWidth,\n  } satisfies React.CSSProperties;\n\n  const documentContent = (\n    <div\n      data-slot=\"pdf-viewer-scroll-range\"\n      // The document surface is a CAMERA view, not a laid-out column: a zoom\n      // step shrinks it about the viewport centre, so whenever it is smaller\n      // than the pane the leftover space splits evenly. Auto margins (never\n      // justify/align-content) do exactly that and collapse to 0 once the\n      // surface overflows, so an overflowing document still starts at the\n      // scroll origin and every pixel of it stays reachable. The inset's\n      // `align` keeps governing the FRAME, not this surface.\n      //\n      // The BLOCK axis centres only outside fit-width. At fit-width a pane\n      // resize re-fits the document, which would move a block-centred surface\n      // by half the height delta — motion the shell's fit-width transform does\n      // not model, so it would land as a hop mid-slide. Zoomed, that transform\n      // is identity and the document's height is pane-independent, so the\n      // centring is inert during a slide; the fit↔zoom switch itself rides the\n      // zoom FLIP, which measures painted rects and absorbs the margin change.\n      className={cn(\"relative mx-auto shrink-0\", !isFitWidth && \"my-auto\")}\n      style={scrollRangeStyle}\n    >\n      <div\n        data-slot=\"pdf-viewer-visual-clip\"\n        data-layout-transitioning={isLayoutTransitioning ? \"\" : undefined}\n        className=\"absolute inset-0\"\n        style={visualClipStyle}\n      >\n        <div\n          ref={setVisualStageElement}\n          data-slot=\"pdf-viewer-visual-stage\"\n          className=\"relative\"\n          style={visualStageStyle}\n        >\n          {renderedWindow ? (\n            <div\n              ref={setScrollInteractionElement}\n              data-slot=\"pdf-page-window\"\n              className=\"absolute inset-0\"\n              style={{\n                contain: \"layout style\",\n                isolation: \"isolate\",\n              }}\n            >\n              {/* Pages sit at their settled layout positions for the whole\n                  motion (commit-then-relax); the kernel's single surface\n                  transform on the visual stage is the only in-flight style. */}\n              {renderedWindow.pages.map((page) => (\n                <div\n                  key={page.pageNumber}\n                  className=\"absolute left-1/2 flex -translate-x-1/2 items-start justify-center\"\n                  data-layout-transitioning={\n                    isLayoutTransitioning ? \"\" : undefined\n                  }\n                  data-slot=\"pdf-page-slot\"\n                  data-page-number={page.pageNumber}\n                  data-visible={\n                    visiblePageNumberSet.has(page.pageNumber) ? \"\" : undefined\n                  }\n                  style={{\n                    top: renderedWindow.beforeHeight + page.windowTop,\n                    width: page.width,\n                    height: page.height,\n                  }}\n                >\n                  {activeRenderPageNumberSet.has(page.pageNumber) ? (\n                    <React.Suspense fallback={<PageSkeleton />}>\n                      <PdfPage\n                        document={document}\n                        documentKey={documentKey}\n                        pageNumber={page.pageNumber}\n                        scale={scale}\n                        renderScale={renderScale}\n                        isLayoutTransitioning={isLayoutTransitioning}\n                        rotation={rotation}\n                        devicePixelRatio={devicePixelRatio}\n                        renderCache={renderCache}\n                        renderOverlay={renderPageOverlay}\n                        onRenderTiming={onPageRenderTiming}\n                        onSize={setPageSize}\n                      />\n                    </React.Suspense>\n                  ) : (\n                    <PageSkeleton />\n                  )}\n                </div>\n              ))}\n            </div>\n          ) : null}\n        </div>\n      </div>\n    </div>\n  );\n\n  return (\n    <div\n      ref={containerRef}\n      data-slot=\"pdf-viewer-fit-width-measure\"\n      // Flex column purely so the surface's block-axis auto margins have free\n      // space to split: a block container gives its child no block centring.\n      className={cn(\n        \"relative flex min-w-0 flex-col\",\n        isInsideDocumentFrame && \"h-full w-full\",\n      )}\n      style={{ paddingInline: PDF_PAGE_HORIZONTAL_PADDING / 2 }}\n    >\n      {documentContent}\n    </div>\n  );\n}\n",
      "type": "registry:ui",
      "target": "@ui/pdf-viewer-pages-layer.tsx"
    },
    {
      "path": "registry/new-york-v4/ui/pdf-viewer-render-scheduler.ts",
      "content": "import * as React from \"react\";\n\nimport type { PdfPageRenderTiming } from \"./pdf-viewer-types\";\nimport { useKeyedMountEffect } from \"@/hooks/use-keyed-mount-effect\";\nimport { useKeyedLayoutEffect } from \"@/hooks/use-keyed-layout-effect\";\nimport { joinEffectKey } from \"@/lib/effect-key\";\n\nexport const PDF_PAGE_RENDER_CONCURRENCY = 2;\nexport const PDF_SCROLLING_PAGE_RENDER_CONCURRENCY = 4;\n\ntype PdfPageRenderKeyInput = {\n  pageNumber: number;\n  scale: number;\n  rotation: number;\n  devicePixelRatio: number;\n};\n\ntype PdfPageRenderRequest = PdfPageRenderKeyInput;\n\nexport function getPdfPageRenderKey({\n  pageNumber,\n  scale,\n  rotation,\n  devicePixelRatio,\n}: PdfPageRenderKeyInput) {\n  return `${pageNumber}:${scale}:${rotation}:${devicePixelRatio}`;\n}\n\nexport function usePdfPageRenderScheduler({\n  isPaused = false,\n  pageNumbers,\n  warmPageNumbers = [],\n  lowPriorityPageNumbers = [],\n  scale,\n  rotation,\n  devicePixelRatio,\n  resetKey,\n  maxRunning = PDF_PAGE_RENDER_CONCURRENCY,\n  maxLowPriorityRunning = 1,\n}: {\n  pageNumbers: readonly number[];\n  warmPageNumbers?: readonly number[];\n  lowPriorityPageNumbers?: readonly number[];\n  isPaused?: boolean;\n  scale: number;\n  rotation: number;\n  devicePixelRatio: number;\n  resetKey: unknown;\n  maxRunning?: number;\n  maxLowPriorityRunning?: number;\n}) {\n  const requestedRenders = React.useMemo<PdfPageRenderRequest[]>(\n    () =>\n      mergePdfPageNumbers(pageNumbers).map((pageNumber) => ({\n        pageNumber,\n        scale,\n        rotation,\n        devicePixelRatio,\n      })),\n    [devicePixelRatio, pageNumbers, rotation, scale],\n  );\n  const warmRequestedRenders = React.useMemo<PdfPageRenderRequest[]>(() => {\n    const primaryPageNumberSet = new Set(pageNumbers);\n    return mergePdfPageNumbers(warmPageNumbers)\n      .filter((pageNumber) => !primaryPageNumberSet.has(pageNumber))\n      .map((pageNumber) => ({\n        pageNumber,\n        scale,\n        rotation,\n        devicePixelRatio,\n      }));\n  }, [devicePixelRatio, pageNumbers, rotation, scale, warmPageNumbers]);\n  const lowPriorityRequestedRenders = React.useMemo<\n    PdfPageRenderRequest[]\n  >(() => {\n    const primaryPageNumberSet = new Set(pageNumbers);\n    const warmPageNumberSet = new Set(warmPageNumbers);\n    return mergePdfPageNumbers(lowPriorityPageNumbers)\n      .filter(\n        (pageNumber) =>\n          !primaryPageNumberSet.has(pageNumber) &&\n          !warmPageNumberSet.has(pageNumber),\n      )\n      .map((pageNumber) => ({\n        pageNumber,\n        scale,\n        rotation,\n        devicePixelRatio,\n      }));\n  }, [\n    devicePixelRatio,\n    lowPriorityPageNumbers,\n    pageNumbers,\n    rotation,\n    scale,\n    warmPageNumbers,\n  ]);\n  const allRequestedRenders = React.useMemo(\n    () => [\n      ...requestedRenders,\n      ...warmRequestedRenders,\n      ...lowPriorityRequestedRenders,\n    ],\n    [lowPriorityRequestedRenders, requestedRenders, warmRequestedRenders],\n  );\n  const requestedRendersRef = React.useRef(allRequestedRenders);\n  const resetKeyRef = React.useRef(resetKey);\n  useKeyedLayoutEffect(joinEffectKey([allRequestedRenders, resetKey]), () => {\n    requestedRendersRef.current = allRequestedRenders;\n    resetKeyRef.current = resetKey;\n  });\n\n  const [state, setState] = React.useState<{\n    resetKey: unknown;\n    renderedByKey: ReadonlyMap<string, PdfPageRenderRequest>;\n  }>(() => ({ resetKey, renderedByKey: new Map() }));\n  const emptyRenderedByKey = React.useMemo<\n    ReadonlyMap<string, PdfPageRenderRequest>\n  >(() => new Map(), []);\n  const renderedByKey = Object.is(state.resetKey, resetKey)\n    ? state.renderedByKey\n    : emptyRenderedByKey;\n\n  useKeyedMountEffect(joinEffectKey([allRequestedRenders, resetKey]), () => {\n    setState((previousState) => {\n      if (!Object.is(previousState.resetKey, resetKey)) {\n        return { resetKey, renderedByKey: new Map() };\n      }\n\n      const renderedByKey = new Map<string, PdfPageRenderRequest>();\n      for (const [key, rendered] of previousState.renderedByKey) {\n        if (\n          allRequestedRenders.some((request) =>\n            doesRenderedPageKeepRequestMounted(rendered, request),\n          )\n        ) {\n          renderedByKey.set(key, rendered);\n        }\n      }\n\n      return areMapsEqual(previousState.renderedByKey, renderedByKey)\n        ? previousState\n        : { resetKey, renderedByKey };\n    });\n  });\n\n  const renderWindow = React.useMemo(() => {\n    const renderedPageNumbers: number[] = [];\n    const stalePageNumbers: number[] = [];\n    const pendingPageNumbers: number[] = [];\n    const warmRenderedPageNumbers: number[] = [];\n    const warmStalePageNumbers: number[] = [];\n    const warmPendingPageNumbers: number[] = [];\n    const lowPriorityRenderedPageNumbers: number[] = [];\n    const lowPriorityStalePageNumbers: number[] = [];\n    const lowPriorityPendingPageNumbers: number[] = [];\n\n    for (const request of requestedRenders) {\n      if (isRenderRequestSatisfied(request, renderedByKey)) {\n        renderedPageNumbers.push(request.pageNumber);\n      } else if (isRenderRequestMountedStale(request, renderedByKey)) {\n        stalePageNumbers.push(request.pageNumber);\n      } else {\n        pendingPageNumbers.push(request.pageNumber);\n      }\n    }\n    for (const request of warmRequestedRenders) {\n      if (isRenderRequestSatisfied(request, renderedByKey)) {\n        warmRenderedPageNumbers.push(request.pageNumber);\n      } else if (isRenderRequestMountedStale(request, renderedByKey)) {\n        warmStalePageNumbers.push(request.pageNumber);\n      } else {\n        warmPendingPageNumbers.push(request.pageNumber);\n      }\n    }\n    for (const request of lowPriorityRequestedRenders) {\n      if (isRenderRequestSatisfied(request, renderedByKey)) {\n        lowPriorityRenderedPageNumbers.push(request.pageNumber);\n      } else if (isRenderRequestMountedStale(request, renderedByKey)) {\n        lowPriorityStalePageNumbers.push(request.pageNumber);\n      } else {\n        lowPriorityPendingPageNumbers.push(request.pageNumber);\n      }\n    }\n\n    const maxPrimaryRunning = Math.max(1, maxRunning);\n    const activePrimaryPendingCount = isPaused\n      ? 0\n      : Math.min(pendingPageNumbers.length, maxPrimaryRunning);\n    const activePrimaryPageNumbers = [\n      ...renderedPageNumbers,\n      ...stalePageNumbers,\n      ...pendingPageNumbers.slice(0, activePrimaryPendingCount),\n    ];\n    const remainingWarmSlots = Math.max(\n      0,\n      maxRunning - activePrimaryPendingCount,\n    );\n    const activeWarmPendingCount = isPaused\n      ? 0\n      : Math.min(warmPendingPageNumbers.length, remainingWarmSlots);\n    const activeWarmPageNumbers = [\n      ...warmRenderedPageNumbers,\n      ...warmStalePageNumbers,\n      ...warmPendingPageNumbers.slice(0, activeWarmPendingCount),\n    ];\n    const activeLowPriorityPageNumbers =\n      isPaused || activePrimaryPendingCount + activeWarmPendingCount > 0\n        ? [...lowPriorityRenderedPageNumbers, ...lowPriorityStalePageNumbers]\n        : [\n            ...lowPriorityRenderedPageNumbers,\n            ...lowPriorityStalePageNumbers,\n            ...lowPriorityPendingPageNumbers.slice(\n              0,\n              Math.max(0, maxLowPriorityRunning),\n            ),\n          ];\n\n    return {\n      activePageNumbers: mergePdfPageNumbers([\n        ...activePrimaryPageNumbers,\n        ...activeWarmPageNumbers,\n        ...activeLowPriorityPageNumbers,\n      ]),\n      pendingPageNumbers: mergePdfPageNumbers([\n        ...pendingPageNumbers,\n        ...warmPendingPageNumbers,\n        ...lowPriorityPendingPageNumbers,\n      ]),\n    };\n  }, [\n    isPaused,\n    lowPriorityRequestedRenders,\n    maxLowPriorityRunning,\n    maxRunning,\n    renderedByKey,\n    requestedRenders,\n    warmRequestedRenders,\n  ]);\n\n  const onPageRenderTiming = React.useCallback(\n    (timing: PdfPageRenderTiming) => {\n      const key = getPdfPageRenderKey(timing);\n\n      setState((previousState) => {\n        const resetKey = resetKeyRef.current;\n        const previousRenderedByKey = Object.is(\n          previousState.resetKey,\n          resetKey,\n        )\n          ? previousState.renderedByKey\n          : new Map<string, PdfPageRenderRequest>();\n        const nextRenderedByKey = new Map(previousRenderedByKey);\n        const rendered: PdfPageRenderRequest = {\n          pageNumber: timing.pageNumber,\n          scale: timing.scale,\n          rotation: timing.rotation,\n          devicePixelRatio: timing.devicePixelRatio,\n        };\n\n        if (\n          timing.status === \"rendered\" &&\n          requestedRendersRef.current.some((request) =>\n            doesRenderedPageKeepRequestMounted(rendered, request),\n          )\n        ) {\n          nextRenderedByKey.set(key, rendered);\n        } else {\n          nextRenderedByKey.delete(key);\n        }\n\n        return areMapsEqual(previousRenderedByKey, nextRenderedByKey)\n          ? previousState\n          : { resetKey, renderedByKey: nextRenderedByKey };\n      });\n    },\n    [],\n  );\n\n  return {\n    activePageNumbers: renderWindow.activePageNumbers,\n    isRenderQueueIdle: renderWindow.pendingPageNumbers.length === 0,\n    onPageRenderTiming,\n    pendingPageNumbers: renderWindow.pendingPageNumbers,\n  };\n}\n\nfunction mergePdfPageNumbers(pageNumbers: readonly number[]) {\n  return [...new Set(pageNumbers)].sort((left, right) => left - right);\n}\n\nfunction isRenderRequestSatisfied(\n  request: PdfPageRenderRequest,\n  renderedByKey: ReadonlyMap<string, PdfPageRenderRequest>,\n) {\n  for (const rendered of renderedByKey.values()) {\n    if (doesRenderedPageSatisfyRequest(rendered, request)) return true;\n  }\n  return false;\n}\n\nfunction isRenderRequestMountedStale(\n  request: PdfPageRenderRequest,\n  renderedByKey: ReadonlyMap<string, PdfPageRenderRequest>,\n) {\n  for (const rendered of renderedByKey.values()) {\n    if (\n      !doesRenderedPageSatisfyRequest(rendered, request) &&\n      doesRenderedPageKeepRequestMounted(rendered, request)\n    ) {\n      return true;\n    }\n  }\n  return false;\n}\n\nfunction doesRenderedPageSatisfyRequest(\n  rendered: PdfPageRenderRequest,\n  request: PdfPageRenderRequest,\n) {\n  return (\n    rendered.pageNumber === request.pageNumber &&\n    rendered.scale === request.scale &&\n    rendered.rotation === request.rotation &&\n    rendered.devicePixelRatio >= request.devicePixelRatio\n  );\n}\n\nfunction doesRenderedPageKeepRequestMounted(\n  rendered: PdfPageRenderRequest,\n  request: PdfPageRenderRequest,\n) {\n  return (\n    rendered.pageNumber === request.pageNumber &&\n    rendered.rotation === request.rotation\n  );\n}\n\nfunction areMapsEqual(\n  left: ReadonlyMap<string, PdfPageRenderRequest>,\n  right: ReadonlyMap<string, PdfPageRenderRequest>,\n) {\n  if (left.size !== right.size) return false;\n  for (const [key, value] of left) {\n    if (right.get(key) !== value) return false;\n  }\n  return true;\n}\n",
      "type": "registry:ui",
      "target": "@ui/pdf-viewer-render-scheduler.ts"
    },
    {
      "path": "registry/new-york-v4/ui/pdf-viewer-render-cache.ts",
      "content": "import { useKeyedMountEffect } from \"@/hooks/use-keyed-mount-effect\";\nimport { joinEffectKey } from \"@/lib/effect-key\";\n\nexport const PDF_RENDERED_PAGE_CACHE_MAX_ENTRIES = 16;\nexport const PDF_RENDERED_PAGE_CACHE_MAX_PIXELS = 24_000_000;\n// Only close lower-resolution renders of the same page may be stretched as\n// resize previews; thumbnail-resolution bitmaps below this ratio would blur.\nexport const PDF_RENDERED_PAGE_PREVIEW_MIN_SCALE_RATIO = 0.5;\n\nexport type PdfRenderedPageSignature = {\n  documentKey: string;\n  pageNumber: number;\n  scale: number;\n  rotation: number;\n  devicePixelRatio: number;\n  viewportWidth: number;\n  viewportHeight: number;\n};\n\nexport type PdfRenderedPageCache = {\n  clock: number;\n  entries: Map<string, PdfRenderedPageCacheEntry>;\n  resetKey: unknown;\n};\n\nexport type PdfRenderedPageCacheEntry = PdfRenderedPageSignature & {\n  canvas: HTMLCanvasElement;\n  lastUsed: number;\n  pixels: number;\n};\n\ntype PdfRenderedPageCacheRecord = {\n  cache: PdfRenderedPageCache;\n  retainCount: number;\n  resetKey: unknown;\n};\n\nconst objectCacheRecords = new WeakMap<object, PdfRenderedPageCacheRecord>();\nconst primitiveCacheRecords = new Map<unknown, PdfRenderedPageCacheRecord>();\n\nexport function createPdfRenderedPageCache(\n  resetKey: unknown,\n): PdfRenderedPageCache {\n  return {\n    clock: 0,\n    entries: new Map(),\n    resetKey,\n  };\n}\n\nexport function usePdfRenderedPageCache(resetKey: unknown) {\n  const record = getPdfRenderedPageCacheRecord(resetKey);\n\n  useKeyedMountEffect(joinEffectKey([record]), () => {\n    record.retainCount += 1;\n    return () => releasePdfRenderedPageCacheRecord(record);\n  });\n\n  return record.cache;\n}\n\nexport function readPdfRenderedPageCache(\n  cache: PdfRenderedPageCache | undefined,\n  requested: PdfRenderedPageSignature,\n) {\n  if (!cache) return null;\n\n  let bestEntry: PdfRenderedPageCacheEntry | null = null;\n  for (const entry of cache.entries.values()) {\n    if (!doesRenderedPageSatisfyRequest(entry, requested)) continue;\n    if (\n      !bestEntry ||\n      entry.pixels < bestEntry.pixels ||\n      (entry.pixels === bestEntry.pixels && entry.lastUsed > bestEntry.lastUsed)\n    ) {\n      bestEntry = entry;\n    }\n  }\n\n  if (!bestEntry) return null;\n  cache.clock += 1;\n  bestEntry.lastUsed = cache.clock;\n  return bestEntry;\n}\n\nexport function readPdfRenderedPageCachePreview(\n  cache: PdfRenderedPageCache | undefined,\n  requested: PdfRenderedPageSignature,\n) {\n  if (!cache) return null;\n\n  let bestEntry: PdfRenderedPageCacheEntry | null = null;\n  for (const entry of cache.entries.values()) {\n    if (!doesRenderedPageMatchPreview(entry, requested)) continue;\n    if (!bestEntry || entry.pixels > bestEntry.pixels) {\n      bestEntry = entry;\n    }\n  }\n\n  if (!bestEntry) return null;\n  cache.clock += 1;\n  bestEntry.lastUsed = cache.clock;\n  return bestEntry;\n}\n\nexport function writePdfRenderedPageCache({\n  cache,\n  rendered,\n  sourceCanvas,\n}: {\n  cache: PdfRenderedPageCache | undefined;\n  rendered: PdfRenderedPageSignature;\n  sourceCanvas: HTMLCanvasElement;\n}) {\n  if (!cache || sourceCanvas.width <= 0 || sourceCanvas.height <= 0) return;\n\n  const canvas = document.createElement(\"canvas\");\n  canvas.width = sourceCanvas.width;\n  canvas.height = sourceCanvas.height;\n  const context = canvas.getContext(\"2d\");\n  if (!context || typeof context.drawImage !== \"function\") return;\n\n  context.drawImage(sourceCanvas, 0, 0);\n  cache.clock += 1;\n  const key = getPdfRenderedPageCacheKey(rendered);\n  cache.entries.set(key, {\n    ...rendered,\n    canvas,\n    lastUsed: cache.clock,\n    pixels: canvas.width * canvas.height,\n  });\n  evictPdfRenderedPageCache(cache, key);\n}\n\nexport function clearPdfRenderedPageCache(cache: PdfRenderedPageCache) {\n  cache.clock = 0;\n  cache.entries.clear();\n}\n\nfunction evictPdfRenderedPageCache(\n  cache: PdfRenderedPageCache,\n  retainedKey: string,\n) {\n  let totalPixels = getTotalCachedPixels(cache);\n\n  while (\n    cache.entries.size > PDF_RENDERED_PAGE_CACHE_MAX_ENTRIES ||\n    totalPixels > PDF_RENDERED_PAGE_CACHE_MAX_PIXELS\n  ) {\n    const evicted = findPdfRenderedPageCacheEviction(cache, retainedKey);\n    if (!evicted) return;\n\n    totalPixels -= evicted.entry.pixels;\n    cache.entries.delete(evicted.key);\n  }\n}\n\nfunction findPdfRenderedPageCacheEviction(\n  cache: PdfRenderedPageCache,\n  retainedKey: string,\n) {\n  let oldest: { key: string; entry: PdfRenderedPageCacheEntry } | null = null;\n\n  for (const [key, entry] of cache.entries) {\n    if (key === retainedKey) continue;\n    if (!oldest || entry.lastUsed < oldest.entry.lastUsed) {\n      oldest = { key, entry };\n    }\n  }\n\n  return oldest;\n}\n\nfunction getTotalCachedPixels(cache: PdfRenderedPageCache) {\n  let pixels = 0;\n  for (const entry of cache.entries.values()) {\n    pixels += entry.pixels;\n  }\n  return pixels;\n}\n\nfunction doesRenderedPageSatisfyRequest(\n  rendered: PdfRenderedPageSignature,\n  requested: PdfRenderedPageSignature,\n) {\n  return (\n    rendered.documentKey === requested.documentKey &&\n    rendered.pageNumber === requested.pageNumber &&\n    rendered.rotation === requested.rotation &&\n    rendered.scale >= requested.scale &&\n    rendered.viewportWidth >= requested.viewportWidth &&\n    rendered.viewportHeight >= requested.viewportHeight &&\n    hasMatchingPageAspectRatio(rendered, requested) &&\n    rendered.devicePixelRatio >= requested.devicePixelRatio\n  );\n}\n\nfunction doesRenderedPageMatchPreview(\n  rendered: PdfRenderedPageSignature,\n  requested: PdfRenderedPageSignature,\n) {\n  return (\n    rendered.documentKey === requested.documentKey &&\n    rendered.pageNumber === requested.pageNumber &&\n    rendered.rotation === requested.rotation &&\n    rendered.viewportWidth >=\n      requested.viewportWidth * PDF_RENDERED_PAGE_PREVIEW_MIN_SCALE_RATIO &&\n    rendered.viewportHeight >=\n      requested.viewportHeight * PDF_RENDERED_PAGE_PREVIEW_MIN_SCALE_RATIO &&\n    hasMatchingPageAspectRatio(rendered, requested)\n  );\n}\n\nfunction getPdfRenderedPageCacheKey(rendered: PdfRenderedPageSignature) {\n  return [\n    rendered.documentKey,\n    rendered.pageNumber,\n    rendered.scale,\n    rendered.rotation,\n    rendered.devicePixelRatio,\n    rendered.viewportWidth,\n    rendered.viewportHeight,\n  ].join(\":\");\n}\n\nfunction getPdfRenderedPageCacheRecord(\n  resetKey: unknown,\n): PdfRenderedPageCacheRecord {\n  const records = isObjectCacheKey(resetKey)\n    ? objectCacheRecords\n    : primitiveCacheRecords;\n  const cached = records.get(resetKey as never);\n  if (cached) return cached;\n\n  const record: PdfRenderedPageCacheRecord = {\n    cache: createPdfRenderedPageCache(resetKey),\n    retainCount: 0,\n    resetKey,\n  };\n  records.set(resetKey as never, record);\n  return record;\n}\n\nfunction releasePdfRenderedPageCacheRecord(record: PdfRenderedPageCacheRecord) {\n  record.retainCount = Math.max(0, record.retainCount - 1);\n  if (record.retainCount > 0) return;\n\n  clearPdfRenderedPageCache(record.cache);\n  if (isObjectCacheKey(record.resetKey)) {\n    objectCacheRecords.delete(record.resetKey);\n  } else {\n    primitiveCacheRecords.delete(record.resetKey);\n  }\n}\n\nfunction isObjectCacheKey(value: unknown): value is object {\n  return (\n    (typeof value === \"object\" && value !== null) || typeof value === \"function\"\n  );\n}\n\nfunction hasMatchingPageAspectRatio(\n  rendered: PdfRenderedPageSignature,\n  requested: PdfRenderedPageSignature,\n) {\n  const renderedRatio = rendered.viewportWidth / rendered.viewportHeight;\n  const requestedRatio = requested.viewportWidth / requested.viewportHeight;\n  return Math.abs(renderedRatio - requestedRatio) < 0.0001;\n}\n",
      "type": "registry:ui",
      "target": "@ui/pdf-viewer-render-cache.ts"
    },
    {
      "path": "registry/new-york-v4/ui/use-pdf-page-metrics.ts",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { useKeyedMountEffect } from \"@/hooks/use-keyed-mount-effect\";\nimport { getPdfPageResource } from \"@/lib/pdf-document-resource\";\nimport type { PdfDocumentProxy } from \"@/lib/pdf-document-types\";\n\nimport { joinEffectKey } from \"@/lib/effect-key\";\n\nexport const PDF_PAGE_METRIC_CONCURRENCY = 4;\n\nexport type PdfPageMetric = {\n  pageNumber: number;\n  width: number;\n  height: number;\n  rotation: number;\n};\n\nexport type PdfPageMetrics = {\n  metricByPageNumber: ReadonlyMap<number, PdfPageMetric>;\n  requestPageMetrics: (pageNumbers: Iterable<number>) => void;\n  status: \"idle\" | \"loading\";\n};\n\ntype MetricControllerState = {\n  documentKey: unknown;\n  pageCount: number;\n  metricByPageNumber: ReadonlyMap<number, PdfPageMetric>;\n  queuedPageNumbers: readonly number[];\n  loadingPageNumbers: ReadonlySet<number>;\n  error: unknown;\n};\n\ntype MetricAction =\n  | { type: \"reset\"; documentKey: unknown; pageCount: number }\n  | {\n      type: \"enqueue\";\n      documentKey: unknown;\n      pageCount: number;\n      pageNumbers: readonly number[];\n    }\n  | { type: \"start\"; documentKey: unknown; pageNumbers: readonly number[] }\n  | { type: \"resolve\"; documentKey: unknown; metric: PdfPageMetric }\n  | { type: \"reject\"; documentKey: unknown; error: unknown };\n\nexport function usePdfPageMetrics(\n  document: PdfDocumentProxy,\n  resetKey: unknown,\n): PdfPageMetrics {\n  const workerSequenceRef = React.useRef(0);\n  const [state, dispatch] = React.useReducer(\n    metricControllerReducer,\n    undefined,\n    () => createMetricControllerState(resetKey, document.numPages),\n  );\n  const visibleState = Object.is(state.documentKey, resetKey)\n    ? state\n    : createMetricControllerState(resetKey, document.numPages);\n  const status =\n    visibleState.queuedPageNumbers.length > 0 ||\n    visibleState.loadingPageNumbers.size > 0\n      ? \"loading\"\n      : \"idle\";\n\n  const resetEffectKey = joinEffectKey([\"reset\", document, resetKey]);\n  useKeyedMountEffect(resetEffectKey, () => {\n    workerSequenceRef.current += 1;\n    dispatch({\n      type: \"reset\",\n      documentKey: resetKey,\n      pageCount: document.numPages,\n    });\n  });\n\n  const requestPageMetrics = React.useCallback(\n    (pageNumbers: Iterable<number>) => {\n      const requestedPageNumbers = Array.from(pageNumbers)\n        .map((pageNumber) =>\n          normalizePdfPageNumber(pageNumber, document.numPages),\n        )\n        .filter((pageNumber): pageNumber is number => pageNumber != null);\n\n      if (requestedPageNumbers.length === 0) return;\n\n      dispatch({\n        type: \"enqueue\",\n        documentKey: resetKey,\n        pageCount: document.numPages,\n        pageNumbers: requestedPageNumbers,\n      });\n    },\n    [document.numPages, resetKey],\n  );\n\n  const loadEffectKey = joinEffectKey([\n    \"load\",\n    document,\n    resetKey,\n    state.documentKey,\n    state.error,\n    state.loadingPageNumbers,\n    state.queuedPageNumbers,\n  ]);\n  useKeyedMountEffect(loadEffectKey, () => {\n    if (!Object.is(state.documentKey, resetKey)) return;\n    if (state.error) return;\n\n    const availableSlotCount =\n      PDF_PAGE_METRIC_CONCURRENCY - state.loadingPageNumbers.size;\n    if (availableSlotCount <= 0) return;\n\n    const startingPageNumbers = state.queuedPageNumbers.slice(\n      0,\n      availableSlotCount,\n    );\n    if (startingPageNumbers.length === 0) return;\n\n    const sequence = workerSequenceRef.current;\n    dispatch({\n      type: \"start\",\n      documentKey: resetKey,\n      pageNumbers: startingPageNumbers,\n    });\n\n    for (const pageNumber of startingPageNumbers) {\n      void getPdfPageResource(document, pageNumber, {\n        retainRejected: true,\n      })\n        .then((page) => {\n          if (workerSequenceRef.current !== sequence) return;\n\n          const rotation = page.rotate ?? 0;\n          const viewport = page.getViewport({ scale: 1, rotation });\n          dispatch({\n            type: \"resolve\",\n            documentKey: resetKey,\n            metric: {\n              pageNumber,\n              width: viewport.width,\n              height: viewport.height,\n              rotation,\n            },\n          });\n        })\n        .catch((error: unknown) => {\n          if (workerSequenceRef.current !== sequence) return;\n          dispatch({ type: \"reject\", documentKey: resetKey, error });\n        });\n    }\n  });\n\n  if (visibleState.error) throw visibleState.error;\n\n  return {\n    metricByPageNumber: visibleState.metricByPageNumber,\n    requestPageMetrics,\n    status,\n  };\n}\n\nfunction metricControllerReducer(\n  state: MetricControllerState,\n  action: MetricAction,\n): MetricControllerState {\n  switch (action.type) {\n    case \"reset\":\n      return createMetricControllerState(action.documentKey, action.pageCount);\n    case \"enqueue\":\n      return enqueuePageMetrics(state, action);\n    case \"start\":\n      return startPageMetrics(state, action);\n    case \"resolve\":\n      return resolvePageMetric(state, action);\n    case \"reject\":\n      if (!Object.is(state.documentKey, action.documentKey)) return state;\n      return {\n        ...state,\n        queuedPageNumbers: [],\n        loadingPageNumbers: new Set(),\n        error: action.error,\n      };\n  }\n}\n\nfunction createMetricControllerState(\n  documentKey: unknown,\n  pageCount: number,\n): MetricControllerState {\n  return {\n    documentKey,\n    pageCount,\n    metricByPageNumber: new Map(),\n    queuedPageNumbers: [],\n    loadingPageNumbers: new Set(),\n    error: null,\n  };\n}\n\nfunction enqueuePageMetrics(\n  state: MetricControllerState,\n  action: Extract<MetricAction, { type: \"enqueue\" }>,\n): MetricControllerState {\n  const baseState = Object.is(state.documentKey, action.documentKey)\n    ? state\n    : createMetricControllerState(action.documentKey, action.pageCount);\n  const queuedPageNumberSet = new Set(baseState.queuedPageNumbers);\n  const queuedPageNumbers = [...baseState.queuedPageNumbers];\n\n  for (const pageNumber of action.pageNumbers) {\n    if (baseState.metricByPageNumber.has(pageNumber)) continue;\n    if (baseState.loadingPageNumbers.has(pageNumber)) continue;\n    if (queuedPageNumberSet.has(pageNumber)) continue;\n\n    queuedPageNumberSet.add(pageNumber);\n    queuedPageNumbers.push(pageNumber);\n  }\n\n  if (queuedPageNumbers.length === baseState.queuedPageNumbers.length) {\n    return baseState;\n  }\n\n  return {\n    ...baseState,\n    queuedPageNumbers,\n  };\n}\n\nfunction startPageMetrics(\n  state: MetricControllerState,\n  action: Extract<MetricAction, { type: \"start\" }>,\n): MetricControllerState {\n  if (!Object.is(state.documentKey, action.documentKey)) return state;\n\n  const startingPageNumberSet = new Set(action.pageNumbers);\n  const queuedPageNumbers = state.queuedPageNumbers.filter(\n    (pageNumber) => !startingPageNumberSet.has(pageNumber),\n  );\n  const loadingPageNumbers = new Set(state.loadingPageNumbers);\n\n  for (const pageNumber of action.pageNumbers) {\n    if (state.metricByPageNumber.has(pageNumber)) continue;\n    loadingPageNumbers.add(pageNumber);\n  }\n\n  return {\n    ...state,\n    queuedPageNumbers,\n    loadingPageNumbers,\n  };\n}\n\nfunction resolvePageMetric(\n  state: MetricControllerState,\n  action: Extract<MetricAction, { type: \"resolve\" }>,\n): MetricControllerState {\n  if (!Object.is(state.documentKey, action.documentKey)) return state;\n\n  const loadingPageNumbers = new Set(state.loadingPageNumbers);\n  loadingPageNumbers.delete(action.metric.pageNumber);\n\n  const metricByPageNumber = new Map(state.metricByPageNumber);\n  metricByPageNumber.set(action.metric.pageNumber, action.metric);\n\n  return {\n    ...state,\n    metricByPageNumber,\n    loadingPageNumbers,\n  };\n}\n\nfunction normalizePdfPageNumber(pageNumber: number, pageCount: number) {\n  return Number.isInteger(pageNumber) &&\n    pageNumber >= 1 &&\n    pageNumber <= pageCount\n    ? pageNumber\n    : null;\n}\n",
      "type": "registry:ui",
      "target": "@ui/use-pdf-page-metrics.ts"
    },
    {
      "path": "registry/new-york-v4/ui/pdf-viewer-context.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport {\n  createViewerResource,\n  type ViewerResource,\n} from \"@/lib/viewer-resource\";\nimport type { BlobViewerSource, UrlViewerSource } from \"@/lib/viewer-source\";\n\nimport { useOptionalFileViewerResource } from \"./file-viewer-resource-state\";\nimport {\n  PdfResourceContent,\n  type PdfViewerContentProps,\n} from \"./pdf-viewer-content\";\nimport type { PdfViewerHandle } from \"./pdf-viewer-types\";\n\nexport type PdfDocumentSource = UrlViewerSource | BlobViewerSource;\n\nexport type PdfViewerThumbnailsState = {\n  currentPage: number | null;\n  onSelectPage: ((page: number) => void) | undefined;\n  resource: ViewerResource;\n};\n\nexport interface PdfViewerProviderProps {\n  source?: PdfDocumentSource;\n  children: React.ReactNode;\n}\n\ntype PdfViewerContextValue = {\n  currentPage: number | null;\n  isViewerReady: boolean;\n  resource: ViewerResource;\n  setCurrentPage: (page: number | null) => void;\n  setViewerHandle: (handle: PdfViewerHandle | null) => void;\n  viewerHandleRef: React.RefObject<PdfViewerHandle | null>;\n};\n\ntype PdfDocumentPagesState = {\n  resource: ViewerResource;\n  setCurrentPage: (page: number | null) => void;\n  setViewerHandle: (handle: PdfViewerHandle | null) => void;\n};\n\nconst PdfViewerContext = React.createContext<PdfViewerContextValue | null>(\n  null,\n);\n\nconst PDF_THUMBNAIL_SMOOTH_SCROLL_MAX_PAGE_DELTA = 8;\n\nfunction usePdfViewerContext(\n  consumer = \"PdfViewer parts\",\n): PdfViewerContextValue {\n  const context = React.useContext(PdfViewerContext);\n  if (!context) {\n    throw new Error(`${consumer} must be used within PdfViewerProvider.`);\n  }\n  return context;\n}\n\nexport function usePdfViewerThumbnails(): PdfViewerThumbnailsState {\n  const { currentPage, isViewerReady, resource, viewerHandleRef } =\n    usePdfViewerContext(\"usePdfViewerThumbnails\");\n  const onSelectPage = React.useCallback(\n    (page: number) => {\n      const pageDelta = currentPage == null ? 0 : Math.abs(page - currentPage);\n      viewerHandleRef.current?.scrollToPage(page, {\n        behavior:\n          pageDelta > PDF_THUMBNAIL_SMOOTH_SCROLL_MAX_PAGE_DELTA\n            ? \"auto\"\n            : \"smooth\",\n      });\n    },\n    [currentPage, viewerHandleRef],\n  );\n\n  return {\n    currentPage,\n    onSelectPage: isViewerReady ? onSelectPage : undefined,\n    resource,\n  };\n}\n\nfunction usePdfDocumentPagesState(): PdfDocumentPagesState {\n  const { resource, setCurrentPage, setViewerHandle } = usePdfViewerContext();\n  return { resource, setCurrentPage, setViewerHandle };\n}\n\nexport const PdfViewerPages = React.forwardRef<\n  PdfViewerHandle,\n  PdfViewerContentProps\n>(function PdfViewerPages(props, ref) {\n  const { resource, setCurrentPage, setViewerHandle } =\n    usePdfDocumentPagesState();\n  const { onVisiblePageChange } = props;\n  const handleVisiblePageChange = React.useCallback(\n    (page: number) => {\n      setCurrentPage(page);\n      onVisiblePageChange?.(page);\n    },\n    [onVisiblePageChange, setCurrentPage],\n  );\n  const handleRef = React.useCallback(\n    (handle: PdfViewerHandle | null) => {\n      setViewerHandle(handle);\n      if (typeof ref === \"function\") {\n        ref(handle);\n        return;\n      }\n      if (ref) ref.current = handle;\n    },\n    [ref, setViewerHandle],\n  );\n\n  return (\n    <PdfResourceContent\n      {...props}\n      ref={handleRef}\n      resource={resource}\n      controls={false}\n      onVisiblePageChange={handleVisiblePageChange}\n    />\n  );\n});\n\nexport function PdfViewerProvider({\n  source,\n  children,\n}: PdfViewerProviderProps) {\n  const fileViewerResource = useOptionalFileViewerResource();\n  const resource = React.useMemo(() => {\n    if (source) return createViewerResource(source);\n    if (fileViewerResource) return fileViewerResource;\n    throw new Error(\n      \"PdfViewerProvider requires a source or enclosing FileViewer.\",\n    );\n  }, [fileViewerResource, source]);\n  const [currentPage, setCurrentPage] = React.useState<number | null>(null);\n  const viewerHandleRef = React.useRef<PdfViewerHandle | null>(null);\n  const [isViewerReady, setIsViewerReady] = React.useState(false);\n  const setViewerHandle = React.useCallback((handle: PdfViewerHandle | null) => {\n    viewerHandleRef.current = handle;\n    const nextIsViewerReady = handle !== null;\n    setIsViewerReady((currentIsViewerReady) =>\n      currentIsViewerReady === nextIsViewerReady\n        ? currentIsViewerReady\n        : nextIsViewerReady,\n    );\n  }, []);\n  const value = React.useMemo<PdfViewerContextValue>(\n    () => ({\n      currentPage,\n      isViewerReady,\n      resource,\n      setCurrentPage,\n      setViewerHandle,\n      viewerHandleRef,\n    }),\n    [currentPage, isViewerReady, resource, setViewerHandle],\n  );\n\n  return (\n    <PdfViewerContext.Provider value={value}>\n      {children}\n    </PdfViewerContext.Provider>\n  );\n}\n",
      "type": "registry:ui",
      "target": "@ui/pdf-viewer-context.tsx"
    },
    {
      "path": "registry/new-york-v4/ui/viewer.tsx",
      "content": "\"use client\";\n\nexport { ViewerBody } from \"./viewer-body\";\nexport type { ViewerSidebarTriggerProps } from \"./viewer-chrome\";\nexport {\n  ViewerFrame,\n  ViewerHeader,\n  ViewerSidebarTrigger,\n} from \"./viewer-chrome\";\nexport {\n  ViewerRoot,\n  useOptionalViewerSidebar,\n  useViewerSidebar,\n} from \"./viewer-root\";\nexport { ViewerSidebar } from \"./viewer-sidebar\";\nexport { ViewerSurface, ViewerViewport } from \"./viewer-surface\";\nexport type {\n  ViewerBodyProps,\n  ViewerDataAttributes,\n  ViewerFrameProps,\n  ViewerHeaderProps,\n  ViewerRootProps,\n  ViewerSidebarCollapsible,\n  ViewerSidebarStateValue,\n  ViewerSidebarMode,\n  ViewerSidebarProps,\n  ViewerSidebarRequestedMode,\n  ViewerSidebarSide,\n  ViewerSidebarSlotNames,\n  ViewerSidebarState,\n  ViewerStateAttributeNamespace,\n  ViewerStateAttributeSlot,\n  ViewerStateAttributeValues,\n  ViewerSurfaceProps,\n  ViewerViewportProps,\n} from \"./viewer-types\";\n",
      "type": "registry:ui",
      "target": "@ui/viewer.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/pdf-viewer-types.ts",
      "content": "export interface PageOverlayProps {\n  pageNumber: number;\n  /** Rendered page size in CSS pixels (post-scale, post-rotation). */\n  width: number;\n  height: number;\n  scale: number;\n  rotation: number;\n}\n\nexport type PdfPageAreaTarget = {\n  pageNumber: number;\n  top: number;\n  left?: number;\n  width?: number;\n  height?: number;\n};\n\nexport interface PdfViewerHandle {\n  scrollToPage: (pageNumber: number, options?: ScrollToOptions) => void;\n  scrollToPageArea: (\n    target: PdfPageAreaTarget,\n    options?: ScrollToOptions,\n  ) => void;\n  getViewportElement: () => HTMLDivElement | null;\n}\n\nexport type PdfPageSize = {\n  width: number;\n  height: number;\n};\n\nexport type PdfPageRenderStatus = \"rendered\" | \"cancelled\" | \"failed\";\nexport type PdfPageRenderSource = \"cache\" | \"cache-preview\" | \"pdfjs\";\n\nexport type PdfPageRenderTiming = {\n  pageNumber: number;\n  scale: number;\n  rotation: number;\n  devicePixelRatio: number;\n  status: PdfPageRenderStatus;\n  source?: PdfPageRenderSource;\n  durationMs: number;\n};\n\nexport type PdfViewerPerformanceOptions = {\n  renderedPageCache?: boolean;\n  directionAwarePreRender?: boolean;\n  imperativePageLayer?: boolean;\n};\n",
      "type": "registry:ui",
      "target": "@ui/pdf-viewer-types.ts"
    },
    {
      "path": "registry/new-york-v4/ui/pdf-viewer-scale.ts",
      "content": "import * as React from \"react\";\n\nimport { useKeyedLayoutEffect } from \"@/hooks/use-keyed-layout-effect\";\nimport { joinEffectKey } from \"@/lib/effect-key\";\n\nexport const MIN_PDF_SCALE = 0.1;\nexport const MAX_PDF_SCALE = 5;\nexport const PDF_ZOOM_STEP = 1.2;\nexport const PDF_PAGE_HORIZONTAL_PADDING = 32;\nexport const PDF_PAGE_SETTLED_MAX_DEVICE_PIXEL_RATIO = 2;\nexport const PDF_PAGE_SCROLLING_MAX_DEVICE_PIXEL_RATIO =\n  PDF_PAGE_SETTLED_MAX_DEVICE_PIXEL_RATIO;\n\nexport type PdfPageDprMode = \"scrolling\" | \"settled\";\n\nexport function clamp(value: number, min: number, max: number) {\n  return Math.min(max, Math.max(min, value));\n}\n\nexport function clampPdfScale(value: number) {\n  // NaN has no ordering, so default it to 1; ±Infinity clamps to the bounds\n  // like any large/small finite value (e.g. 1e9 → MAX, -1e9 → MIN).\n  if (Number.isNaN(value)) return 1;\n  return clamp(value, MIN_PDF_SCALE, MAX_PDF_SCALE);\n}\n\nexport function getPdfFitWidthScale(\n  containerWidth: number | null,\n  pageWidth: number,\n  inlinePadding = PDF_PAGE_HORIZONTAL_PADDING,\n) {\n  if (!containerWidth || !Number.isFinite(containerWidth) || pageWidth <= 0) {\n    return 1;\n  }\n  return clampPdfScale((containerWidth - inlinePadding) / pageWidth);\n}\n\nexport function getPdfPageDevicePixelRatio({\n  devicePixelRatio,\n  mode: _mode,\n}: {\n  devicePixelRatio: number;\n  mode: PdfPageDprMode;\n}) {\n  const normalizedDevicePixelRatio =\n    Number.isFinite(devicePixelRatio) && devicePixelRatio > 0\n      ? devicePixelRatio\n      : 1;\n  return Math.min(\n    normalizedDevicePixelRatio,\n    PDF_PAGE_SETTLED_MAX_DEVICE_PIXEL_RATIO,\n  );\n}\n\nexport function useMeasuredElementWidth({\n  enabled = true,\n}: {\n  enabled?: boolean;\n} = {}) {\n  const [element, setElement] = React.useState<HTMLDivElement | null>(null);\n  const [width, setWidth] = React.useState<number | null>(null);\n\n  const ref = React.useCallback((nextElement: HTMLDivElement | null) => {\n    setElement(nextElement);\n  }, []);\n\n  useKeyedLayoutEffect(enabled ? null : \"reset\", () => {\n    setWidth(null);\n  });\n\n  useKeyedLayoutEffect(\n    enabled && element ? joinEffectKey([element]) : null,\n    () => {\n      if (!element) return;\n\n      let frame = 0;\n      const measure = () => {\n        if (frame) return;\n        frame = requestAnimationFrame(() => {\n          frame = 0;\n          setWidth(element.clientWidth);\n        });\n      };\n\n      measure();\n      if (typeof ResizeObserver === \"undefined\") {\n        return () => {\n          if (frame) cancelAnimationFrame(frame);\n        };\n      }\n\n      const observer = new ResizeObserver(measure);\n\n      observer.observe(element);\n      return () => {\n        if (frame) cancelAnimationFrame(frame);\n        observer.disconnect();\n      };\n    },\n  );\n\n  return { ref, width };\n}\n\nexport function usePdfScale({\n  controlledScale,\n  defaultScale,\n  onScaleChange,\n  containerWidth,\n  pageWidth,\n  fitWidthInlinePadding,\n  resetKey,\n}: {\n  controlledScale?: number;\n  defaultScale?: number;\n  onScaleChange?: (scale: number | null) => void;\n  containerWidth: number | null;\n  fitWidthInlinePadding?: number;\n  pageWidth: number;\n  resetKey?: unknown;\n}) {\n  const initialRequestedScale =\n    defaultScale == null ? null : clampPdfScale(defaultScale);\n  const [uncontrolledScaleState, setUncontrolledScaleState] = React.useState<{\n    resetKey: unknown;\n    requestedScale: number | null;\n  }>(() => ({\n    resetKey,\n    requestedScale: initialRequestedScale,\n  }));\n  const uncontrolledRequestedScale = Object.is(\n    uncontrolledScaleState.resetKey,\n    resetKey,\n  )\n    ? uncontrolledScaleState.requestedScale\n    : initialRequestedScale;\n  const isControlledScale = controlledScale !== undefined;\n  const isFitWidth = !isControlledScale && uncontrolledRequestedScale == null;\n  const fitWidthScale = getPdfFitWidthScale(\n    containerWidth,\n    pageWidth,\n    fitWidthInlinePadding,\n  );\n  const resolvedScale = isControlledScale\n    ? clampPdfScale(controlledScale)\n    : (uncontrolledRequestedScale ?? fitWidthScale);\n\n  const setRequestedScale = React.useCallback(\n    (nextScale: number | null) => {\n      const normalizedScale =\n        nextScale == null ? null : clampPdfScale(nextScale);\n      if (isControlledScale) {\n        onScaleChange?.(normalizedScale);\n        return;\n      }\n      setUncontrolledScaleState((previousState) =>\n        Object.is(previousState.resetKey, resetKey) &&\n        previousState.requestedScale === normalizedScale\n          ? previousState\n          : { resetKey, requestedScale: normalizedScale },\n      );\n    },\n    [isControlledScale, onScaleChange, resetKey],\n  );\n\n  const zoomIn = React.useCallback(\n    () => setRequestedScale(resolvedScale * PDF_ZOOM_STEP),\n    [resolvedScale, setRequestedScale],\n  );\n  const zoomOut = React.useCallback(\n    () => setRequestedScale(resolvedScale / PDF_ZOOM_STEP),\n    [resolvedScale, setRequestedScale],\n  );\n  const fitWidth = React.useCallback(\n    () => setRequestedScale(null),\n    [setRequestedScale],\n  );\n\n  return {\n    resolvedScale,\n    isFitWidth,\n    zoomIn,\n    zoomOut,\n    fitWidth,\n  };\n}\n",
      "type": "registry:ui",
      "target": "@ui/pdf-viewer-scale.ts"
    },
    {
      "path": "registry/new-york-v4/ui/pdf-viewer-scroll.ts",
      "content": "import * as React from \"react\";\nimport { useMountEffect } from \"@/hooks/use-mount-effect\";\n\nimport {\n  findPdfPageByOffset,\n  getPdfLogicalScrollTop,\n  getPdfPageLayout,\n  getPdfPhysicalScrollHeight,\n  resolvePdfPhysicalScrollPosition,\n  type PdfPageLayoutModel,\n} from \"./pdf-viewer-layout\";\nimport { clamp } from \"./pdf-viewer-scale\";\nimport { createPdfZoomMotionController } from \"./pdf-viewer-zoom-motion\";\nimport type { PdfPageAreaTarget } from \"./pdf-viewer-types\";\nimport type {\n  ViewerDocumentScrollMapper,\n  ViewerDocumentScrollMetrics,\n  ViewerDocumentScrollTargetResolver,\n  ViewerDocumentLayoutModel,\n  ViewerDocumentTransition,\n} from \"./viewer-types\";\nimport { useViewerDocumentScroll } from \"./viewer-document-scroll\";\nimport { useKeyedMountEffect } from \"@/hooks/use-keyed-mount-effect\";\nimport { useKeyedLayoutEffect } from \"@/hooks/use-keyed-layout-effect\";\nimport { joinEffectKey } from \"@/lib/effect-key\";\n\nconst PDF_SCROLL_TARGET_HEADROOM = 48;\nconst PDF_SCROLL_TARGET_INLINE_HEADROOM = 32;\nexport const PDF_READING_MARKER_RATIO = 0.2;\nconst PDF_SCROLL_IDLE_MS = 120;\n\ntype PdfReadingAnchor =\n  | {\n      kind: \"top\";\n    }\n  | {\n      kind: \"page\";\n      pageNumber: number;\n      yPercent: number;\n    };\n\ntype PdfResolvedPageAreaTarget = {\n  top: number;\n  left?: number;\n};\n\ntype PdfScrollMetrics = {\n  physicalScrollHeight: number;\n  physicalScrollTop: number;\n  scrollPageOffset: number;\n  scrollTop: number;\n  viewportHeight: number;\n};\n\ntype PdfDocumentLayoutModel = ViewerDocumentLayoutModel<PdfReadingAnchor>;\n\nconst PDF_DOCUMENT_SCROLL_MAPPER: ViewerDocumentScrollMapper = {\n  getLogicalScrollTop: ({\n    blockSize,\n    physicalScrollTop,\n    scrollPageOffset,\n    viewportBlockSize,\n  }) =>\n    getPdfLogicalScrollTop({\n      physicalScrollTop,\n      scrollPageOffset,\n      totalHeight: blockSize,\n      viewportHeight: viewportBlockSize,\n    }),\n  getPhysicalScrollSize: ({ blockSize, viewportBlockSize }) =>\n    getPdfPhysicalScrollHeight({\n      totalHeight: blockSize,\n      viewportHeight: viewportBlockSize,\n    }),\n  resolvePhysicalScrollPosition: ({\n    blockSize,\n    logicalScrollTop,\n    scrollPageOffset,\n    viewportBlockSize,\n  }) =>\n    resolvePdfPhysicalScrollPosition({\n      logicalScrollTop,\n      scrollPageOffset,\n      totalHeight: blockSize,\n      viewportHeight: viewportBlockSize,\n    }),\n};\n\nexport function usePdfScrollActivity() {\n  const [isScrolling, setIsScrolling] = React.useState(false);\n  const [scrollDirection, setScrollDirection] = React.useState(1);\n  const idleTimeoutRef = React.useRef<ReturnType<typeof setTimeout> | null>(\n    null,\n  );\n  const scrollTopRef = React.useRef(0);\n\n  const handleScrollActivity = React.useCallback((viewport?: HTMLElement) => {\n    const scrollTop = viewport?.scrollTop ?? scrollTopRef.current;\n    const previousScrollTop = scrollTopRef.current;\n    if (scrollTop > previousScrollTop) {\n      setScrollDirection(1);\n    } else if (scrollTop < previousScrollTop) {\n      setScrollDirection(-1);\n    }\n    scrollTopRef.current = scrollTop;\n\n    if (idleTimeoutRef.current) {\n      clearTimeout(idleTimeoutRef.current);\n    }\n\n    setIsScrolling(true);\n    idleTimeoutRef.current = setTimeout(() => {\n      idleTimeoutRef.current = null;\n      setIsScrolling(false);\n    }, PDF_SCROLL_IDLE_MS);\n  }, []);\n\n  useMountEffect(() => () => {\n    if (idleTimeoutRef.current) clearTimeout(idleTimeoutRef.current);\n  });\n\n  return { isScrolling, scrollDirection, handleScrollActivity };\n}\n\n// The reading marker's block offset in document coordinates. Layout and\n// scroll are committed to their settled values at slide start (commit-then-\n// relax), so no settle prediction is needed — the marker is always read\n// against live geometry.\nexport function getPdfReadingMarkerBlockOffset({\n  scrollTop,\n  viewportHeight,\n}: {\n  scrollTop: number;\n  viewportHeight: number;\n}) {\n  const safeScrollTop = Number.isFinite(scrollTop) ? Math.max(0, scrollTop) : 0;\n  const safeViewportHeight = Number.isFinite(viewportHeight)\n    ? Math.max(0, viewportHeight)\n    : 0;\n  return safeScrollTop + safeViewportHeight * PDF_READING_MARKER_RATIO;\n}\n\nexport function usePdfScroll({\n  isLayoutTransitioning = false,\n  pageCount,\n  layout,\n  transition,\n  resetKey,\n  onVisiblePageChange,\n  onScrollProgressChange,\n}: {\n  isLayoutTransitioning?: boolean;\n  pageCount: number;\n  layout: PdfPageLayoutModel;\n  transition?: ViewerDocumentTransition;\n  resetKey?: unknown;\n  onVisiblePageChange?: (page: number) => void;\n  onScrollProgressChange?: (progress: number) => void;\n}) {\n  const lastReportedPageRef = React.useRef(0);\n  const scrollFrameRef = React.useRef(0);\n  const didMountResetEffectRef = React.useRef(false);\n  const [currentPageState, setCurrentPageState] = React.useState<{\n    resetKey: unknown;\n    page: number;\n  }>(() => ({ resetKey, page: 1 }));\n  const currentPage = Object.is(currentPageState.resetKey, resetKey)\n    ? currentPageState.page\n    : 1;\n  const documentLayout = React.useMemo(\n    () =>\n      createPdfDocumentLayoutModel({\n        isTransitioning: isLayoutTransitioning,\n        layout,\n        transition,\n      }),\n    [isLayoutTransitioning, layout, transition],\n  );\n  const resolveScrollTarget = React.useCallback<\n    ViewerDocumentScrollTargetResolver<PdfReadingAnchor, PdfPageAreaTarget>\n  >(\n    ({ scrollTop, target, viewportElement }) =>\n      getPdfPageAreaScrollTarget(\n        viewportElement,\n        layout,\n        pageCount,\n        target,\n        scrollTop,\n      ),\n    [layout, pageCount],\n  );\n  const zoomMotion = React.useMemo(\n    () => createPdfZoomMotionController(layout),\n    [layout],\n  );\n  const documentScroll = useViewerDocumentScroll({\n    copyScrollTarget: copyPdfPageAreaTarget,\n    layout: documentLayout,\n    resetKey,\n    resolveScrollTarget,\n    scrollMapper: PDF_DOCUMENT_SCROLL_MAPPER,\n    zoomMotion,\n  });\n  const getScrollMetrics = React.useCallback(() => {\n    const metrics = documentScroll.getScrollMetrics();\n    return toPdfScrollMetrics(metrics);\n  }, [documentScroll]);\n  const viewportElement = documentScroll.viewportElement;\n\n  const resetCurrentPage = React.useCallback(() => {\n    lastReportedPageRef.current = 0;\n    setCurrentPageState((previousState) =>\n      Object.is(previousState.resetKey, resetKey) && previousState.page === 1\n        ? previousState\n        : { resetKey, page: 1 },\n    );\n  }, [resetKey]);\n\n  const measureScroll = React.useCallback(() => {\n    scrollFrameRef.current = 0;\n    const viewportElement = documentScroll.getViewportElement();\n    if (!viewportElement) return;\n\n    const metrics = documentScroll.syncScrollPosition();\n    if (!metrics) return;\n    const isRebased = metrics.physicalScrollSize < documentLayout.blockSize;\n    const scrollable = isRebased\n      ? documentLayout.blockSize - metrics.viewportBlockSize\n      : viewportElement.scrollHeight - metrics.viewportBlockSize;\n    const progress =\n      scrollable > 0\n        ? clamp(\n            (isRebased ? metrics.scrollTop : viewportElement.scrollTop) /\n              scrollable,\n            0,\n            1,\n          )\n        : 0;\n    onScrollProgressChange?.(progress);\n\n    const markerOffset = getPdfReadingMarkerBlockOffset({\n      scrollTop: metrics.scrollTop,\n      viewportHeight: metrics.viewportBlockSize,\n    });\n    const visiblePage = findPdfPageByOffset(layout, markerOffset);\n    if (\n      visiblePage >= 1 &&\n      visiblePage <= pageCount &&\n      visiblePage !== lastReportedPageRef.current\n    ) {\n      lastReportedPageRef.current = visiblePage;\n      setCurrentPageState((previousState) =>\n        Object.is(previousState.resetKey, resetKey) &&\n        previousState.page === visiblePage\n          ? previousState\n          : { resetKey, page: visiblePage },\n      );\n      onVisiblePageChange?.(visiblePage);\n    }\n  }, [\n    documentLayout,\n    documentScroll,\n    layout,\n    onScrollProgressChange,\n    onVisiblePageChange,\n    pageCount,\n    resetKey,\n  ]);\n  const measureScrollRef = React.useRef(measureScroll);\n  useKeyedLayoutEffect(joinEffectKey([measureScroll]), () => {\n    measureScrollRef.current = measureScroll;\n  });\n\n  const handleScroll = React.useCallback(() => {\n    const result = documentScroll.handleScroll();\n    if (result.source === \"internal\") return result;\n\n    if (scrollFrameRef.current) return result;\n    scrollFrameRef.current = requestAnimationFrame(() =>\n      measureScrollRef.current(),\n    );\n    return result;\n  }, [documentScroll]);\n\n  useKeyedMountEffect(joinEffectKey([resetCurrentPage]), () => {\n    if (!didMountResetEffectRef.current) {\n      didMountResetEffectRef.current = true;\n      return;\n    }\n    resetCurrentPage();\n  });\n\n  const scrollToPageArea = React.useCallback(\n    (target: PdfPageAreaTarget, options?: ScrollToOptions) => {\n      documentScroll.scrollToTarget(target, options);\n    },\n    [documentScroll],\n  );\n  const scrollToPage = React.useCallback(\n    (pageNumber: number, options?: ScrollToOptions) => {\n      scrollToPageArea({ pageNumber, top: 0 }, options);\n    },\n    [scrollToPageArea],\n  );\n  useKeyedMountEffect(joinEffectKey([measureScroll]), () => {\n    if (scrollFrameRef.current) {\n      cancelAnimationFrame(scrollFrameRef.current);\n      scrollFrameRef.current = 0;\n    }\n  });\n\n  useMountEffect(() => () => {\n    if (scrollFrameRef.current) cancelAnimationFrame(scrollFrameRef.current);\n  });\n\n  return {\n    captureZoomIntent: documentScroll.captureZoomIntent,\n    currentPage,\n    viewportElement,\n    setViewportElement: documentScroll.setViewportElement,\n    measureScroll,\n    handleScroll,\n    getScrollMetrics,\n    scrollToPage,\n    scrollToPageArea,\n    getViewportElement: documentScroll.getViewportElement,\n  };\n}\n\nfunction getPdfPageAreaScrollTarget(\n  viewportElement: HTMLDivElement,\n  layout: PdfPageLayoutModel,\n  pageCount: number,\n  target: PdfPageAreaTarget,\n  scrollTop: number,\n): PdfResolvedPageAreaTarget | null {\n  const pageNumber = target.pageNumber;\n  if (pageNumber < 1 || pageNumber > pageCount) return null;\n\n  const pageLayout = getPdfPageLayout(layout, pageNumber);\n  if (!pageLayout) return null;\n\n  const requestedTop = Number.isNaN(target.top) ? 0 : target.top;\n  const targetTopPercent = clamp(requestedTop, 0, 100);\n  const targetHeightPercent = normalizeOptionalPercent(target.height);\n  const areaTop =\n    pageLayout.offsetTop + (targetTopPercent / 100) * pageLayout.height;\n  const areaBottom =\n    areaTop + ((targetHeightPercent ?? 0) / 100) * pageLayout.height;\n  const visibleTop = scrollTop + PDF_SCROLL_TARGET_HEADROOM;\n  const visibleBottom =\n    scrollTop + viewportElement.clientHeight - PDF_SCROLL_TARGET_HEADROOM;\n  let targetTop = areaTop - PDF_SCROLL_TARGET_HEADROOM;\n\n  if (targetHeightPercent != null && areaTop >= visibleTop) {\n    targetTop =\n      areaBottom > visibleBottom\n        ? areaBottom - viewportElement.clientHeight + PDF_SCROLL_TARGET_HEADROOM\n        : scrollTop;\n  }\n\n  const targetLeft = getPdfPageAreaScrollLeft(viewportElement, layout, {\n    pageLayout,\n    left: target.left,\n    width: target.width,\n  });\n\n  return {\n    top: Math.max(0, targetTop),\n    ...(targetLeft == null ? null : { left: targetLeft }),\n  };\n}\n\nfunction copyPdfPageAreaTarget(target: PdfPageAreaTarget): PdfPageAreaTarget {\n  return {\n    pageNumber: target.pageNumber,\n    top: target.top,\n    left: target.left,\n    width: target.width,\n    height: target.height,\n  };\n}\n\nfunction getPdfPageAreaScrollLeft(\n  viewportElement: HTMLDivElement,\n  layout: PdfPageLayoutModel,\n  target: {\n    pageLayout: NonNullable<ReturnType<typeof getPdfPageLayout>>;\n    left?: number;\n    width?: number;\n  },\n) {\n  const targetLeftPercent = normalizeOptionalPercent(target.left);\n  const targetWidthPercent = normalizeOptionalPercent(target.width);\n  if (targetLeftPercent == null || targetWidthPercent == null) return undefined;\n\n  const documentInlineOffset = Math.max(\n    0,\n    (viewportElement.clientWidth - layout.maxPageWidth) / 2,\n  );\n  const pageInlineOffset =\n    documentInlineOffset + (layout.maxPageWidth - target.pageLayout.width) / 2;\n  const areaLeft =\n    pageInlineOffset + (targetLeftPercent / 100) * target.pageLayout.width;\n  const areaRight =\n    areaLeft + (targetWidthPercent / 100) * target.pageLayout.width;\n  const visibleLeft =\n    viewportElement.scrollLeft + PDF_SCROLL_TARGET_INLINE_HEADROOM;\n  const visibleRight =\n    viewportElement.scrollLeft +\n    viewportElement.clientWidth -\n    PDF_SCROLL_TARGET_INLINE_HEADROOM;\n\n  if (areaLeft < visibleLeft) {\n    return Math.max(0, areaLeft - PDF_SCROLL_TARGET_INLINE_HEADROOM);\n  }\n  if (areaRight > visibleRight) {\n    return Math.max(\n      0,\n      areaRight -\n        viewportElement.clientWidth +\n        PDF_SCROLL_TARGET_INLINE_HEADROOM,\n    );\n  }\n  return viewportElement.scrollLeft;\n}\n\nfunction normalizeOptionalPercent(value: number | undefined) {\n  if (value == null || !Number.isFinite(value)) return undefined;\n  return clamp(value, 0, 100);\n}\n\nfunction createPdfDocumentLayoutModel({\n  isTransitioning,\n  layout,\n  transition,\n}: {\n  isTransitioning: boolean;\n  layout: PdfPageLayoutModel;\n  transition?: ViewerDocumentTransition;\n}): PdfDocumentLayoutModel {\n  const documentTransition: ViewerDocumentTransition =\n    transition ??\n    (isTransitioning\n      ? {\n          layoutPolicy: \"live\",\n          scrollPolicy: \"preserve\",\n          source: \"document-layout\",\n          transitionId: null,\n          visualPolicy: \"document-flip\",\n        }\n      : {\n          layoutPolicy: \"live\",\n          scrollPolicy: \"preserve\",\n          source: \"none\",\n          transitionId: null,\n          visualPolicy: \"none\",\n        });\n\n  return {\n    blockSize: layout.totalHeight,\n    captureReadingAnchor: (input) => capturePdfReadingAnchor(layout, input),\n    getReadingAnchorScrollTop: (target) =>\n      getPdfReadingAnchorScrollTop(layout, target),\n    inlineSize: layout.maxPageWidth,\n    isTransitioning,\n    transition: documentTransition,\n  };\n}\n\nfunction capturePdfReadingAnchor(\n  layout: PdfPageLayoutModel,\n  {\n    scrollTop,\n    viewportBlockSize,\n  }: {\n    scrollTop: number;\n    viewportBlockSize: number;\n  },\n): PdfReadingAnchor | null {\n  if (layout.pageCount === 0) return null;\n  if (scrollTop <= 0) return { kind: \"top\" };\n\n  const anchorOffset = scrollTop + getPdfReadingAnchorOffset(viewportBlockSize);\n  const pageNumber = findPdfPageByOffset(layout, anchorOffset);\n  const pageLayout = getPdfPageLayout(layout, pageNumber);\n  if (!pageLayout || pageLayout.height <= 0) return null;\n\n  // yPercent is deliberately unclamped: the layout is a single linear\n  // function of scale (gaps and padding included), so a marker sitting in the\n  // top padding (< 0) or an inter-page gap (> 1) restores exactly by the same\n  // page-relative fraction. Clamping — or pinning a nearby page edge in\n  // viewport pixels — breaks the reading marker across a re-fit, because the\n  // marker offset is viewport-relative and does not scale with the document.\n  return {\n    kind: \"page\",\n    pageNumber,\n    yPercent: (anchorOffset - pageLayout.offsetTop) / pageLayout.height,\n  };\n}\n\nfunction getPdfReadingAnchorScrollTop(\n  layout: PdfPageLayoutModel,\n  {\n    anchor,\n    viewportBlockSize,\n  }: {\n    anchor: PdfReadingAnchor;\n    viewportBlockSize: number;\n  },\n) {\n  if (anchor.kind === \"top\") {\n    return 0;\n  }\n\n  const pageLayout = getPdfPageLayout(layout, anchor.pageNumber);\n  if (!pageLayout) return null;\n\n  const maxScrollTop = Math.max(0, layout.totalHeight - viewportBlockSize);\n  const targetTop =\n    pageLayout.offsetTop +\n    pageLayout.height * anchor.yPercent -\n    getPdfReadingAnchorOffset(viewportBlockSize);\n  return clamp(targetTop, 0, maxScrollTop);\n}\n\nfunction getPdfReadingAnchorOffset(viewportBlockSize: number) {\n  return Math.max(0, viewportBlockSize * PDF_READING_MARKER_RATIO);\n}\n\nfunction toPdfScrollMetrics(\n  metrics: ViewerDocumentScrollMetrics,\n): PdfScrollMetrics {\n  return {\n    physicalScrollHeight: metrics.physicalScrollSize,\n    physicalScrollTop: metrics.physicalScrollTop,\n    scrollPageOffset: metrics.scrollPageOffset,\n    scrollTop: metrics.scrollTop,\n    viewportHeight: metrics.viewportBlockSize,\n  };\n}\n",
      "type": "registry:ui",
      "target": "@ui/pdf-viewer-scroll.ts"
    },
    {
      "path": "registry/new-york-v4/ui/pdf-viewer-zoom-motion.ts",
      "content": "import {\n  findPdfPageByOffset,\n  getPdfPageLayout,\n  type PdfPageLayoutModel,\n} from \"./pdf-viewer-layout\";\nimport { clamp } from \"./pdf-viewer-scale\";\nimport type { PdfPageRenderTiming } from \"./pdf-viewer-types\";\nimport type { ViewerDocumentZoomMotionController } from \"./viewer-types\";\n\n// A toolbar zoom step re-anchors the viewport CENTER on both axes (Apple\n// Preview semantics): the content point under the viewport center before the\n// step is back under the viewport center after it, and a short relax scales\n// the painted surface about that fixed point. The reading-marker restore\n// (20% from the top, block axis only) stays the semantics for every other\n// geometry change — re-fits, rotation, container resizes — where the intent\n// is \"keep my reading position\", not \"zoom the camera\".\nconst PDF_ZOOM_CENTER_MARKER_RATIO = 0.5;\nconst PDF_ZOOM_MOTION_DURATION_MS = 200;\nconst PDF_ZOOM_MOTION_MIN_TRANSLATE_PX = 0.5;\nconst PDF_ZOOM_MOTION_MIN_SCALE_DELTA = 0.001;\n// A rebased (paged) physical scroll detaches the stage box from the content\n// scale, so the whole-surface FLIP would warp. Its signature is precise: the\n// rebased axis is PINNED (the container keeps its size while the content\n// rescales), so one axis barely moves while the other moves a lot. Testing for\n// that beats a plain ratio tolerance — an honest page stack carries small\n// constant terms (rounded gaps, fixed outer padding) that put the block axis a\n// couple of percent off the inline one. On a big jump a tight ratio tolerance\n// then refuses the relax and the whole scale change lands in one frame —\n// measured on the image viewer as an un-animated 620px snap on a multi-frame\n// TIFF's fit-width, and this stack has the same shape of layout. The\n// wide ratio net below still catches anything wilder, and the FLIP writes\n// per-axis scales, so a slightly non-affine layout renders exactly.\nconst PDF_ZOOM_MOTION_AXIS_FROZEN_DELTA = 0.02;\nconst PDF_ZOOM_MOTION_AXIS_MOVED_DELTA = 0.05;\nconst PDF_ZOOM_MOTION_AXIS_MISMATCH_RATIO = 0.25;\n// The scroll write quantizes to device pixels, so the commit can land the\n// anchor a sub-pixel off the pure center scale. Smearing that residual over\n// the relax pans the anchor for 200ms — the eye tracks it as a wander.\n// Folding it into the (invisible) opening frame instead keeps the anchor\n// mathematically stationary for the whole flight; a residual beyond this\n// epsilon is a REAL clamped-edge pan and must stay animated.\nconst PDF_ZOOM_MOTION_ANCHOR_SNAP_EPSILON_PX = 1.25;\n// Stall net: a hidden tab freezes rAF, and an abandoned counter-transform\n// must never outlive its flight. Generous on purpose — it only fires when\n// the clock stopped ticking.\nconst PDF_ZOOM_MOTION_STALL_TIMEOUT_MS = PDF_ZOOM_MOTION_DURATION_MS * 2 + 200;\n\n// The exact wait-out of the relax before the visual clip re-tightens and\n// rasterization resumes; clip release and raster holds are React-rendered\n// state, so they outlive the inline writes.\nexport const PDF_ZOOM_MOTION_TOTAL_MS = PDF_ZOOM_MOTION_DURATION_MS + 100;\n\nconst PDF_ZOOM_VISUAL_LAYER_SELECTOR = '[data-slot=\"pdf-viewer-visual-clip\"]';\n\nconst PDF_ZOOM_FLIGHT_RECORD_LIMIT = 12;\nconst PDF_ZOOM_FLIGHT_TICK_LIMIT = 120;\n\nexport type PdfZoomTransaction = {\n  /** Gesture timestamp — the flight's start-latency reference. */\n  capturedAt: number;\n  pageNumber: number;\n  /**\n   * Deliberately unclamped, like the reading anchor: the layout is linear in\n   * scale, so a center marker resting in a gap or the edge padding restores\n   * by the same page-relative fraction. Fallback for the rebased (paged)\n   * physical scroll, where the painted layer stops spanning the document.\n   */\n  yPercent: number;\n  /**\n   * The viewport-center content point as fractions of the PAINTED visual\n   * layer — the primary anchor on both axes. Painted (not committed) on\n   * purpose: during a rapid retarget the user is centered on what is on\n   * screen mid-flight, and the model-based fallback would re-anchor on the\n   * committed geometry instead, bending the zoom's trajectory step to step.\n   */\n  paintedAnchor: { x: number; y: number } | null;\n  /** Painted visual-layer rect at click time — the relax's \"first\" frame. */\n  previousVisualRect: DOMRectReadOnly | null;\n};\n\nexport type PdfZoomMotionTick = {\n  elapsedMs: number;\n  frameGapMs: number;\n  progress: number;\n  /** Painted (transform-inclusive) visual-layer rect at this tick. */\n  rectLeft: number;\n  rectTop: number;\n  rectWidth: number;\n  rectHeight: number;\n  scrollDriftPx: number;\n};\n\nexport type PdfZoomMotionSkipReason =\n  | \"axis-scale-mismatch\"\n  | \"bypass:resolve-failed\"\n  | \"bypass:shell-transition\"\n  | \"bypass:stale-intent\"\n  | \"no-previous-rect\"\n  | \"no-raf\"\n  | \"no-visible-delta\"\n  | \"no-visual-layer\"\n  | \"reduced-motion\"\n  | \"zero-rect\";\n\nexport type PdfZoomMotionInterruption = \"cancelled\" | \"none\" | \"stalled\";\n\n// Always-on flight recorder, mirroring the shell kernel's: every zoom relax\n// (and every refusal to relax) leaves a bounded trace so a jagged report is\n// diagnosable after the fact without re-instrumenting.\nexport type PdfZoomMotionFlightRecord = {\n  /**\n   * Measured stationarity of the anchored content point (the viewport\n   * center), per axis: how far the point the zoom is anchored on WANDERS\n   * during the flight. A pure center zoom keeps this at 0; any residual is\n   * a pan smeared over the relax — the \"illegal move\".\n   */\n  anchorDriftMax: { x: number; y: number };\n  durationMs: number;\n  id: number;\n  interruption: PdfZoomMotionInterruption;\n  /** Long tasks observed while the flight was live (needs PerformanceObserver). */\n  longTaskCount: number;\n  longTaskMs: number;\n  maxTickGapMs: number;\n  /** Page raster work that landed mid-flight — the main jank source; 0 when healthy. */\n  pageRenderCount: number;\n  pageRenderMainThreadMs: number;\n  /**\n   * Max deviation of the painted surface corner from the straight chord\n   * between its start and end positions. A pure zoom (translate+scale on one\n   * shared progress) moves every point on a straight line; curvature here\n   * means an external writer or an inconsistent per-axis motion law.\n   */\n  pathDeviationMaxPx: number;\n  /** Commit's deviation from a pure center scale (quantization/clamping). */\n  residualPx: number;\n  /** True when the residual was folded into the opening frame, not animated. */\n  residualSnapped: boolean;\n  scale: { x: number; y: number };\n  /** Max |scroll − scroll@start| seen during the flight; scroll must be static. */\n  scrollDriftMaxPx: number;\n  /** Styles verified cleared at finish. */\n  settledClean: boolean;\n  skipReason: PdfZoomMotionSkipReason | null;\n  /** Gesture capture → first painted-frame tick. */\n  startLatencyMs: number | null;\n  /**\n   * First tick's painted rect vs the click-time captured rect: how far the\n   * flight's opening frame is from the screen the user was just looking at.\n   */\n  startSnapPx: number | null;\n  startedAt: number;\n  status: \"played\" | \"skipped\";\n  tickCount: number;\n  ticks: PdfZoomMotionTick[];\n  translate: { x: number; y: number };\n};\n\nconst zoomFlightRecords: PdfZoomMotionFlightRecord[] = [];\nlet zoomFlightSequence = 0;\nlet liveZoomFlightRecord: PdfZoomMotionFlightRecord | null = null;\n\nexport function getPdfZoomMotionFlightRecords(): readonly PdfZoomMotionFlightRecord[] {\n  return zoomFlightRecords.slice();\n}\n\nexport function clearPdfZoomMotionFlightRecords() {\n  zoomFlightRecords.length = 0;\n  liveZoomFlightRecord = null;\n}\n\n/**\n * Attributes page raster work to a live zoom flight. Wired from the runtime's\n * render-timing tap; with the flight-time raster holds in place this counter\n * staying at 0 is the telemetry proof that no pdf.js work contends with the\n * relax.\n */\nexport function notePdfZoomMotionPageRender(timing: PdfPageRenderTiming) {\n  const record = liveZoomFlightRecord;\n  if (!record) return;\n  record.pageRenderCount += 1;\n  record.pageRenderMainThreadMs += Math.max(0, timing.durationMs);\n}\n\nfunction pushZoomFlightRecord(record: PdfZoomMotionFlightRecord) {\n  zoomFlightRecords.push(record);\n  if (zoomFlightRecords.length > PDF_ZOOM_FLIGHT_RECORD_LIMIT) {\n    zoomFlightRecords.splice(\n      0,\n      zoomFlightRecords.length - PDF_ZOOM_FLIGHT_RECORD_LIMIT,\n    );\n  }\n}\n\nfunction createZoomFlightRecord({\n  scaleX,\n  scaleY,\n  skipReason,\n  translateX,\n  translateY,\n}: {\n  scaleX: number;\n  scaleY: number;\n  skipReason: PdfZoomMotionSkipReason | null;\n  translateX: number;\n  translateY: number;\n}): PdfZoomMotionFlightRecord {\n  zoomFlightSequence += 1;\n  return {\n    anchorDriftMax: { x: 0, y: 0 },\n    durationMs: PDF_ZOOM_MOTION_DURATION_MS,\n    id: zoomFlightSequence,\n    interruption: \"none\",\n    longTaskCount: 0,\n    longTaskMs: 0,\n    maxTickGapMs: 0,\n    pageRenderCount: 0,\n    pageRenderMainThreadMs: 0,\n    pathDeviationMaxPx: 0,\n    residualPx: 0,\n    residualSnapped: false,\n    scale: { x: scaleX, y: scaleY },\n    scrollDriftMaxPx: 0,\n    settledClean: skipReason !== null,\n    skipReason,\n    startLatencyMs: null,\n    startSnapPx: null,\n    startedAt: readNow(),\n    status: skipReason === null ? \"played\" : \"skipped\",\n    tickCount: 0,\n    ticks: [],\n    translate: { x: translateX, y: translateY },\n  };\n}\n\nfunction recordZoomFlightSkip(reason: PdfZoomMotionSkipReason) {\n  pushZoomFlightRecord(\n    createZoomFlightRecord({\n      scaleX: 1,\n      scaleY: 1,\n      skipReason: reason,\n      translateX: 0,\n      translateY: 0,\n    }),\n  );\n  return null;\n}\n\nexport function createPdfZoomMotionController(\n  layout: PdfPageLayoutModel,\n): ViewerDocumentZoomMotionController<PdfZoomTransaction> {\n  return {\n    capture: ({ scrollTop, viewportElement }) =>\n      capturePdfZoomTransaction({ layout, scrollTop, viewportElement }),\n    noteBypass: (reason) => {\n      recordZoomFlightSkip(`bypass:${reason}`);\n    },\n    resolveScrollTarget: ({ transaction, viewportElement }) =>\n      resolvePdfZoomScrollTarget({ layout, transaction, viewportElement }),\n    play: ({ transaction, viewportElement }) =>\n      playPdfZoomMotion({ transaction, viewportElement }),\n  };\n}\n\nexport function capturePdfZoomTransaction({\n  layout,\n  scrollTop,\n  viewportElement,\n}: {\n  layout: PdfPageLayoutModel;\n  scrollTop: number;\n  viewportElement: HTMLDivElement;\n}): PdfZoomTransaction | null {\n  if (layout.pageCount === 0) return null;\n\n  const viewportBlockSize = Math.max(0, viewportElement.clientHeight);\n  const centerOffset =\n    Math.max(0, scrollTop) + viewportBlockSize * PDF_ZOOM_CENTER_MARKER_RATIO;\n  const pageNumber = findPdfPageByOffset(layout, centerOffset);\n  const pageLayout = getPdfPageLayout(layout, pageNumber);\n  if (!pageLayout || pageLayout.height <= 0) return null;\n\n  const paintedRect = readElementRect(findPdfZoomVisualLayer(viewportElement));\n  return {\n    capturedAt: readNow(),\n    pageNumber,\n    yPercent: (centerOffset - pageLayout.offsetTop) / pageLayout.height,\n    paintedAnchor:\n      paintedRect && paintedRect.width > 0 && paintedRect.height > 0\n        ? {\n            x:\n              (getViewportCenterX(viewportElement) - paintedRect.left) /\n              paintedRect.width,\n            y:\n              (getViewportCenterY(viewportElement) - paintedRect.top) /\n              paintedRect.height,\n          }\n        : null,\n    previousVisualRect: paintedRect,\n  };\n}\n\n// Both axes resolve by rect delta against the COMMITTED (untransformed)\n// visual layer: scroll by however far the anchored content point sits from\n// the viewport center. The rects ARE the painted DOM, so model↔DOM sub-pixel\n// mismatch cannot re-enter, and the math is direction-agnostic (RTL scroll\n// coordinate spaces and browser clamping fall out for free). The page-model\n// path stays as the fallback for a rebased physical scroll, where the layer\n// stops spanning the document.\nexport function resolvePdfZoomScrollTarget({\n  layout,\n  transaction,\n  viewportElement,\n}: {\n  layout: PdfPageLayoutModel;\n  transaction: PdfZoomTransaction;\n  viewportElement: HTMLDivElement;\n}): { left?: number; top: number } | null {\n  const viewportBlockSize = Math.max(0, viewportElement.clientHeight);\n  const maxScrollTop = Math.max(0, layout.totalHeight - viewportBlockSize);\n  const committedRect = readElementRect(\n    findPdfZoomVisualLayer(viewportElement),\n  );\n  const anchor = transaction.paintedAnchor;\n  const isLayerDocumentSpanning =\n    committedRect != null &&\n    Math.abs(committedRect.height - layout.totalHeight) < 2;\n\n  let top: number | null = null;\n  if (anchor && committedRect && isLayerDocumentSpanning) {\n    top = clamp(\n      viewportElement.scrollTop +\n        (committedRect.top + anchor.y * committedRect.height) -\n        getViewportCenterY(viewportElement),\n      0,\n      maxScrollTop,\n    );\n  }\n  if (top == null) {\n    const pageLayout = getPdfPageLayout(layout, transaction.pageNumber);\n    if (!pageLayout) return null;\n    top = clamp(\n      pageLayout.offsetTop +\n        pageLayout.height * transaction.yPercent -\n        viewportBlockSize * PDF_ZOOM_CENTER_MARKER_RATIO,\n      0,\n      maxScrollTop,\n    );\n  }\n\n  const left =\n    anchor && committedRect\n      ? viewportElement.scrollLeft +\n        (committedRect.left + anchor.x * committedRect.width) -\n        getViewportCenterX(viewportElement)\n      : undefined;\n\n  return { top, ...(left == null ? null : { left }) };\n}\n\n// The relax is a kernel-style rAF clock, not a CSS transition: the clock\n// anchors its ease at the first tick's FRAME time (the commit can burn ms\n// before anything paints, and an ease anchored at the click lands its first\n// painted frame that deep into the curve), every write is recorded as a\n// flight tick, and translate/scale share one eased progress so the anchor\n// point stays exactly fixed for the whole flight.\nexport function playPdfZoomMotion({\n  transaction,\n  viewportElement,\n}: {\n  transaction: PdfZoomTransaction;\n  viewportElement: HTMLDivElement;\n}): (() => void) | null {\n  if (typeof requestAnimationFrame !== \"function\") {\n    return recordZoomFlightSkip(\"no-raf\");\n  }\n  if (prefersReducedMotion()) return recordZoomFlightSkip(\"reduced-motion\");\n\n  const previousRect = transaction.previousVisualRect;\n  if (!previousRect) return recordZoomFlightSkip(\"no-previous-rect\");\n  const visualLayer = findPdfZoomVisualLayer(viewportElement);\n  if (!visualLayer) return recordZoomFlightSkip(\"no-visual-layer\");\n\n  const currentRect = readElementRect(visualLayer);\n  if (\n    !currentRect ||\n    previousRect.width <= 0 ||\n    previousRect.height <= 0 ||\n    currentRect.width <= 0 ||\n    currentRect.height <= 0\n  ) {\n    return recordZoomFlightSkip(\"zero-rect\");\n  }\n\n  const scaleX = previousRect.width / currentRect.width;\n  const scaleY = previousRect.height / currentRect.height;\n  if (hasDetachedPdfZoomAxes(scaleX, scaleY)) {\n    return recordZoomFlightSkip(\"axis-scale-mismatch\");\n  }\n\n  const translateX = previousRect.left - currentRect.left;\n  const translateY = previousRect.top - currentRect.top;\n  const hasVisibleDelta =\n    Math.abs(translateX) > PDF_ZOOM_MOTION_MIN_TRANSLATE_PX ||\n    Math.abs(translateY) > PDF_ZOOM_MOTION_MIN_TRANSLATE_PX ||\n    Math.abs(1 - scaleX) > PDF_ZOOM_MOTION_MIN_SCALE_DELTA ||\n    Math.abs(1 - scaleY) > PDF_ZOOM_MOTION_MIN_SCALE_DELTA;\n  if (!hasVisibleDelta) return recordZoomFlightSkip(\"no-visible-delta\");\n\n  // Re-express the relax about the viewport center instead of the stage's\n  // top-left: the stage is the full document (hundreds of thousands of px\n  // tall), and a scale that far from its origin runs into GPU float\n  // precision. Anchoring at the viewport keeps the rasterized region's\n  // coordinates small; the mapping is identical.\n  const originX = getViewportCenterX(viewportElement) - currentRect.left;\n  const originY = getViewportCenterY(viewportElement) - currentRect.top;\n  const residualX = translateX + (scaleX - 1) * originX;\n  const residualY = translateY + (scaleY - 1) * originY;\n\n  // The anchored translate IS the flight's deviation from a pure scale about\n  // the viewport center. A sub-pixel residual (scroll quantization) folds\n  // into the opening frame — invisible under the zoom onset, and the anchor\n  // then never moves. A larger residual is a clamped-edge pan: intended\n  // motion, animated.\n  const residualPx = Math.hypot(residualX, residualY);\n  const shouldSnapResidual =\n    residualPx > 0 && residualPx <= PDF_ZOOM_MOTION_ANCHOR_SNAP_EPSILON_PX;\n  const anchoredTranslateX = shouldSnapResidual ? 0 : residualX;\n  const anchoredTranslateY = shouldSnapResidual ? 0 : residualY;\n\n  const record = createZoomFlightRecord({\n    scaleX,\n    scaleY,\n    skipReason: null,\n    translateX: anchoredTranslateX,\n    translateY: anchoredTranslateY,\n  });\n  record.residualPx = residualPx;\n  record.residualSnapped = shouldSnapResidual;\n  pushZoomFlightRecord(record);\n  liveZoomFlightRecord = record;\n\n  const scrollLeftAtStart = viewportElement.scrollLeft;\n  const scrollTopAtStart = viewportElement.scrollTop;\n  const stopLongTaskObserver = observeZoomFlightLongTasks(record);\n  // Spatial probe basis: the anchored content point as fractions of the\n  // untransformed (committed) layer, and its expected fixed screen position.\n  // The per-tick painted rect re-derives the point; any wander is a real\n  // style-level pan, whatever wrote it.\n  const anchorFractionX = originX / currentRect.width;\n  const anchorFractionY = originY / currentRect.height;\n  const expectedAnchorScreenX = currentRect.left + originX;\n  const expectedAnchorScreenY = currentRect.top + originY;\n\n  let clockStartedAt: number | null = null;\n  let lastTickAt: number | null = null;\n  let rafHandle = 0;\n  let stallTimeout: ReturnType<typeof setTimeout> | null = null;\n  let finished = false;\n\n  const writeFrame = (progress: number) => {\n    const remaining = 1 - progress;\n    const frameTranslateX = anchoredTranslateX * remaining;\n    const frameTranslateY = anchoredTranslateY * remaining;\n    const frameScaleX = scaleX + (1 - scaleX) * progress;\n    const frameScaleY = scaleY + (1 - scaleY) * progress;\n    visualLayer.style.transform = `translate3d(${frameTranslateX}px, ${frameTranslateY}px, 0px) scale(${frameScaleX}, ${frameScaleY})`;\n  };\n\n  const finish = (interruption: PdfZoomMotionInterruption) => {\n    if (finished) return;\n    finished = true;\n    if (rafHandle !== 0) cancelAnimationFrame(rafHandle);\n    if (stallTimeout !== null) clearTimeout(stallTimeout);\n    stopLongTaskObserver();\n    visualLayer.style.transform = \"\";\n    visualLayer.style.transformOrigin = \"\";\n    visualLayer.style.willChange = \"\";\n    record.interruption = interruption;\n    record.settledClean =\n      visualLayer.style.transform === \"\" && visualLayer.style.willChange === \"\";\n    record.pathDeviationMaxPx = computeZoomFlightPathDeviation(record.ticks);\n    if (liveZoomFlightRecord === record) liveZoomFlightRecord = null;\n  };\n\n  const tick = (frameTime: number) => {\n    rafHandle = 0;\n    if (finished) return;\n    const now = Number.isFinite(frameTime) ? frameTime : readNow();\n    if (clockStartedAt === null) {\n      clockStartedAt = now;\n      record.startLatencyMs = Math.max(0, now - transaction.capturedAt);\n    }\n    const timeProgress = clamp(\n      PDF_ZOOM_MOTION_DURATION_MS <= 0\n        ? 1\n        : (now - clockStartedAt) / PDF_ZOOM_MOTION_DURATION_MS,\n      0,\n      1,\n    );\n    const progress = easePdfZoomMotion(timeProgress);\n    writeFrame(progress);\n\n    const scrollDriftPx = Math.max(\n      Math.abs(viewportElement.scrollTop - scrollTopAtStart),\n      Math.abs(viewportElement.scrollLeft - scrollLeftAtStart),\n    );\n    record.scrollDriftMaxPx = Math.max(record.scrollDriftMaxPx, scrollDriftPx);\n    const frameGapMs = lastTickAt === null ? 0 : now - lastTickAt;\n    lastTickAt = now;\n    record.maxTickGapMs = Math.max(record.maxTickGapMs, frameGapMs);\n    record.tickCount += 1;\n\n    const paintedRect = visualLayer.getBoundingClientRect();\n    record.anchorDriftMax.x = Math.max(\n      record.anchorDriftMax.x,\n      Math.abs(\n        paintedRect.left +\n          anchorFractionX * paintedRect.width -\n          expectedAnchorScreenX,\n      ),\n    );\n    record.anchorDriftMax.y = Math.max(\n      record.anchorDriftMax.y,\n      Math.abs(\n        paintedRect.top +\n          anchorFractionY * paintedRect.height -\n          expectedAnchorScreenY,\n      ),\n    );\n    if (record.startSnapPx === null) {\n      record.startSnapPx = Math.max(\n        Math.abs(paintedRect.left - previousRect.left),\n        Math.abs(paintedRect.top - previousRect.top),\n      );\n    }\n    if (record.ticks.length < PDF_ZOOM_FLIGHT_TICK_LIMIT) {\n      record.ticks.push({\n        elapsedMs: Math.max(0, now - clockStartedAt),\n        frameGapMs,\n        progress,\n        rectLeft: paintedRect.left,\n        rectTop: paintedRect.top,\n        rectWidth: paintedRect.width,\n        rectHeight: paintedRect.height,\n        scrollDriftPx,\n      });\n    }\n\n    if (timeProgress >= 1) {\n      finish(\"none\");\n      return;\n    }\n    rafHandle = requestAnimationFrame(tick);\n  };\n\n  // First frame is written synchronously inside the geometry commit, so the\n  // discontinuity is hidden behind the counter-transform before first paint\n  // (commit-then-relax); the clock itself starts at the next painted frame.\n  visualLayer.style.transformOrigin = `${originX}px ${originY}px`;\n  visualLayer.style.willChange = \"transform\";\n  writeFrame(0);\n\n  rafHandle = requestAnimationFrame(tick);\n  stallTimeout = setTimeout(\n    () => finish(\"stalled\"),\n    PDF_ZOOM_MOTION_STALL_TIMEOUT_MS,\n  );\n\n  return () => finish(\"cancelled\");\n}\n\n// Max perpendicular distance of the painted corner's trajectory from the\n// straight chord between its first and last tick positions. Shared-progress\n// translate+scale moves every point on a straight line, so any curvature is\n// foreign motion.\nfunction computeZoomFlightPathDeviation(\n  ticks: readonly PdfZoomMotionTick[],\n): number {\n  if (ticks.length < 3) return 0;\n  const first = ticks[0];\n  const last = ticks[ticks.length - 1];\n  const chordX = last.rectLeft - first.rectLeft;\n  const chordY = last.rectTop - first.rectTop;\n  const chordLength = Math.hypot(chordX, chordY);\n  let maxDeviation = 0;\n  for (const tick of ticks) {\n    const pointX = tick.rectLeft - first.rectLeft;\n    const pointY = tick.rectTop - first.rectTop;\n    const deviation =\n      chordLength <= 1e-6\n        ? Math.hypot(pointX, pointY)\n        : Math.abs(pointX * chordY - pointY * chordX) / chordLength;\n    maxDeviation = Math.max(maxDeviation, deviation);\n  }\n  return maxDeviation;\n}\n\nfunction observeZoomFlightLongTasks(record: PdfZoomMotionFlightRecord) {\n  if (typeof PerformanceObserver !== \"function\") return () => {};\n  try {\n    const observer = new PerformanceObserver((entries) => {\n      for (const entry of entries.getEntries()) {\n        record.longTaskCount += 1;\n        record.longTaskMs += entry.duration;\n      }\n    });\n    observer.observe({ entryTypes: [\"longtask\"] });\n    return () => observer.disconnect();\n  } catch {\n    // Long-task attribution is diagnostic, not correctness-critical; some\n    // engines do not expose the entry type.\n    return () => {};\n  }\n}\n\nfunction easePdfZoomMotion(timeProgress: number) {\n  // easeOutCubic: fast attack, gentle landing — a linear relax reads as a\n  // hard stop at settle.\n  const inverse = 1 - clamp(timeProgress, 0, 1);\n  return 1 - inverse * inverse * inverse;\n}\n\nfunction findPdfZoomVisualLayer(viewportElement: HTMLDivElement) {\n  if (typeof viewportElement.querySelector !== \"function\") return null;\n  return viewportElement.querySelector<HTMLElement>(\n    PDF_ZOOM_VISUAL_LAYER_SELECTOR,\n  );\n}\n\nfunction getViewportCenterX(viewportElement: HTMLDivElement) {\n  return (\n    viewportElement.getBoundingClientRect().left +\n    Math.max(0, viewportElement.clientWidth) / 2\n  );\n}\n\nfunction getViewportCenterY(viewportElement: HTMLDivElement) {\n  return (\n    viewportElement.getBoundingClientRect().top +\n    Math.max(0, viewportElement.clientHeight) / 2\n  );\n}\n\n// True when one axis is pinned while the other rescales — the paged-scroll\n// signature — or when the two ratios are so far apart that the stage box\n// cannot be tracking the content at all.\nfunction hasDetachedPdfZoomAxes(scaleX: number, scaleY: number) {\n  const inlineDelta = Math.abs(scaleX - 1);\n  const blockDelta = Math.abs(scaleY - 1);\n  const frozenAxis =\n    (blockDelta < PDF_ZOOM_MOTION_AXIS_FROZEN_DELTA &&\n      inlineDelta > PDF_ZOOM_MOTION_AXIS_MOVED_DELTA) ||\n    (inlineDelta < PDF_ZOOM_MOTION_AXIS_FROZEN_DELTA &&\n      blockDelta > PDF_ZOOM_MOTION_AXIS_MOVED_DELTA);\n  return (\n    frozenAxis ||\n    Math.abs(scaleX - scaleY) >\n      PDF_ZOOM_MOTION_AXIS_MISMATCH_RATIO * Math.max(scaleX, scaleY)\n  );\n}\n\nfunction readElementRect(element: HTMLElement | null) {\n  if (!element || typeof element.getBoundingClientRect !== \"function\") {\n    return null;\n  }\n  const rect = element.getBoundingClientRect();\n  return rect.width > 0 && rect.height > 0 ? rect : null;\n}\n\nfunction prefersReducedMotion() {\n  return (\n    typeof matchMedia === \"function\" &&\n    matchMedia(\"(prefers-reduced-motion: reduce)\").matches\n  );\n}\n\nfunction readNow() {\n  return typeof performance !== \"undefined\" &&\n    typeof performance.now === \"function\"\n    ? performance.now()\n    : Date.now();\n}\n",
      "type": "registry:ui",
      "target": "@ui/pdf-viewer-zoom-motion.ts"
    },
    {
      "path": "registry/new-york-v4/ui/viewer-document-scroll.ts",
      "content": "import * as React from \"react\";\n\nimport { useKeyedLayoutEffect } from \"@/hooks/use-keyed-layout-effect\";\nimport { useKeyedMountEffect } from \"@/hooks/use-keyed-mount-effect\";\nimport { useMountEffect } from \"@/hooks/use-mount-effect\";\nimport { joinEffectKey } from \"@/lib/effect-key\";\n\nimport type {\n  ViewerDocumentLayoutModel,\n  ViewerDocumentPhysicalScrollPosition,\n  ViewerDocumentResolvedScrollTarget,\n  ViewerDocumentScrollMapper,\n  ViewerDocumentScrollMetrics,\n  ViewerDocumentScrollTargetResolver,\n  ViewerDocumentZoomMotionController,\n} from \"./viewer-types\";\nimport { useViewerDocumentGeometryTransaction } from \"./viewer-document-geometry\";\n\nconst VIEWER_DOCUMENT_SCROLL_IDLE_MS = 120;\nconst VIEWER_DOCUMENT_SCROLL_POSITION_EPSILON = 1;\nconst VIEWER_DOCUMENT_SCROLL_RESTORE_MAX_FRAMES = 45;\nconst VIEWER_DOCUMENT_INTERNAL_SCROLL_SUPPRESS_MS = 120;\n// A zoom intent is consumed by the geometry commit its gesture causes; if no\n// commit claims it in this window (scale already clamped, controlled scale\n// ignored by the owner), it is stale and must not re-anchor a later,\n// unrelated geometry change.\nconst VIEWER_DOCUMENT_ZOOM_INTENT_MAX_AGE_MS = 400;\n\ntype ViewerDocumentScrollIntent<Target> =\n  | {\n      kind: \"idle\";\n    }\n  | {\n      kind: \"user\";\n    }\n  | {\n      behavior: ScrollBehavior;\n      kind: \"programmatic\";\n      sequence: number;\n      target: Target;\n      targetLeft?: number;\n      targetTop: number;\n    };\n\ntype PendingViewerDocumentScrollRestore = {\n  attempts: number;\n  options: ScrollToOptions;\n  targetTop: number;\n};\n\ntype ViewerDocumentScrollEventResult = {\n  source: \"internal\" | \"programmatic\" | \"user\";\n};\n\nexport function useViewerDocumentScroll<Anchor, Target, ZoomTransaction = unknown>({\n  copyScrollTarget,\n  layout,\n  resetKey,\n  resolveScrollTarget,\n  scrollMapper,\n  zoomMotion,\n}: {\n  copyScrollTarget?: (target: Target) => Target;\n  layout: ViewerDocumentLayoutModel<Anchor>;\n  resetKey?: unknown;\n  resolveScrollTarget?: ViewerDocumentScrollTargetResolver<Anchor, Target>;\n  scrollMapper: ViewerDocumentScrollMapper;\n  zoomMotion?: ViewerDocumentZoomMotionController<ZoomTransaction>;\n}) {\n  const viewportElementRef = React.useRef<HTMLDivElement | null>(null);\n  const scrollPageOffsetRef = React.useRef(0);\n  const pendingZoomIntentRef = React.useRef<{\n    capturedAt: number;\n    transaction: ZoomTransaction;\n  } | null>(null);\n  const activeZoomMotionCancelRef = React.useRef<(() => void) | null>(null);\n  const pendingScrollRestoreRef =\n    React.useRef<PendingViewerDocumentScrollRestore | null>(null);\n  const pendingScrollRestoreFrameRef = React.useRef(0);\n  const flushPendingScrollRestoreRef = React.useRef<() => void>(() => {});\n  const isInternalScrollWriteRef = React.useRef(false);\n  const internalScrollWriteTimeoutRef = React.useRef<ReturnType<\n    typeof setTimeout\n  > | null>(null);\n  const viewportResetKeyRef = React.useRef<unknown>(resetKey);\n  const didMountResetEffectRef = React.useRef(false);\n  const committedLayoutRef = React.useRef(layout);\n  const committedResetKeyRef = React.useRef<unknown>(resetKey);\n  const geometryTransaction = useViewerDocumentGeometryTransaction({\n    layout,\n    resetKey,\n  });\n  const {\n    cacheAnchor,\n    cacheReadingAnchor,\n    cacheVisualLayerRect,\n    cancel: cancelGeometryTransaction,\n    classifyLayoutChange,\n    commit,\n    prepare,\n    recordStableState,\n    reset: resetGeometryTransaction,\n  } = geometryTransaction;\n  const scrollIntent = useViewerDocumentScrollIntentController<Target>();\n  const [viewportElement, setViewportElementState] =\n    React.useState<HTMLDivElement | null>(null);\n\n  const cancelPendingScrollRestore = React.useCallback(() => {\n    pendingScrollRestoreRef.current = null;\n    if (pendingScrollRestoreFrameRef.current === 0) return;\n    cancelAnimationFrame(pendingScrollRestoreFrameRef.current);\n    pendingScrollRestoreFrameRef.current = 0;\n  }, []);\n\n  // Interrupting a zoom relax snaps to its committed endpoint: the layout and\n  // scroll landed in the zoom's own commit, so clearing the transform is\n  // always safe and never moves the settled geometry.\n  const cancelZoomMotion = React.useCallback(() => {\n    pendingZoomIntentRef.current = null;\n    const cancelActiveZoomMotion = activeZoomMotionCancelRef.current;\n    activeZoomMotionCancelRef.current = null;\n    cancelActiveZoomMotion?.();\n  }, []);\n\n  const captureZoomIntent = React.useCallback(() => {\n    const viewportElement = viewportElementRef.current;\n    if (!viewportElement || !zoomMotion) {\n      pendingZoomIntentRef.current = null;\n      return;\n    }\n    const metrics = readViewerDocumentScrollMetrics({\n      layout,\n      scrollMapper,\n      scrollPageOffset: scrollPageOffsetRef.current,\n      viewportElement,\n    });\n    const transaction = zoomMotion.capture({\n      scrollTop: metrics.scrollTop,\n      viewportElement,\n    });\n    pendingZoomIntentRef.current =\n      transaction == null\n        ? null\n        : { capturedAt: readViewerDocumentNow(), transaction };\n  }, [layout, scrollMapper, zoomMotion]);\n\n  const clearInternalScrollWrite = React.useCallback(() => {\n    isInternalScrollWriteRef.current = false;\n    if (internalScrollWriteTimeoutRef.current === null) return;\n    clearTimeout(internalScrollWriteTimeoutRef.current);\n    internalScrollWriteTimeoutRef.current = null;\n  }, []);\n\n  const markInternalScrollWrite = React.useCallback(() => {\n    isInternalScrollWriteRef.current = true;\n    if (internalScrollWriteTimeoutRef.current !== null) {\n      clearTimeout(internalScrollWriteTimeoutRef.current);\n    }\n    internalScrollWriteTimeoutRef.current = setTimeout(() => {\n      internalScrollWriteTimeoutRef.current = null;\n      isInternalScrollWriteRef.current = false;\n    }, VIEWER_DOCUMENT_INTERNAL_SCROLL_SUPPRESS_MS);\n  }, []);\n\n  const resetViewportForKey = React.useCallback(\n    (element: HTMLDivElement, key: unknown) => {\n      cancelPendingScrollRestore();\n      cancelZoomMotion();\n      viewportResetKeyRef.current = key;\n      scrollPageOffsetRef.current = 0;\n      resetGeometryTransaction(element);\n      scrollIntent.reset();\n      markInternalScrollWrite();\n      setViewportPhysicalScrollTop(element, 0);\n      element.scrollTo?.({ top: 0, behavior: \"auto\" });\n    },\n    [\n      cancelPendingScrollRestore,\n      cancelZoomMotion,\n      markInternalScrollWrite,\n      resetGeometryTransaction,\n      scrollIntent,\n    ],\n  );\n\n  const setViewportElement = React.useCallback(\n    (element: HTMLDivElement | null) => {\n      viewportElementRef.current = element;\n      if (element && !Object.is(viewportResetKeyRef.current, resetKey)) {\n        resetViewportForKey(element, resetKey);\n      }\n      setViewportElementState(element);\n    },\n    [resetKey, resetViewportForKey],\n  );\n\n  const readScrollMetrics = React.useCallback(\n    () =>\n      readViewerDocumentScrollMetrics({\n        layout,\n        scrollMapper,\n        scrollPageOffset: scrollPageOffsetRef.current,\n        viewportElement: viewportElementRef.current,\n      }),\n    [layout, scrollMapper],\n  );\n\n  const syncViewportScrollPosition = React.useCallback(\n    (viewportElement: HTMLDivElement, blockSize = layout.blockSize) => {\n      const metrics = readViewerDocumentScrollMetrics({\n        layout: {\n          ...layout,\n          blockSize,\n        },\n        scrollMapper,\n        scrollPageOffset: scrollPageOffsetRef.current,\n        viewportElement,\n      });\n\n      if (metrics.physicalScrollSize >= blockSize) {\n        const nextMetrics = {\n          ...metrics,\n          scrollPageOffset: hasViewerDocumentScrollPageOffset(\n            scrollPageOffsetRef.current,\n          )\n            ? metrics.scrollPageOffset\n            : 0,\n        };\n        scrollPageOffsetRef.current = nextMetrics.scrollPageOffset;\n        cacheReadingAnchor(nextMetrics);\n        cacheVisualLayerRect(viewportElement);\n        return nextMetrics;\n      }\n\n      const position = scrollMapper.resolvePhysicalScrollPosition({\n        blockSize,\n        logicalScrollTop: metrics.scrollTop,\n        scrollPageOffset: metrics.scrollPageOffset,\n        viewportBlockSize: metrics.viewportBlockSize,\n      });\n      scrollPageOffsetRef.current = position.scrollPageOffset;\n      markInternalScrollWrite();\n      setViewportPhysicalScrollTop(viewportElement, position.physicalScrollTop);\n\n      const nextMetrics = {\n        ...metrics,\n        physicalScrollTop: position.physicalScrollTop,\n        scrollPageOffset: position.scrollPageOffset,\n      };\n      cacheReadingAnchor(nextMetrics);\n      cacheVisualLayerRect(viewportElement);\n      return nextMetrics;\n    },\n    [\n      cacheReadingAnchor,\n      cacheVisualLayerRect,\n      layout,\n      markInternalScrollWrite,\n      scrollMapper,\n    ],\n  );\n\n  const syncScrollPosition = React.useCallback(() => {\n    const viewportElement = viewportElementRef.current;\n    return viewportElement ? syncViewportScrollPosition(viewportElement) : null;\n  }, [syncViewportScrollPosition]);\n\n  const schedulePendingScrollRestore = React.useCallback(\n    (restore: PendingViewerDocumentScrollRestore) => {\n      pendingScrollRestoreRef.current = restore;\n      if (pendingScrollRestoreFrameRef.current !== 0) return;\n      pendingScrollRestoreFrameRef.current = requestAnimationFrame(() => {\n        pendingScrollRestoreFrameRef.current = 0;\n        flushPendingScrollRestoreRef.current();\n      });\n    },\n    [],\n  );\n\n  const resolveLogicalScrollPosition = React.useCallback(\n    (viewportElement: HTMLDivElement, targetTop: number) => {\n      const viewportBlockSize = viewportElement.clientHeight;\n      const position = scrollMapper.resolvePhysicalScrollPosition({\n        blockSize: layout.blockSize,\n        logicalScrollTop: targetTop,\n        scrollPageOffset: scrollPageOffsetRef.current,\n        viewportBlockSize,\n      });\n      const expectedPhysicalScrollSize = scrollMapper.getPhysicalScrollSize({\n        blockSize: layout.blockSize,\n        viewportBlockSize,\n      });\n\n      return {\n        expectedMaxPhysicalScrollTop: Math.max(\n          0,\n          expectedPhysicalScrollSize - viewportBlockSize,\n        ),\n        position,\n      };\n    },\n    [layout.blockSize, scrollMapper],\n  );\n\n  const applyLogicalScrollTop = React.useCallback(\n    (\n      viewportElement: HTMLDivElement,\n      targetTop: number,\n      options: ScrollToOptions,\n      {\n        allowDefer,\n        preservePhysicalScrollTop = false,\n      }: { allowDefer: boolean; preservePhysicalScrollTop?: boolean },\n    ) => {\n      if (preservePhysicalScrollTop) {\n        scrollPageOffsetRef.current = targetTop - viewportElement.scrollTop;\n        cacheReadingAnchor({\n          scrollTop: targetTop,\n          viewportBlockSize: viewportElement.clientHeight,\n        });\n        cacheVisualLayerRect(viewportElement);\n        return true;\n      }\n\n      const { expectedMaxPhysicalScrollTop, position } =\n        resolveLogicalScrollPosition(viewportElement, targetTop);\n      const currentMaxPhysicalScrollTop =\n        getViewportMaxPhysicalScrollTop(viewportElement);\n      const shouldDefer =\n        allowDefer &&\n        position.physicalScrollTop >\n          currentMaxPhysicalScrollTop +\n            VIEWER_DOCUMENT_SCROLL_POSITION_EPSILON &&\n        position.physicalScrollTop <=\n          expectedMaxPhysicalScrollTop +\n            VIEWER_DOCUMENT_SCROLL_POSITION_EPSILON;\n\n      if (shouldDefer) {\n        schedulePendingScrollRestore({\n          attempts: 0,\n          options: {\n            ...options,\n            behavior: \"auto\",\n          },\n          targetTop,\n        });\n        return false;\n      }\n\n      scrollPageOffsetRef.current = position.scrollPageOffset;\n      markInternalScrollWrite();\n      scrollViewportToPhysicalTop(viewportElement, position.physicalScrollTop, {\n        behavior: \"auto\",\n        ...options,\n      });\n      return true;\n    },\n    [\n      cacheReadingAnchor,\n      cacheVisualLayerRect,\n      markInternalScrollWrite,\n      resolveLogicalScrollPosition,\n      schedulePendingScrollRestore,\n    ],\n  );\n\n  const flushPendingScrollRestore = React.useCallback(() => {\n    const pendingRestore = pendingScrollRestoreRef.current;\n    const viewportElement = viewportElementRef.current;\n    if (!pendingRestore || !viewportElement) return;\n\n    const { expectedMaxPhysicalScrollTop, position } =\n      resolveLogicalScrollPosition(viewportElement, pendingRestore.targetTop);\n    const currentMaxPhysicalScrollTop =\n      getViewportMaxPhysicalScrollTop(viewportElement);\n    const shouldWaitForRange =\n      position.physicalScrollTop >\n        currentMaxPhysicalScrollTop + VIEWER_DOCUMENT_SCROLL_POSITION_EPSILON &&\n      position.physicalScrollTop <=\n        expectedMaxPhysicalScrollTop +\n          VIEWER_DOCUMENT_SCROLL_POSITION_EPSILON &&\n      pendingRestore.attempts < VIEWER_DOCUMENT_SCROLL_RESTORE_MAX_FRAMES;\n\n    if (shouldWaitForRange) {\n      schedulePendingScrollRestore({\n        ...pendingRestore,\n        attempts: pendingRestore.attempts + 1,\n      });\n      return;\n    }\n\n    pendingScrollRestoreRef.current = null;\n    scrollPageOffsetRef.current = position.scrollPageOffset;\n    markInternalScrollWrite();\n    scrollViewportToPhysicalTop(\n      viewportElement,\n      position.physicalScrollTop,\n      pendingRestore.options,\n    );\n  }, [\n    markInternalScrollWrite,\n    resolveLogicalScrollPosition,\n    schedulePendingScrollRestore,\n  ]);\n\n  useKeyedLayoutEffect(joinEffectKey([flushPendingScrollRestore]), () => {\n    flushPendingScrollRestoreRef.current = flushPendingScrollRestore;\n    flushPendingScrollRestore();\n  });\n\n  const materializeScrollPageOffset = React.useCallback(() => {\n    const viewportElement = viewportElementRef.current;\n    if (\n      !viewportElement ||\n      !hasViewerDocumentScrollPageOffset(scrollPageOffsetRef.current)\n    ) {\n      return;\n    }\n\n    const metrics = readViewerDocumentScrollMetrics({\n      layout,\n      scrollMapper,\n      scrollPageOffset: scrollPageOffsetRef.current,\n      viewportElement,\n    });\n    const position = scrollMapper.resolvePhysicalScrollPosition({\n      blockSize: layout.blockSize,\n      logicalScrollTop: metrics.scrollTop,\n      scrollPageOffset: 0,\n      viewportBlockSize: metrics.viewportBlockSize,\n    });\n    scrollPageOffsetRef.current = position.scrollPageOffset;\n    markInternalScrollWrite();\n    scrollViewportToPhysicalTop(viewportElement, position.physicalScrollTop, {\n      behavior: \"auto\",\n    });\n    cacheReadingAnchor({\n      scrollTop: metrics.scrollTop,\n      viewportBlockSize: metrics.viewportBlockSize,\n    });\n    cacheVisualLayerRect(viewportElement);\n  }, [\n    cacheReadingAnchor,\n    cacheVisualLayerRect,\n    layout,\n    markInternalScrollWrite,\n    scrollMapper,\n  ]);\n\n  const scrollViewportToLogicalTop = React.useCallback(\n    (\n      viewportElement: HTMLDivElement,\n      targetTop: number,\n      options?: ScrollToOptions,\n      behavior?: {\n        preservePhysicalScrollTop?: boolean;\n      },\n    ) => {\n      applyLogicalScrollTop(\n        viewportElement,\n        targetTop,\n        {\n          behavior: \"auto\",\n          ...options,\n        },\n        {\n          allowDefer: true,\n          preservePhysicalScrollTop: behavior?.preservePhysicalScrollTop,\n        },\n      );\n      cacheReadingAnchor({\n        scrollTop: targetTop,\n        viewportBlockSize: viewportElement.clientHeight,\n      });\n    },\n    [applyLogicalScrollTop, cacheReadingAnchor],\n  );\n\n  const scrollViewportToResolvedTarget = React.useCallback(\n    (\n      viewportElement: HTMLDivElement,\n      target: ViewerDocumentResolvedScrollTarget,\n      options: ScrollToOptions,\n    ) => {\n      const physicalScrollSize = scrollMapper.getPhysicalScrollSize({\n        blockSize: layout.blockSize,\n        viewportBlockSize: viewportElement.clientHeight,\n      });\n      const position =\n        physicalScrollSize < layout.blockSize\n          ? scrollMapper.resolvePhysicalScrollPosition({\n              blockSize: layout.blockSize,\n              logicalScrollTop: target.top,\n              scrollPageOffset: scrollPageOffsetRef.current,\n              viewportBlockSize: viewportElement.clientHeight,\n            })\n          : {\n              physicalScrollTop: Math.max(0, target.top),\n              scrollPageOffset: 0,\n            };\n      scrollPageOffsetRef.current = position.scrollPageOffset;\n      viewportElement.scrollTo({\n        top: position.physicalScrollTop,\n        ...(target.left == null ? null : { left: target.left }),\n        ...options,\n      });\n    },\n    [layout.blockSize, scrollMapper],\n  );\n\n  useKeyedLayoutEffect(\n    joinEffectKey([\n      applyLogicalScrollTop,\n      copyScrollTarget,\n      cacheAnchor,\n      cacheReadingAnchor,\n      cacheVisualLayerRect,\n      cancelZoomMotion,\n      classifyLayoutChange,\n      commit,\n      layout,\n      markInternalScrollWrite,\n      prepare,\n      recordStableState,\n      resetKey,\n      resetGeometryTransaction,\n      resolveScrollTarget,\n      scrollIntent,\n      scrollMapper,\n      scrollViewportToLogicalTop,\n      scrollViewportToResolvedTarget,\n      syncViewportScrollPosition,\n      zoomMotion,\n    ]),\n    () => {\n      const previousLayout = committedLayoutRef.current;\n      const previousResetKey = committedResetKeyRef.current;\n      committedLayoutRef.current = layout;\n      committedResetKeyRef.current = resetKey;\n\n      const viewportElement = viewportElementRef.current;\n      if (!viewportElement) return;\n\n      const layoutChange = classifyLayoutChange({\n        previousLayout,\n        previousResetKey,\n      });\n      if (layoutChange === \"reset\") {\n        cancelZoomMotion();\n        resetGeometryTransaction(viewportElement);\n        return;\n      }\n\n      const currentLogicalScrollTop = scrollMapper.getLogicalScrollTop({\n        blockSize: layout.blockSize,\n        physicalScrollTop: viewportElement.scrollTop,\n        scrollPageOffset: scrollPageOffsetRef.current,\n        viewportBlockSize: viewportElement.clientHeight,\n      });\n      if (layoutChange !== \"geometry\") {\n        recordStableState({\n          clearTransition:\n            layoutChange === \"metadata\" && !layout.isTransitioning,\n          scrollTop: currentLogicalScrollTop,\n          viewportBlockSize: viewportElement.clientHeight,\n          viewportElement,\n        });\n        return;\n      }\n\n      // A fresh geometry commit owns the visual layer; an in-flight zoom\n      // relax against the previous geometry can no longer settle correctly.\n      const pendingZoomIntent = pendingZoomIntentRef.current;\n      cancelZoomMotion();\n\n      const transition = layout.transition;\n      if (pendingZoomIntent && zoomMotion) {\n        if (transition?.source === \"viewer-shell\") {\n          zoomMotion.noteBypass?.(\"shell-transition\");\n        } else if (\n          readViewerDocumentNow() - pendingZoomIntent.capturedAt >\n          VIEWER_DOCUMENT_ZOOM_INTENT_MAX_AGE_MS\n        ) {\n          zoomMotion.noteBypass?.(\"stale-intent\");\n        } else {\n          const zoomTarget = zoomMotion.resolveScrollTarget({\n            transaction: pendingZoomIntent.transaction,\n            viewportElement,\n          });\n          if (!zoomTarget) {\n            zoomMotion.noteBypass?.(\"resolve-failed\");\n          } else {\n            // Commit-then-relax: land the centered scroll inside this commit\n            // (never deferred), then relax the painted FLIP over it.\n            applyLogicalScrollTop(\n              viewportElement,\n              zoomTarget.top,\n              { behavior: \"auto\" },\n              { allowDefer: false },\n            );\n            if (zoomTarget.left != null) {\n              markInternalScrollWrite();\n              setViewportPhysicalScrollLeft(viewportElement, zoomTarget.left);\n            }\n            cacheReadingAnchor({\n              scrollTop: zoomTarget.top,\n              viewportBlockSize: viewportElement.clientHeight,\n            });\n            cacheVisualLayerRect(viewportElement);\n            activeZoomMotionCancelRef.current = zoomMotion.play({\n              transaction: pendingZoomIntent.transaction,\n              viewportElement,\n            });\n            return;\n          }\n        }\n      }\n\n      const activeIntent = scrollIntent.current();\n      if (\n        activeIntent.kind === \"programmatic\" &&\n        resolveScrollTarget &&\n        transition?.source !== \"viewer-shell\"\n      ) {\n        const metrics = syncViewportScrollPosition(viewportElement);\n        const target = resolveScrollTarget({\n          layout,\n          scrollTop: metrics.scrollTop,\n          target: activeIntent.target,\n          viewportElement,\n        });\n        if (!target) return;\n\n        const { intent, targetChanged } = scrollIntent.updateProgrammaticTarget(\n          activeIntent,\n          target,\n        );\n\n        if (targetChanged) {\n          scrollViewportToResolvedTarget(viewportElement, target, {\n            behavior: intent.behavior,\n          });\n          scrollIntent.scheduleProgrammaticCompletion(intent.sequence);\n        }\n        cacheVisualLayerRect(viewportElement);\n        return;\n      }\n\n      const previousLogicalScrollTop = scrollMapper.getLogicalScrollTop({\n        blockSize: previousLayout.blockSize,\n        physicalScrollTop: viewportElement.scrollTop,\n        scrollPageOffset: scrollPageOffsetRef.current,\n        viewportBlockSize: viewportElement.clientHeight,\n      });\n      const viewportBlockSize = viewportElement.clientHeight;\n      const transaction = prepare({\n        previousLayout,\n        scrollTop: previousLogicalScrollTop,\n        viewportBlockSize,\n      });\n      if (!transaction) return;\n\n      if (transaction.shouldRestoreScroll) {\n        const targetTop = layout.getReadingAnchorScrollTop({\n          anchor: transaction.anchor,\n          viewportBlockSize: transaction.viewportBlockSize,\n        });\n        if (targetTop != null) {\n          // Shell motions always rebase (commit-then-relax rebases scroll in\n          // the slide-start commit), so the restore is a real scroll write.\n          scrollViewportToLogicalTop(viewportElement, targetTop);\n        } else {\n          cacheAnchor({\n            anchor: transaction.anchor,\n            viewportBlockSize,\n          });\n        }\n      } else {\n        cacheAnchor({\n          anchor: transaction.anchor,\n          viewportBlockSize: transaction.viewportBlockSize,\n        });\n      }\n\n      commit({\n        transaction,\n        viewportElement,\n      });\n    },\n  );\n\n  const handleScroll = React.useCallback((): ViewerDocumentScrollEventResult => {\n    const viewportElement = viewportElementRef.current;\n    if (viewportElement && isInternalScrollWriteRef.current) {\n      const metrics = readScrollMetrics();\n      cacheReadingAnchor(metrics);\n      cacheVisualLayerRect(viewportElement);\n      return { source: \"internal\" };\n    }\n\n    if (viewportElement) {\n      syncViewportScrollPosition(viewportElement);\n    }\n\n    const activeIntent = scrollIntent.current();\n    if (activeIntent.kind === \"programmatic\") {\n      scrollIntent.scheduleProgrammaticCompletion(activeIntent.sequence);\n      return { source: \"programmatic\" };\n    } else {\n      scrollIntent.markUser();\n      return { source: \"user\" };\n    }\n  }, [\n    cacheReadingAnchor,\n    cacheVisualLayerRect,\n    readScrollMetrics,\n    scrollIntent,\n    syncViewportScrollPosition,\n  ]);\n\n  const markUserScrollIntent = React.useCallback(() => {\n    cancelZoomMotion();\n    materializeScrollPageOffset();\n    cancelPendingScrollRestore();\n    scrollIntent.markUser();\n  }, [\n    cancelPendingScrollRestore,\n    cancelZoomMotion,\n    materializeScrollPageOffset,\n    scrollIntent,\n  ]);\n\n  useKeyedMountEffect(joinEffectKey([resetKey, resetViewportForKey]), () => {\n    if (!didMountResetEffectRef.current) {\n      didMountResetEffectRef.current = true;\n      return;\n    }\n    const viewportElement = viewportElementRef.current;\n    if (viewportElement) {\n      resetViewportForKey(viewportElement, resetKey);\n    }\n  });\n\n  useKeyedMountEffect(\n    joinEffectKey([markUserScrollIntent, scrollIntent, viewportElement]),\n    () => {\n      const viewportElement = viewportElementRef.current;\n      if (!viewportElement) return;\n\n      const handleScrollEnd = () => {\n        const activeIntent = scrollIntent.current();\n        if (activeIntent.kind === \"programmatic\") {\n          scrollIntent.completeProgrammatic(activeIntent.sequence);\n        } else {\n          scrollIntent.reset();\n        }\n      };\n\n      viewportElement.addEventListener?.(\"wheel\", markUserScrollIntent, {\n        passive: true,\n      });\n      viewportElement.addEventListener?.(\"touchstart\", markUserScrollIntent, {\n        passive: true,\n      });\n      viewportElement.addEventListener?.(\"pointerdown\", markUserScrollIntent);\n      viewportElement.addEventListener?.(\"keydown\", markUserScrollIntent);\n      viewportElement.addEventListener?.(\"scrollend\", handleScrollEnd);\n\n      return () => {\n        viewportElement.removeEventListener?.(\"wheel\", markUserScrollIntent);\n        viewportElement.removeEventListener?.(\n          \"touchstart\",\n          markUserScrollIntent,\n        );\n        viewportElement.removeEventListener?.(\n          \"pointerdown\",\n          markUserScrollIntent,\n        );\n        viewportElement.removeEventListener?.(\"keydown\", markUserScrollIntent);\n        viewportElement.removeEventListener?.(\"scrollend\", handleScrollEnd);\n      };\n    },\n  );\n\n  const scrollToTarget = React.useCallback(\n    (target: Target, options?: ScrollToOptions) => {\n      const viewportElement = viewportElementRef.current;\n      const metrics = readScrollMetrics();\n      const resolvedTarget =\n        viewportElement && resolveScrollTarget\n          ? resolveScrollTarget({\n              layout,\n              scrollTop: metrics.scrollTop,\n              target,\n              viewportElement,\n            })\n          : null;\n      if (!viewportElement || !resolvedTarget) return;\n\n      cancelPendingScrollRestore();\n      cancelZoomMotion();\n      const behavior = options?.behavior ?? \"smooth\";\n      const intent = scrollIntent.startProgrammatic({\n        behavior,\n        copyScrollTarget,\n        resolvedTarget,\n        target,\n      });\n      scrollViewportToResolvedTarget(viewportElement, resolvedTarget, {\n        behavior: \"smooth\",\n        ...options,\n      });\n      scrollIntent.scheduleProgrammaticCompletion(intent.sequence);\n    },\n    [\n      cancelPendingScrollRestore,\n      cancelZoomMotion,\n      copyScrollTarget,\n      layout,\n      readScrollMetrics,\n      resolveScrollTarget,\n      scrollIntent,\n      scrollViewportToResolvedTarget,\n    ],\n  );\n  const getViewportElement = React.useCallback(\n    () => viewportElementRef.current,\n    [],\n  );\n\n  useMountEffect(() => () => {\n    clearInternalScrollWrite();\n    cancelPendingScrollRestore();\n    cancelZoomMotion();\n    cancelGeometryTransaction();\n    scrollIntent.clearProgrammaticTimeout();\n  });\n\n  return React.useMemo(\n    () => ({\n      captureZoomIntent,\n      getScrollMetrics: readScrollMetrics,\n      getViewportElement,\n      handleScroll,\n      scrollToTarget,\n      scrollViewportToLogicalTop,\n      setViewportElement,\n      syncScrollPosition,\n      viewportElement,\n    }),\n    [\n      captureZoomIntent,\n      getViewportElement,\n      handleScroll,\n      readScrollMetrics,\n      scrollToTarget,\n      scrollViewportToLogicalTop,\n      setViewportElement,\n      syncScrollPosition,\n      viewportElement,\n    ],\n  );\n}\n\nfunction useViewerDocumentScrollIntentController<Target>() {\n  const scrollIntentRef = React.useRef<ViewerDocumentScrollIntent<Target>>({\n    kind: \"idle\",\n  });\n  const scrollIntentSequenceRef = React.useRef(0);\n  const programmaticScrollIdleTimeoutRef = React.useRef<ReturnType<\n    typeof setTimeout\n  > | null>(null);\n\n  const clearProgrammaticTimeout = React.useCallback(() => {\n    if (programmaticScrollIdleTimeoutRef.current) {\n      clearTimeout(programmaticScrollIdleTimeoutRef.current);\n      programmaticScrollIdleTimeoutRef.current = null;\n    }\n  }, []);\n\n  const reset = React.useCallback(() => {\n    clearProgrammaticTimeout();\n    scrollIntentRef.current = { kind: \"idle\" };\n  }, [clearProgrammaticTimeout]);\n\n  const completeProgrammatic = React.useCallback(\n    (sequence: number) => {\n      if (\n        scrollIntentRef.current.kind === \"programmatic\" &&\n        scrollIntentRef.current.sequence === sequence\n      ) {\n        scrollIntentRef.current = { kind: \"idle\" };\n      }\n      clearProgrammaticTimeout();\n    },\n    [clearProgrammaticTimeout],\n  );\n\n  const scheduleProgrammaticCompletion = React.useCallback(\n    (sequence: number) => {\n      clearProgrammaticTimeout();\n      programmaticScrollIdleTimeoutRef.current = setTimeout(() => {\n        completeProgrammatic(sequence);\n      }, VIEWER_DOCUMENT_SCROLL_IDLE_MS);\n    },\n    [clearProgrammaticTimeout, completeProgrammatic],\n  );\n\n  const markUser = React.useCallback(() => {\n    clearProgrammaticTimeout();\n    scrollIntentRef.current = { kind: \"user\" };\n  }, [clearProgrammaticTimeout]);\n\n  const startProgrammatic = React.useCallback(\n    ({\n      behavior,\n      copyScrollTarget,\n      resolvedTarget,\n      target,\n    }: {\n      behavior: ScrollBehavior;\n      copyScrollTarget?: (target: Target) => Target;\n      resolvedTarget: ViewerDocumentResolvedScrollTarget;\n      target: Target;\n    }) => {\n      const sequence = scrollIntentSequenceRef.current + 1;\n      scrollIntentSequenceRef.current = sequence;\n      clearProgrammaticTimeout();\n\n      const intent: Extract<\n        ViewerDocumentScrollIntent<Target>,\n        { kind: \"programmatic\" }\n      > = {\n        behavior,\n        kind: \"programmatic\",\n        sequence,\n        target: copyScrollTarget ? copyScrollTarget(target) : target,\n        targetLeft: resolvedTarget.left,\n        targetTop: resolvedTarget.top,\n      };\n      scrollIntentRef.current = intent;\n      return intent;\n    },\n    [clearProgrammaticTimeout],\n  );\n\n  const updateProgrammaticTarget = React.useCallback(\n    (\n      intent: Extract<\n        ViewerDocumentScrollIntent<Target>,\n        { kind: \"programmatic\" }\n      >,\n      target: ViewerDocumentResolvedScrollTarget,\n    ) => {\n      const targetChanged =\n        Math.abs(intent.targetTop - target.top) >\n          VIEWER_DOCUMENT_SCROLL_POSITION_EPSILON ||\n        Math.abs((intent.targetLeft ?? 0) - (target.left ?? 0)) >\n          VIEWER_DOCUMENT_SCROLL_POSITION_EPSILON;\n      const nextIntent = {\n        ...intent,\n        targetLeft: target.left,\n        targetTop: target.top,\n      };\n      scrollIntentRef.current = nextIntent;\n      return { intent: nextIntent, targetChanged };\n    },\n    [],\n  );\n\n  useKeyedMountEffect(joinEffectKey([clearProgrammaticTimeout]), () => () => {\n    clearProgrammaticTimeout();\n  });\n\n  return React.useMemo(\n    () => ({\n      clearProgrammaticTimeout,\n      completeProgrammatic,\n      current: () => scrollIntentRef.current,\n      markUser,\n      reset,\n      scheduleProgrammaticCompletion,\n      startProgrammatic,\n      updateProgrammaticTarget,\n    }),\n    [\n      clearProgrammaticTimeout,\n      completeProgrammatic,\n      markUser,\n      reset,\n      scheduleProgrammaticCompletion,\n      startProgrammatic,\n      updateProgrammaticTarget,\n    ],\n  );\n}\n\nfunction readViewerDocumentScrollMetrics<Anchor>({\n  layout,\n  scrollMapper,\n  scrollPageOffset,\n  viewportElement,\n}: {\n  layout: ViewerDocumentLayoutModel<Anchor>;\n  scrollMapper: ViewerDocumentScrollMapper;\n  scrollPageOffset: number;\n  viewportElement: HTMLDivElement | null;\n}): ViewerDocumentScrollMetrics {\n  const viewportBlockSize = viewportElement?.clientHeight ?? 0;\n  const physicalScrollTop = viewportElement?.scrollTop ?? 0;\n  const physicalScrollSize = scrollMapper.getPhysicalScrollSize({\n    blockSize: layout.blockSize,\n    viewportBlockSize,\n  });\n  const isRebased = physicalScrollSize < layout.blockSize;\n  const hasLogicalOffset = hasViewerDocumentScrollPageOffset(scrollPageOffset);\n  const shouldUseLogicalOffset = isRebased || hasLogicalOffset;\n\n  return {\n    physicalScrollSize,\n    physicalScrollTop,\n    scrollPageOffset: shouldUseLogicalOffset ? scrollPageOffset : 0,\n    scrollTop: shouldUseLogicalOffset\n      ? scrollMapper.getLogicalScrollTop({\n          blockSize: layout.blockSize,\n          physicalScrollTop,\n          scrollPageOffset,\n          viewportBlockSize,\n        })\n      : Math.max(0, physicalScrollTop),\n    viewportBlockSize,\n  };\n}\n\nfunction hasViewerDocumentScrollPageOffset(scrollPageOffset: number) {\n  return Math.abs(scrollPageOffset) > VIEWER_DOCUMENT_SCROLL_POSITION_EPSILON;\n}\n\nfunction scrollViewportToPhysicalTop(\n  viewportElement: HTMLDivElement,\n  targetTop: number,\n  options: ScrollToOptions,\n) {\n  if (\n    Math.abs(viewportElement.scrollTop - targetTop) <=\n    VIEWER_DOCUMENT_SCROLL_POSITION_EPSILON\n  ) {\n    return;\n  }\n\n  if (typeof viewportElement.scrollTo === \"function\") {\n    viewportElement.scrollTo({\n      top: targetTop,\n      ...options,\n    });\n    return;\n  }\n\n  setViewportPhysicalScrollTop(viewportElement, targetTop);\n}\n\nfunction getViewportMaxPhysicalScrollTop(viewportElement: HTMLDivElement) {\n  return Math.max(\n    0,\n    viewportElement.scrollHeight - viewportElement.clientHeight,\n  );\n}\n\nfunction setViewportPhysicalScrollTop(\n  viewportElement: HTMLDivElement,\n  targetTop: number,\n) {\n  if (\n    Math.abs(viewportElement.scrollTop - targetTop) <=\n    VIEWER_DOCUMENT_SCROLL_POSITION_EPSILON\n  ) {\n    return;\n  }\n\n  viewportElement.scrollTop = targetTop;\n}\n\n// Raw assignment on purpose: the browser clamps to the live scrollable range\n// (including RTL's negative coordinate space), which is exactly the clamp the\n// centered zoom wants at the document's inline edges.\nfunction setViewportPhysicalScrollLeft(\n  viewportElement: HTMLDivElement,\n  targetLeft: number,\n) {\n  if (\n    !Number.isFinite(targetLeft) ||\n    Math.abs(viewportElement.scrollLeft - targetLeft) <=\n      VIEWER_DOCUMENT_SCROLL_POSITION_EPSILON\n  ) {\n    return;\n  }\n\n  viewportElement.scrollLeft = targetLeft;\n}\n\nfunction readViewerDocumentNow() {\n  return typeof performance !== \"undefined\" &&\n    typeof performance.now === \"function\"\n    ? performance.now()\n    : Date.now();\n}\n",
      "type": "registry:ui",
      "target": "@ui/viewer-document-scroll.ts"
    },
    {
      "path": "registry/new-york-v4/ui/viewer-document-geometry.ts",
      "content": "import * as React from \"react\";\n\nimport type {\n  ViewerDocumentLayoutModel,\n  ViewerDocumentTransition,\n} from \"./viewer-types\";\n\nconst VIEWER_DOCUMENT_FLIP_LAYER_SELECTOR = \"[data-viewer-document-flip-layer]\";\nconst VIEWER_DOCUMENT_FLIP_TRANSITION_MS = 150;\nconst VIEWER_DOCUMENT_FLIP_TRANSITION_CLEANUP_MS =\n  VIEWER_DOCUMENT_FLIP_TRANSITION_MS + 50;\nconst VIEWER_DOCUMENT_FLIP_EPSILON = 0.5;\nconst VIEWER_DOCUMENT_LAYOUT_EPSILON = 0.5;\n\ntype ViewerDocumentGeometryLayoutChange =\n  | \"geometry\"\n  | \"metadata\"\n  | \"reset\"\n  | \"same\";\n\ntype ViewerDocumentTransitionAnchor<Anchor> = {\n  anchor: Anchor;\n  transitionId: ViewerDocumentTransition[\"transitionId\"];\n  viewportBlockSize: number;\n};\n\ntype ViewerDocumentReadingAnchor<Anchor> = {\n  anchor: Anchor;\n  resetKey: unknown;\n  viewportBlockSize: number;\n};\n\ntype ViewerDocumentFlipAnimation = {\n  element: HTMLElement;\n  frame: number;\n  timeout: ReturnType<typeof setTimeout>;\n};\n\nexport type ViewerDocumentGeometryTransaction<Anchor> = {\n  anchor: Anchor;\n  previousVisualLayerRect: DOMRectReadOnly | null;\n  shouldAnimate: boolean;\n  shouldRestoreScroll: boolean;\n  viewportBlockSize: number;\n};\n\nexport function useViewerDocumentGeometryTransaction<Anchor>({\n  layout,\n  resetKey,\n}: {\n  layout: ViewerDocumentLayoutModel<Anchor>;\n  resetKey: unknown;\n}) {\n  const transitionAnchorRef =\n    React.useRef<ViewerDocumentTransitionAnchor<Anchor> | null>(null);\n  const readingAnchorRef =\n    React.useRef<ViewerDocumentReadingAnchor<Anchor> | null>(null);\n  const visualLayerRectRef = React.useRef<DOMRectReadOnly | null>(null);\n  const flipAnimationRef = React.useRef<ViewerDocumentFlipAnimation | null>(\n    null,\n  );\n\n  const cancel = React.useCallback(() => {\n    cancelViewerDocumentFlipAnimation(flipAnimationRef.current);\n    flipAnimationRef.current = null;\n  }, []);\n\n  const cacheVisualLayerRect = React.useCallback(\n    (viewportElement: HTMLDivElement) => {\n      if (flipAnimationRef.current) return;\n      visualLayerRectRef.current =\n        readViewerDocumentVisualLayerRect(viewportElement);\n    },\n    [],\n  );\n\n  const cacheReadingAnchor = React.useCallback(\n    ({\n      scrollTop,\n      viewportBlockSize,\n    }: {\n      scrollTop: number;\n      viewportBlockSize: number;\n    }) => {\n      const anchor = layout.captureReadingAnchor({\n        scrollTop,\n        viewportBlockSize,\n      });\n      readingAnchorRef.current = anchor\n        ? {\n            anchor,\n            resetKey,\n            viewportBlockSize,\n          }\n        : null;\n      return anchor;\n    },\n    [layout, resetKey],\n  );\n\n  const classifyLayoutChange = React.useCallback(\n    ({\n      previousLayout,\n      previousResetKey,\n    }: {\n      previousLayout: ViewerDocumentLayoutModel<Anchor>;\n      previousResetKey: unknown;\n    }): ViewerDocumentGeometryLayoutChange => {\n      if (!Object.is(previousResetKey, resetKey)) return \"reset\";\n      if (Object.is(previousLayout, layout)) return \"same\";\n      if (!hasViewerDocumentLayoutGeometryChange(previousLayout, layout)) {\n        return \"metadata\";\n      }\n      return \"geometry\";\n    },\n    [layout, resetKey],\n  );\n\n  const reset = React.useCallback(\n    (viewportElement: HTMLDivElement) => {\n      transitionAnchorRef.current = null;\n      readingAnchorRef.current = null;\n      cancel();\n      cacheVisualLayerRect(viewportElement);\n    },\n    [cacheVisualLayerRect, cancel],\n  );\n\n  const recordStableState = React.useCallback(\n    ({\n      clearTransition = false,\n      scrollTop,\n      viewportBlockSize,\n      viewportElement,\n    }: {\n      clearTransition?: boolean;\n      scrollTop: number;\n      viewportBlockSize: number;\n      viewportElement: HTMLDivElement;\n    }) => {\n      if (clearTransition) transitionAnchorRef.current = null;\n      const anchor = cacheReadingAnchor({ scrollTop, viewportBlockSize });\n      if (anchor && shouldRetainViewerDocumentTransitionAnchor(layout)) {\n        const transition = getViewerDocumentTransition(layout);\n        transitionAnchorRef.current = {\n          anchor,\n          transitionId: transition.transitionId,\n          viewportBlockSize,\n        };\n      }\n      cacheVisualLayerRect(viewportElement);\n    },\n    [cacheReadingAnchor, cacheVisualLayerRect, layout],\n  );\n\n  const prepare = React.useCallback(\n    ({\n      previousLayout,\n      scrollTop,\n      viewportBlockSize,\n    }: {\n      previousLayout: ViewerDocumentLayoutModel<Anchor>;\n      scrollTop: number;\n      viewportBlockSize: number;\n    }): ViewerDocumentGeometryTransaction<Anchor> | null => {\n      const isLayoutTransition = isViewerDocumentLayoutTransition({\n        layout,\n        previousLayout,\n      });\n      const transition = resolveViewerDocumentTransition({\n        layout,\n        previousLayout,\n      });\n      const previousTransitionAnchor =\n        getReusableViewerDocumentTransitionAnchor({\n          anchor: transitionAnchorRef.current,\n          transition,\n        });\n      const cachedReadingAnchor = Object.is(\n        readingAnchorRef.current?.resetKey,\n        resetKey,\n      )\n        ? readingAnchorRef.current\n        : null;\n      const shouldUseCachedShellTransitionAnchor =\n        isLayoutTransition &&\n        transition.source === \"viewer-shell\" &&\n        transition.layoutPolicy === \"target\" &&\n        Boolean(cachedReadingAnchor);\n      const shouldCaptureLiveShellTransitionAnchor =\n        isLayoutTransition &&\n        transition.source === \"viewer-shell\" &&\n        !previousTransitionAnchor &&\n        !shouldUseCachedShellTransitionAnchor;\n      const liveReadingAnchor = shouldCaptureLiveShellTransitionAnchor\n        ? previousLayout.captureReadingAnchor({\n            scrollTop,\n            viewportBlockSize,\n          })\n        : null;\n      const anchor =\n        isLayoutTransition && previousTransitionAnchor\n          ? previousTransitionAnchor.anchor\n          : liveReadingAnchor\n            ? liveReadingAnchor\n            : isLayoutTransition && cachedReadingAnchor\n              ? cachedReadingAnchor.anchor\n              : previousLayout.captureReadingAnchor({\n                  scrollTop,\n                  viewportBlockSize,\n                });\n      if (!anchor) return null;\n\n      const transactionViewportBlockSize =\n        previousTransitionAnchor?.viewportBlockSize ??\n        (liveReadingAnchor ? viewportBlockSize : null) ??\n        cachedReadingAnchor?.viewportBlockSize ??\n        viewportBlockSize;\n\n      if (shouldRetainViewerDocumentTransitionAnchor(layout)) {\n        transitionAnchorRef.current = {\n          anchor,\n          transitionId: transition.transitionId,\n          viewportBlockSize: transactionViewportBlockSize,\n        };\n      } else {\n        transitionAnchorRef.current = null;\n      }\n\n      return {\n        anchor,\n        previousVisualLayerRect: visualLayerRectRef.current,\n        shouldAnimate:\n          isLayoutTransition && transition.visualPolicy === \"document-flip\",\n        // Every remaining scroll policy restores (rebase) or preserves through\n        // an explicit restore target; nothing defers anymore.\n        shouldRestoreScroll: true,\n        viewportBlockSize: transactionViewportBlockSize,\n      };\n    },\n    [layout, resetKey],\n  );\n\n  const cacheAnchor = React.useCallback(\n    ({\n      anchor,\n      viewportBlockSize,\n    }: {\n      anchor: Anchor;\n      viewportBlockSize: number;\n    }) => {\n      readingAnchorRef.current = {\n        anchor,\n        resetKey,\n        viewportBlockSize,\n      };\n    },\n    [resetKey],\n  );\n\n  const commit = React.useCallback(\n    ({\n      transaction,\n      viewportElement,\n    }: {\n      transaction: ViewerDocumentGeometryTransaction<Anchor>;\n      viewportElement: HTMLDivElement;\n    }) => {\n      visualLayerRectRef.current = playViewerDocumentFlipAnimation({\n        animationRef: flipAnimationRef,\n        enabled: transaction.shouldAnimate,\n        previousRect: transaction.previousVisualLayerRect,\n        viewportElement,\n      });\n    },\n    [],\n  );\n\n  return React.useMemo(\n    () => ({\n      cacheAnchor,\n      cacheReadingAnchor,\n      cacheVisualLayerRect,\n      cancel,\n      classifyLayoutChange,\n      commit,\n      prepare,\n      recordStableState,\n      reset,\n    }),\n    [\n      cacheAnchor,\n      cacheReadingAnchor,\n      cacheVisualLayerRect,\n      cancel,\n      classifyLayoutChange,\n      commit,\n      prepare,\n      recordStableState,\n      reset,\n    ],\n  );\n}\n\nconst VIEWER_DOCUMENT_STABLE_TRANSITION: ViewerDocumentTransition = {\n  layoutPolicy: \"live\",\n  scrollPolicy: \"preserve\",\n  source: \"none\",\n  transitionId: null,\n  visualPolicy: \"none\",\n};\n\nfunction getViewerDocumentTransition<Anchor>(\n  layout: ViewerDocumentLayoutModel<Anchor>,\n) {\n  return layout.transition ?? VIEWER_DOCUMENT_STABLE_TRANSITION;\n}\n\nfunction isViewerDocumentLayoutTransition<Anchor>({\n  layout,\n  previousLayout,\n}: {\n  layout: ViewerDocumentLayoutModel<Anchor>;\n  previousLayout: ViewerDocumentLayoutModel<Anchor>;\n}) {\n  return (\n    previousLayout.isTransitioning === true ||\n    layout.isTransitioning === true ||\n    getViewerDocumentTransition(previousLayout).source !== \"none\" ||\n    getViewerDocumentTransition(layout).source !== \"none\"\n  );\n}\n\nfunction resolveViewerDocumentTransition<Anchor>({\n  layout,\n  previousLayout,\n}: {\n  layout: ViewerDocumentLayoutModel<Anchor>;\n  previousLayout: ViewerDocumentLayoutModel<Anchor>;\n}) {\n  const transition = getViewerDocumentTransition(layout);\n  if (transition.source !== \"none\") return transition;\n\n  const previousTransition = getViewerDocumentTransition(previousLayout);\n  if (previousTransition.source !== \"none\") return previousTransition;\n\n  if (\n    previousLayout.isTransitioning === true ||\n    layout.isTransitioning === true\n  ) {\n    return {\n      layoutPolicy: \"live\",\n      scrollPolicy: \"preserve\",\n      source: \"document-layout\",\n      transitionId: null,\n      visualPolicy: \"document-flip\",\n    } satisfies ViewerDocumentTransition;\n  }\n\n  return VIEWER_DOCUMENT_STABLE_TRANSITION;\n}\n\nfunction shouldRetainViewerDocumentTransitionAnchor<Anchor>(\n  layout: ViewerDocumentLayoutModel<Anchor>,\n) {\n  const transition = getViewerDocumentTransition(layout);\n  return layout.isTransitioning === true;\n}\n\nfunction getReusableViewerDocumentTransitionAnchor<Anchor>({\n  anchor,\n  transition,\n}: {\n  anchor: ViewerDocumentTransitionAnchor<Anchor> | null;\n  transition: ViewerDocumentTransition;\n}) {\n  if (!anchor) return null;\n  if (transition.source === \"viewer-shell\") {\n    return anchor.transitionId !== null &&\n      transition.transitionId !== null &&\n      Object.is(anchor.transitionId, transition.transitionId)\n      ? anchor\n      : null;\n  }\n  if (anchor.transitionId === null || transition.transitionId === null) {\n    return anchor;\n  }\n  return Object.is(anchor.transitionId, transition.transitionId)\n    ? anchor\n    : null;\n}\n\nfunction playViewerDocumentFlipAnimation({\n  animationRef,\n  enabled,\n  previousRect,\n  viewportElement,\n}: {\n  animationRef: React.MutableRefObject<ViewerDocumentFlipAnimation | null>;\n  enabled: boolean;\n  previousRect: DOMRectReadOnly | null;\n  viewportElement: HTMLDivElement;\n}) {\n  const visualLayer = findViewerDocumentVisualLayer(viewportElement);\n  const currentRect = readElementRect(visualLayer);\n  if (!currentRect) return null;\n\n  if (\n    !visualLayer ||\n    !enabled ||\n    !previousRect ||\n    typeof requestAnimationFrame !== \"function\"\n  ) {\n    return currentRect;\n  }\n\n  const transform = getViewerDocumentFlipTransform({\n    currentRect,\n    previousRect,\n  });\n  if (!transform) return currentRect;\n\n  cancelViewerDocumentFlipAnimation(animationRef.current);\n\n  visualLayer.style.transition = \"none\";\n  visualLayer.style.transformOrigin = \"top left\";\n  visualLayer.style.transform = transform;\n  visualLayer.style.willChange = \"transform\";\n\n  const frame = requestAnimationFrame(() => {\n    visualLayer.style.transition = `transform ${VIEWER_DOCUMENT_FLIP_TRANSITION_MS}ms linear`;\n    visualLayer.style.transform = \"translate3d(0px, 0px, 0px) scale(1, 1)\";\n  });\n  const timeout = setTimeout(() => {\n    if (animationRef.current?.element !== visualLayer) return;\n    finishViewerDocumentFlipAnimation(visualLayer);\n    animationRef.current = null;\n  }, VIEWER_DOCUMENT_FLIP_TRANSITION_CLEANUP_MS);\n\n  animationRef.current = {\n    element: visualLayer,\n    frame,\n    timeout,\n  };\n  return currentRect;\n}\n\nfunction cancelViewerDocumentFlipAnimation(\n  animation: ViewerDocumentFlipAnimation | null,\n) {\n  if (!animation) return;\n  if (typeof cancelAnimationFrame === \"function\") {\n    cancelAnimationFrame(animation.frame);\n  }\n  clearTimeout(animation.timeout);\n  finishViewerDocumentFlipAnimation(animation.element);\n}\n\nfunction finishViewerDocumentFlipAnimation(element: HTMLElement) {\n  element.style.transition = \"\";\n  element.style.transform = \"\";\n  element.style.transformOrigin = \"\";\n  element.style.willChange = \"\";\n}\n\nfunction getViewerDocumentFlipTransform({\n  currentRect,\n  previousRect,\n}: {\n  currentRect: DOMRectReadOnly;\n  previousRect: DOMRectReadOnly;\n}) {\n  if (\n    previousRect.width <= 0 ||\n    previousRect.height <= 0 ||\n    currentRect.width <= 0 ||\n    currentRect.height <= 0\n  ) {\n    return null;\n  }\n\n  const translateX = previousRect.left - currentRect.left;\n  const translateY = previousRect.top - currentRect.top;\n  const scaleX = previousRect.width / currentRect.width;\n  const scaleY = previousRect.height / currentRect.height;\n  const hasVisibleDelta =\n    Math.abs(translateX) > VIEWER_DOCUMENT_FLIP_EPSILON ||\n    Math.abs(translateY) > VIEWER_DOCUMENT_FLIP_EPSILON ||\n    Math.abs(1 - scaleX) > 0.001 ||\n    Math.abs(1 - scaleY) > 0.001;\n\n  if (!hasVisibleDelta) return null;\n\n  return `translate3d(${translateX}px, ${translateY}px, 0px) scale(${scaleX}, ${scaleY})`;\n}\n\nfunction hasViewerDocumentLayoutGeometryChange<Anchor>(\n  previousLayout: ViewerDocumentLayoutModel<Anchor>,\n  nextLayout: ViewerDocumentLayoutModel<Anchor>,\n) {\n  return (\n    !areViewerDocumentLayoutSizesEqual(\n      previousLayout.inlineSize,\n      nextLayout.inlineSize,\n    ) ||\n    !areViewerDocumentLayoutSizesEqual(\n      previousLayout.blockSize,\n      nextLayout.blockSize,\n    )\n  );\n}\n\nfunction areViewerDocumentLayoutSizesEqual(previous: number, next: number) {\n  return Math.abs(previous - next) < VIEWER_DOCUMENT_LAYOUT_EPSILON;\n}\n\nfunction readViewerDocumentVisualLayerRect(\n  viewportElement: HTMLDivElement | null,\n) {\n  return readElementRect(findViewerDocumentVisualLayer(viewportElement));\n}\n\nfunction findViewerDocumentVisualLayer(viewportElement: HTMLDivElement | null) {\n  if (!viewportElement || typeof viewportElement.querySelector !== \"function\") {\n    return null;\n  }\n\n  return viewportElement.querySelector<HTMLElement>(\n    VIEWER_DOCUMENT_FLIP_LAYER_SELECTOR,\n  );\n}\n\nfunction readElementRect(element: HTMLElement | null | undefined) {\n  if (!element || typeof element.getBoundingClientRect !== \"function\") {\n    return null;\n  }\n\n  const rect = element.getBoundingClientRect();\n  return rect.width > 0 && rect.height > 0 ? rect : null;\n}\n",
      "type": "registry:ui",
      "target": "@ui/viewer-document-geometry.ts"
    },
    {
      "path": "registry/new-york-v4/ui/pdf-viewer-layout.ts",
      "content": "import type { PdfPageSize } from \"./pdf-viewer-types\";\n\n// The inter-page gap scales with the document so zoom behaves like a camera.\n// The outer padding stays fixed because it is the loaded page's shared frame\n// contract with the p-4 fallback skeleton.\nexport const PDF_PAGE_GAP = 16;\nexport const PDF_PAGE_PADDING = 16;\nexport const PDF_RENDER_FIT_PERFECTLY_OVERSCAN_PX =\n  PDF_PAGE_GAP + PDF_PAGE_PADDING;\nexport const PDF_RENDER_WINDOW_OVERSCAN_PX = 1000;\nexport const PDF_VISIBLE_PAGE_OVERSCAN = 2;\nexport const PDF_PRELOAD_PAGE_OVERSCAN = 4;\nexport const PDF_SCROLL_REBASE_CONTAINER_PX = 12_000_000;\nexport const PDF_SCROLL_REBASE_TRIGGER_PX = 1_000_000;\nexport const PDF_SCROLL_REBASE_TARGET_PX = 2_000_000;\nexport const PDF_SCROLL_REBASE_TARGET_BOTTOM_PX =\n  PDF_SCROLL_REBASE_CONTAINER_PX - PDF_SCROLL_REBASE_TARGET_PX;\nexport const PDF_SCROLL_REBASE_THRESHOLD_PX =\n  PDF_SCROLL_REBASE_CONTAINER_PX - PDF_SCROLL_REBASE_TRIGGER_PX;\n\nexport type PdfPageLayout = {\n  pageNumber: number;\n  width: number;\n  height: number;\n  offsetTop: number;\n};\n\nexport type PdfRenderedPageLayout = PdfPageLayout & {\n  windowTop: number;\n};\n\nexport type PdfRenderedPageWindow = {\n  afterHeight: number;\n  beforeHeight: number;\n  height: number;\n  pages: readonly PdfRenderedPageLayout[];\n  stickyInset: number;\n};\n\nexport type PdfRenderPixelWindow = {\n  bottom: number;\n  top: number;\n};\n\nexport type PdfScrollRebasePosition = {\n  physicalScrollTop: number;\n  scrollPageOffset: number;\n};\n\nexport type PdfMeasuredPageLayout = {\n  pageNumber: number;\n  width: number;\n  height: number;\n  heightDelta: number;\n};\n\nexport type PdfPageLayoutModel = {\n  pageCount: number;\n  totalHeight: number;\n  maxPageWidth: number;\n  estimatedWidth: number;\n  estimatedHeight: number;\n  // Resolved (scaled) vertical spacing, so offset math reads the model rather\n  // than re-deriving the scale.\n  gap: number;\n  padding: number;\n  measuredPages: readonly PdfMeasuredPageLayout[];\n  measuredPageByNumber: ReadonlyMap<number, PdfMeasuredPageLayout>;\n  prefixHeightDeltas: readonly number[];\n};\n\nexport function createPdfPageLayout({\n  pageCount,\n  defaultPageSize,\n  pageSizeByNumber,\n  scale,\n  rotation,\n}: {\n  pageCount: number;\n  defaultPageSize: PdfPageSize;\n  pageSizeByNumber: ReadonlyMap<number, PdfPageSize>;\n  scale: number;\n  rotation: number;\n}): PdfPageLayoutModel {\n  // Rounded to whole pixels like the page dimensions, so offsets stay\n  // integer. The sub-pixel rounding residual is absorbed by the model-based\n  // reading anchor (which pins the transform to the visible page's actual\n  // offset), exactly as page-height rounding already is.\n  const gap = Math.round(PDF_PAGE_GAP * scale);\n  const padding = PDF_PAGE_PADDING;\n  const estimatedSize = getRenderedPageSize(defaultPageSize, scale, rotation);\n  const measuredPages = Array.from(pageSizeByNumber.entries())\n    .filter(([pageNumber]) => pageNumber >= 1 && pageNumber <= pageCount)\n    .map(([pageNumber, pageSize]) => {\n      const renderedSize = getRenderedPageSize(pageSize, scale, rotation);\n      return {\n        pageNumber,\n        width: renderedSize.width,\n        height: renderedSize.height,\n        heightDelta: renderedSize.height - estimatedSize.height,\n      };\n    })\n    .filter(\n      (page) =>\n        page.width !== estimatedSize.width ||\n        page.height !== estimatedSize.height,\n    )\n    .sort((a, b) => a.pageNumber - b.pageNumber);\n\n  const measuredPageByNumber = new Map(\n    measuredPages.map((page) => [page.pageNumber, page]),\n  );\n  const prefixHeightDeltas: number[] = [];\n  let totalHeightDelta = 0;\n  let maxPageWidth = pageCount === 0 ? 0 : estimatedSize.width;\n\n  for (const page of measuredPages) {\n    totalHeightDelta += page.heightDelta;\n    prefixHeightDeltas.push(totalHeightDelta);\n    maxPageWidth = Math.max(maxPageWidth, page.width);\n  }\n\n  return {\n    pageCount,\n    totalHeight:\n      pageCount === 0\n        ? 0\n        : padding * 2 +\n          pageCount * estimatedSize.height +\n          (pageCount - 1) * gap +\n          totalHeightDelta,\n    maxPageWidth,\n    estimatedWidth: estimatedSize.width,\n    estimatedHeight: estimatedSize.height,\n    gap,\n    padding,\n    measuredPages,\n    measuredPageByNumber,\n    prefixHeightDeltas,\n  };\n}\n\nexport function getPdfPageLayout(\n  layout: PdfPageLayoutModel,\n  pageNumber: number,\n): PdfPageLayout | undefined {\n  if (pageNumber < 1 || pageNumber > layout.pageCount) return undefined;\n\n  const measuredPage = layout.measuredPageByNumber.get(pageNumber);\n  return {\n    pageNumber,\n    width: measuredPage?.width ?? layout.estimatedWidth,\n    height: measuredPage?.height ?? layout.estimatedHeight,\n    offsetTop: getPdfPageOffsetTop(layout, pageNumber),\n  };\n}\n\nexport function findPdfPageByOffset(\n  layout: PdfPageLayoutModel,\n  offset: number,\n) {\n  if (layout.pageCount === 0) return 1;\n\n  let low = 1;\n  let high = layout.pageCount;\n  let match = 1;\n\n  while (low <= high) {\n    const mid = Math.floor((low + high) / 2);\n    if (getPdfPageOffsetTop(layout, mid) <= offset) {\n      match = mid;\n      low = mid + 1;\n    } else {\n      high = mid - 1;\n    }\n  }\n\n  return match;\n}\n\nexport function getPdfVisiblePageNumbers({\n  layout,\n  scrollTop,\n  viewportHeight,\n  overscanPages = PDF_VISIBLE_PAGE_OVERSCAN,\n}: {\n  layout: PdfPageLayoutModel;\n  scrollTop: number;\n  viewportHeight: number;\n  overscanPages?: number;\n}) {\n  if (layout.pageCount === 0) return [];\n\n  return getPdfPageNumbersInRange({\n    layout,\n    startOffset: Math.max(0, scrollTop - viewportHeight),\n    endOffset: scrollTop + viewportHeight * 2,\n    overscanPages,\n  });\n}\n\nexport function getPdfRenderPageNumbers({\n  fitPerfectly = false,\n  fitPerfectlyOverscanPx = PDF_RENDER_FIT_PERFECTLY_OVERSCAN_PX,\n  layout,\n  overscanPx = PDF_RENDER_WINDOW_OVERSCAN_PX,\n  scrollTop,\n  viewportHeight,\n}: {\n  fitPerfectly?: boolean;\n  fitPerfectlyOverscanPx?: number;\n  layout: PdfPageLayoutModel;\n  overscanPx?: number;\n  scrollTop: number;\n  viewportHeight: number;\n}) {\n  if (layout.pageCount === 0) return [];\n\n  const window = createWindowFromScrollPosition({\n    fitPerfectly,\n    fitPerfectlyOverscanPx,\n    overscanPx,\n    scrollHeight: layout.totalHeight,\n    scrollTop,\n    viewportHeight,\n  });\n\n  return getPdfPageNumbersInRange({\n    layout,\n    startOffset: window.top,\n    endOffset: window.bottom,\n    overscanPages: 0,\n  });\n}\n\nexport function getPdfPreloadPageNumbers({\n  layout,\n  renderPageNumbers,\n  overscanPages = PDF_PRELOAD_PAGE_OVERSCAN,\n}: {\n  layout: PdfPageLayoutModel;\n  renderPageNumbers: readonly number[];\n  overscanPages?: number;\n}) {\n  if (layout.pageCount === 0 || renderPageNumbers.length === 0) return [];\n\n  const firstRenderPage = renderPageNumbers[0];\n  const lastRenderPage = renderPageNumbers[renderPageNumbers.length - 1];\n  const firstPage = Math.max(1, firstRenderPage - overscanPages);\n  const lastPage = Math.min(layout.pageCount, lastRenderPage + overscanPages);\n\n  return createPageNumberRange(firstPage, lastPage);\n}\n\nexport function getPdfRenderedPageWindow({\n  layout,\n  pageNumbers,\n  physicalScrollHeight = layout.totalHeight,\n  scrollPageOffset = 0,\n  viewportHeight,\n}: {\n  layout: PdfPageLayoutModel;\n  pageNumbers: readonly number[];\n  physicalScrollHeight?: number;\n  scrollPageOffset?: number;\n  viewportHeight: number;\n}): PdfRenderedPageWindow | null {\n  const pages = pageNumbers\n    .map((pageNumber) => getPdfPageLayout(layout, pageNumber))\n    .filter((page): page is PdfPageLayout => Boolean(page))\n    .sort((a, b) => a.pageNumber - b.pageNumber);\n\n  if (pages.length === 0) return null;\n\n  const logicalBeforeHeight = pages[0].offsetTop;\n  const windowBottom = Math.max(\n    ...pages.map((page) => page.offsetTop + page.height),\n  );\n  const height = Math.max(0, windowBottom - logicalBeforeHeight);\n  const beforeHeight = getPdfPagedLayoutTop({\n    logicalTop: logicalBeforeHeight,\n    scrollPageOffset,\n    totalHeight: layout.totalHeight,\n    viewportHeight,\n  });\n  const safeViewportHeight =\n    Number.isFinite(viewportHeight) && viewportHeight > 0 ? viewportHeight : 0;\n  const safePhysicalScrollHeight = Math.max(\n    0,\n    finiteNumber(physicalScrollHeight),\n  );\n  const stickyInset =\n    safeViewportHeight > 0 ? -Math.max(0, height - safeViewportHeight) : 0;\n\n  return {\n    afterHeight: Math.max(0, safePhysicalScrollHeight - beforeHeight - height),\n    beforeHeight,\n    height,\n    pages: pages.map((page) => ({\n      ...page,\n      windowTop: page.offsetTop - logicalBeforeHeight,\n    })),\n    stickyInset,\n  };\n}\n\nexport function createWindowFromScrollPosition({\n  fitPerfectly = false,\n  fitPerfectlyOverscanPx = 0,\n  overscanPx,\n  scrollHeight,\n  scrollTop,\n  viewportHeight,\n}: {\n  fitPerfectly?: boolean;\n  fitPerfectlyOverscanPx?: number;\n  overscanPx: number;\n  scrollHeight: number;\n  scrollTop: number;\n  viewportHeight: number;\n}): PdfRenderPixelWindow {\n  const safeOverscanPx = safePadding(overscanPx);\n  const safeScrollHeight = Math.max(0, finiteNumber(scrollHeight));\n  const safeScrollTop = Math.max(0, finiteNumber(scrollTop));\n  const safeViewportHeight = Math.max(0, finiteNumber(viewportHeight));\n  const windowHeight = safeViewportHeight + safeOverscanPx * 2;\n  const fitPerfectlyOverscan = safePadding(fitPerfectlyOverscanPx);\n  const effectiveHeight = fitPerfectly\n    ? safeViewportHeight + fitPerfectlyOverscan * 2\n    : windowHeight;\n\n  if (windowHeight >= safeScrollHeight || fitPerfectly) {\n    const fitScrollTop = Math.min(safeScrollTop, safeScrollHeight);\n    const top = Math.max(fitScrollTop - fitPerfectlyOverscan, 0);\n    const bottom = Math.min(fitScrollTop + effectiveHeight, safeScrollHeight);\n    return {\n      bottom: Math.ceil(Math.max(bottom, top)),\n      top: Math.floor(Math.max(0, top)),\n    };\n  }\n\n  const scrollCenter = safeScrollTop + safeViewportHeight / 2;\n  let top = scrollCenter - windowHeight / 2;\n  let bottom = top + windowHeight;\n\n  if (top < 0) {\n    top = 0;\n    bottom = windowHeight;\n  }\n  if (bottom > safeScrollHeight) {\n    bottom = safeScrollHeight;\n    top = safeScrollHeight - windowHeight;\n  }\n\n  return {\n    bottom: Math.ceil(Math.max(bottom, top)),\n    top: Math.floor(Math.max(0, top)),\n  };\n}\n\nexport function getPdfPhysicalScrollHeight({\n  totalHeight,\n  viewportHeight,\n}: {\n  totalHeight: number;\n  viewportHeight: number;\n}) {\n  const safeTotalHeight = safeSize(totalHeight);\n  return shouldRebasePdfScroll({ totalHeight: safeTotalHeight, viewportHeight })\n    ? Math.min(safeTotalHeight, PDF_SCROLL_REBASE_CONTAINER_PX)\n    : safeTotalHeight;\n}\n\nexport function getPdfLogicalScrollTop({\n  physicalScrollTop,\n  scrollPageOffset,\n  totalHeight,\n  viewportHeight,\n}: {\n  physicalScrollTop: number;\n  scrollPageOffset: number;\n  totalHeight: number;\n  viewportHeight: number;\n}) {\n  return clamp(\n    finiteNumber(physicalScrollTop) + safePadding(scrollPageOffset),\n    0,\n    getPdfMaxLogicalScrollTop({ totalHeight, viewportHeight }),\n  );\n}\n\nexport function resolvePdfPhysicalScrollPosition({\n  logicalScrollTop,\n  scrollPageOffset,\n  totalHeight,\n  viewportHeight,\n}: {\n  logicalScrollTop: number;\n  scrollPageOffset: number;\n  totalHeight: number;\n  viewportHeight: number;\n}): PdfScrollRebasePosition {\n  const safeLogicalScrollTop = clamp(\n    finiteNumber(logicalScrollTop),\n    0,\n    getPdfMaxLogicalScrollTop({ totalHeight, viewportHeight }),\n  );\n\n  if (!shouldRebasePdfScroll({ totalHeight, viewportHeight })) {\n    return {\n      physicalScrollTop: clamp(\n        safeLogicalScrollTop,\n        0,\n        getPdfMaxPhysicalScrollTop({ totalHeight, viewportHeight }),\n      ),\n      scrollPageOffset: 0,\n    };\n  }\n\n  const currentPageOffset = clampPdfScrollPageOffset({\n    scrollPageOffset,\n    totalHeight,\n    viewportHeight,\n  });\n  const physicalScrollTop = safeLogicalScrollTop - currentPageOffset;\n  const maxPhysicalScrollTop = getPdfMaxPhysicalScrollTop({\n    totalHeight,\n    viewportHeight,\n  });\n  const maxPageOffset = getPdfMaxScrollPageOffset({\n    totalHeight,\n    viewportHeight,\n  });\n  const shouldMoveDown =\n    physicalScrollTop > PDF_SCROLL_REBASE_THRESHOLD_PX &&\n    currentPageOffset < maxPageOffset;\n  const shouldMoveUp =\n    physicalScrollTop < PDF_SCROLL_REBASE_TRIGGER_PX && currentPageOffset > 0;\n\n  if (\n    physicalScrollTop < 0 ||\n    physicalScrollTop > maxPhysicalScrollTop ||\n    shouldMoveDown ||\n    shouldMoveUp\n  ) {\n    return resolvePdfScrollPageWindow({\n      logicalScrollTop: safeLogicalScrollTop,\n      preferredPhysicalScrollTop: shouldMoveUp\n        ? Math.min(PDF_SCROLL_REBASE_TARGET_BOTTOM_PX, maxPhysicalScrollTop)\n        : PDF_SCROLL_REBASE_TARGET_PX,\n      totalHeight,\n      viewportHeight,\n    });\n  }\n\n  return {\n    physicalScrollTop: roundPdfScrollPixel(\n      clamp(physicalScrollTop, 0, maxPhysicalScrollTop),\n    ),\n    scrollPageOffset: currentPageOffset,\n  };\n}\n\nexport function getPdfPagedLayoutTop({\n  logicalTop,\n  scrollPageOffset,\n  totalHeight,\n  viewportHeight,\n}: {\n  logicalTop: number;\n  scrollPageOffset: number;\n  totalHeight: number;\n  viewportHeight: number;\n}) {\n  const offset = finiteNumber(scrollPageOffset);\n  if (\n    Math.abs(offset) <= 0.001 &&\n    !shouldRebasePdfScroll({ totalHeight, viewportHeight })\n  ) {\n    return finiteNumber(logicalTop);\n  }\n  return Math.max(0, finiteNumber(logicalTop) - offset);\n}\n\nfunction getPdfPageNumbersInRange({\n  layout,\n  startOffset,\n  endOffset,\n  overscanPages,\n}: {\n  layout: PdfPageLayoutModel;\n  startOffset: number;\n  endOffset: number;\n  overscanPages: number;\n}) {\n  const safeStartOffset = Math.max(0, startOffset);\n  const safeEndOffset = Math.max(safeStartOffset, endOffset);\n  const firstVisiblePage = findPdfPageByOffset(layout, safeStartOffset);\n  const lastVisiblePage = findPdfPageByOffset(layout, safeEndOffset);\n  const firstPage = Math.max(1, firstVisiblePage - overscanPages);\n  const lastPage = Math.min(layout.pageCount, lastVisiblePage + overscanPages);\n\n  return createPageNumberRange(firstPage, lastPage);\n}\n\nfunction shouldRebasePdfScroll({\n  totalHeight,\n  viewportHeight,\n}: {\n  totalHeight: number;\n  viewportHeight: number;\n}) {\n  return (\n    getPdfMaxLogicalScrollTop({ totalHeight, viewportHeight }) >\n    PDF_SCROLL_REBASE_THRESHOLD_PX\n  );\n}\n\nfunction getPdfMaxLogicalScrollTop({\n  totalHeight,\n  viewportHeight,\n}: {\n  totalHeight: number;\n  viewportHeight: number;\n}) {\n  return Math.max(\n    safeSize(totalHeight) - Math.max(0, finiteNumber(viewportHeight)),\n    0,\n  );\n}\n\nfunction getPdfMaxPhysicalScrollTop({\n  totalHeight,\n  viewportHeight,\n}: {\n  totalHeight: number;\n  viewportHeight: number;\n}) {\n  return Math.max(\n    getPdfPhysicalScrollHeight({ totalHeight, viewportHeight }) -\n      Math.max(0, finiteNumber(viewportHeight)),\n    0,\n  );\n}\n\nfunction getPdfMaxScrollPageOffset({\n  totalHeight,\n  viewportHeight,\n}: {\n  totalHeight: number;\n  viewportHeight: number;\n}) {\n  return Math.max(\n    getPdfMaxLogicalScrollTop({ totalHeight, viewportHeight }) -\n      getPdfMaxPhysicalScrollTop({ totalHeight, viewportHeight }),\n    0,\n  );\n}\n\nfunction clampPdfScrollPageOffset({\n  scrollPageOffset,\n  totalHeight,\n  viewportHeight,\n}: {\n  scrollPageOffset: number;\n  totalHeight: number;\n  viewportHeight: number;\n}) {\n  return clamp(\n    safePadding(scrollPageOffset),\n    0,\n    getPdfMaxScrollPageOffset({ totalHeight, viewportHeight }),\n  );\n}\n\nfunction resolvePdfScrollPageWindow({\n  logicalScrollTop,\n  preferredPhysicalScrollTop,\n  totalHeight,\n  viewportHeight,\n}: {\n  logicalScrollTop: number;\n  preferredPhysicalScrollTop: number;\n  totalHeight: number;\n  viewportHeight: number;\n}): PdfScrollRebasePosition {\n  let physicalScrollTop = roundPdfScrollPixel(\n    clamp(\n      finiteNumber(preferredPhysicalScrollTop),\n      0,\n      getPdfMaxPhysicalScrollTop({ totalHeight, viewportHeight }),\n    ),\n  );\n  let scrollPageOffset = clampPdfScrollPageOffset({\n    scrollPageOffset: logicalScrollTop - physicalScrollTop,\n    totalHeight,\n    viewportHeight,\n  });\n\n  physicalScrollTop = roundPdfScrollPixel(\n    clamp(\n      logicalScrollTop - scrollPageOffset,\n      0,\n      getPdfMaxPhysicalScrollTop({ totalHeight, viewportHeight }),\n    ),\n  );\n  scrollPageOffset = clampPdfScrollPageOffset({\n    scrollPageOffset: logicalScrollTop - physicalScrollTop,\n    totalHeight,\n    viewportHeight,\n  });\n\n  return { physicalScrollTop, scrollPageOffset };\n}\n\nfunction createPageNumberRange(firstPage: number, lastPage: number) {\n  if (lastPage < firstPage) return [];\n\n  return Array.from(\n    { length: lastPage - firstPage + 1 },\n    (_, index) => firstPage + index,\n  );\n}\n\nfunction getRenderedPageSize(\n  pageSize: PdfPageSize,\n  scale: number,\n  rotation: number,\n) {\n  const isRotated = rotation % 180 !== 0;\n  return {\n    width: Math.round((isRotated ? pageSize.height : pageSize.width) * scale),\n    height: Math.round((isRotated ? pageSize.width : pageSize.height) * scale),\n  };\n}\n\nfunction finiteNumber(value: number) {\n  return Number.isFinite(value) ? value : 0;\n}\n\nfunction roundPdfScrollPixel(value: number) {\n  if (typeof window === \"undefined\") return Math.round(value);\n  const ratio = window.devicePixelRatio || 1;\n  return Math.round(value * ratio) / ratio;\n}\n\nfunction safePadding(value: number) {\n  return Number.isFinite(value) && value > 0 ? value : 0;\n}\n\nfunction safeSize(value: number) {\n  return Number.isFinite(value) && value > 0 ? value : 0;\n}\n\nfunction clamp(value: number, min: number, max: number) {\n  return Math.min(max, Math.max(min, value));\n}\n\nfunction getPdfPageOffsetTop(layout: PdfPageLayoutModel, pageNumber: number) {\n  return (\n    layout.padding +\n    (pageNumber - 1) * (layout.estimatedHeight + layout.gap) +\n    getHeightDeltaBeforePage(layout, pageNumber)\n  );\n}\n\nfunction getHeightDeltaBeforePage(\n  layout: PdfPageLayoutModel,\n  pageNumber: number,\n) {\n  const measuredPageIndex = getLastMeasuredPageIndexBefore(layout, pageNumber);\n  return measuredPageIndex < 0\n    ? 0\n    : layout.prefixHeightDeltas[measuredPageIndex];\n}\n\nfunction getLastMeasuredPageIndexBefore(\n  layout: PdfPageLayoutModel,\n  pageNumber: number,\n) {\n  let low = 0;\n  let high = layout.measuredPages.length - 1;\n  let match = -1;\n\n  while (low <= high) {\n    const mid = Math.floor((low + high) / 2);\n    if (layout.measuredPages[mid].pageNumber < pageNumber) {\n      match = mid;\n      low = mid + 1;\n    } else {\n      high = mid - 1;\n    }\n  }\n\n  return match;\n}\n",
      "type": "registry:ui",
      "target": "@ui/pdf-viewer-layout.ts"
    },
    {
      "path": "registry/new-york-v4/ui/pdf-viewer-virtualization.ts",
      "content": "import * as React from \"react\";\nimport { useMountEffect } from \"@/hooks/use-mount-effect\";\n\nimport {\n  getPdfPreloadPageNumbers,\n  getPdfRenderPageNumbers,\n  getPdfVisiblePageNumbers,\n  PDF_RENDER_WINDOW_OVERSCAN_PX,\n  type PdfPageLayoutModel,\n} from \"./pdf-viewer-layout\";\nimport type { ViewerDocumentTransition } from \"./viewer-types\";\nimport { useKeyedMountEffect } from \"@/hooks/use-keyed-mount-effect\";\nimport { useKeyedLayoutEffect } from \"@/hooks/use-keyed-layout-effect\";\nimport { joinEffectKey } from \"@/lib/effect-key\";\n\nexport const PDF_PAGE_WINDOW_RETENTION_MS = 250;\n\nconst PDF_SHELL_MOTION_RENDER_WINDOW_VIEWPORT_MULTIPLIER = 2;\nconst PDF_SHELL_MOTION_RENDER_WINDOW_PAGE_OVERSCAN = 3;\n\ntype PdfPageWindow = {\n  scrollPageOffset: number;\n  visiblePageNumbers: readonly number[];\n  renderPageNumbers: readonly number[];\n  warmPageNumbers: readonly number[];\n  preloadPageNumbers: readonly number[];\n};\n\nexport type PdfPageVirtualizationScrollMetrics = {\n  scrollPageOffset: number;\n  scrollTop: number;\n  viewportHeight: number;\n};\n\nexport function usePdfPageVirtualization({\n  getScrollMetrics,\n  isLayoutTransitioning = false,\n  layout,\n  resetKey,\n  shouldHoldPageWindow = false,\n  transition,\n  viewportElement,\n}: {\n  getScrollMetrics?: () => PdfPageVirtualizationScrollMetrics;\n  isLayoutTransitioning?: boolean;\n  layout: PdfPageLayoutModel;\n  resetKey?: unknown;\n  shouldHoldPageWindow?: boolean;\n  transition?: ViewerDocumentTransition;\n  viewportElement: HTMLDivElement | null;\n}) {\n  const measureFrameRef = React.useRef(0);\n  const retentionTimeoutRef = React.useRef<ReturnType<\n    typeof setTimeout\n  > | null>(null);\n  const releasedRetentionLayoutRef = React.useRef<PdfPageLayoutModel | null>(\n    null,\n  );\n  const hasMeasuredScrollRef = React.useRef(false);\n  const lastMeasuredLayoutRef = React.useRef(layout);\n  const lastMeasuredResetKeyRef = React.useRef<unknown>(resetKey);\n  const lastMeasuredScrollTopRef = React.useRef(0);\n  const hasReleasedPageWindowRetention =\n    transition?.source === \"viewer-shell\" &&\n    transition.layoutPolicy === \"target\" &&\n    Object.is(releasedRetentionLayoutRef.current, layout);\n  const shouldRetainPreviousPageWindow =\n    (shouldHoldPageWindow ||\n      shouldRetainPdfPageWindow({\n        isLayoutTransitioning,\n        transition,\n      })) &&\n    (!hasReleasedPageWindowRetention || shouldHoldPageWindow);\n  const renderWindowTransition =\n    transition?.source === \"viewer-shell\" &&\n    (!hasReleasedPageWindowRetention || shouldHoldPageWindow)\n      ? transition\n      : undefined;\n  const shouldFreezePreviousPageWindow =\n    shouldHoldPageWindow || renderWindowTransition?.source === \"viewer-shell\";\n  const getCurrentScrollMetrics =\n    React.useCallback((): PdfPageVirtualizationScrollMetrics => {\n      return Object.is(lastMeasuredResetKeyRef.current, resetKey) &&\n        getScrollMetrics\n        ? getScrollMetrics()\n        : {\n            scrollPageOffset: 0,\n            scrollTop: Object.is(lastMeasuredResetKeyRef.current, resetKey)\n              ? (viewportElement?.scrollTop ?? 0)\n              : 0,\n            viewportHeight: viewportElement?.clientHeight ?? 0,\n          };\n    }, [getScrollMetrics, resetKey, viewportElement]);\n  const getPageWindow = React.useCallback(\n    (\n      metrics: PdfPageVirtualizationScrollMetrics,\n      fitPerfectly = false,\n      overscanPx = getPdfRenderWindowOverscanPx({\n        layout,\n        transition: renderWindowTransition,\n        viewportHeight: metrics.viewportHeight,\n      }),\n    ): PdfPageWindow => {\n      const renderPageNumbers = getPdfRenderPageNumbers({\n        fitPerfectly,\n        layout,\n        overscanPx,\n        scrollTop: metrics.scrollTop,\n        viewportHeight: metrics.viewportHeight,\n      });\n\n      return {\n        scrollPageOffset: metrics.scrollPageOffset,\n        visiblePageNumbers: getPdfVisiblePageNumbers({\n          layout,\n          scrollTop: metrics.scrollTop,\n          viewportHeight: metrics.viewportHeight,\n        }),\n        renderPageNumbers,\n        warmPageNumbers: renderPageNumbers,\n        preloadPageNumbers: getPdfPreloadPageNumbers({\n          layout,\n          renderPageNumbers,\n        }),\n      };\n    },\n    [layout, renderWindowTransition],\n  );\n  const getCurrentVisiblePageNumbers = React.useCallback((): PdfPageWindow => {\n    return getPageWindow(getCurrentScrollMetrics());\n  }, [getCurrentScrollMetrics, getPageWindow]);\n  const getResetPageWindow = React.useCallback(() => {\n    const viewportHeight = viewportElement?.clientHeight ?? 0;\n    const renderPageNumbers = getPdfRenderPageNumbers({\n      layout,\n      scrollTop: 0,\n      viewportHeight,\n    });\n\n    return {\n      scrollPageOffset: 0,\n      visiblePageNumbers: getPdfVisiblePageNumbers({\n        layout,\n        scrollTop: 0,\n        viewportHeight,\n      }),\n      renderPageNumbers,\n      warmPageNumbers: renderPageNumbers,\n      preloadPageNumbers: getPdfPreloadPageNumbers({\n        layout,\n        renderPageNumbers,\n      }),\n    };\n  }, [layout, viewportElement]);\n  const [state, setState] = React.useState<{\n    isTransitionPageWindow: boolean;\n    layout: PdfPageLayoutModel;\n    resetKey: unknown;\n    pageWindow: PdfPageWindow;\n  }>(() => ({\n    isTransitionPageWindow: false,\n    layout,\n    resetKey,\n    pageWindow: getCurrentVisiblePageNumbers(),\n  }));\n  const pageWindow =\n    Object.is(state.layout, layout) && Object.is(state.resetKey, resetKey)\n      ? state.isTransitionPageWindow && !shouldRetainPreviousPageWindow\n        ? getCurrentVisiblePageNumbers()\n        : state.pageWindow\n      : Object.is(state.resetKey, resetKey)\n        ? createTransitionPageWindow({\n            currentPageWindow: getCurrentVisiblePageNumbers(),\n            layout,\n            previousPageWindow: state.pageWindow,\n            shouldFreezePreviousPageWindow,\n            shouldRetainPreviousPageWindow,\n          })\n        : getResetPageWindow();\n  const cancelPageWindowRetentionRelease = React.useCallback(() => {\n    if (retentionTimeoutRef.current === null) return;\n    clearTimeout(retentionTimeoutRef.current);\n    retentionTimeoutRef.current = null;\n  }, []);\n  const schedulePageWindowRetentionRelease = React.useCallback(() => {\n    cancelPageWindowRetentionRelease();\n    retentionTimeoutRef.current = setTimeout(() => {\n      retentionTimeoutRef.current = null;\n      const metrics = getCurrentScrollMetrics();\n      const currentPageWindow = getPageWindow(\n        metrics,\n        false,\n        PDF_RENDER_WINDOW_OVERSCAN_PX,\n      );\n      releasedRetentionLayoutRef.current = layout;\n      lastMeasuredLayoutRef.current = layout;\n      lastMeasuredResetKeyRef.current = resetKey;\n      lastMeasuredScrollTopRef.current = metrics.scrollTop;\n      hasMeasuredScrollRef.current = true;\n      setState((previousState) => {\n        if (!Object.is(previousState.resetKey, resetKey)) {\n          return previousState;\n        }\n\n        return Object.is(previousState.layout, layout) &&\n          !previousState.isTransitionPageWindow &&\n          arePageWindowsEqual(previousState.pageWindow, currentPageWindow)\n          ? previousState\n          : {\n              isTransitionPageWindow: false,\n              layout,\n              resetKey,\n              pageWindow: currentPageWindow,\n            };\n      });\n    }, PDF_PAGE_WINDOW_RETENTION_MS);\n  }, [\n    cancelPageWindowRetentionRelease,\n    getCurrentScrollMetrics,\n    getPageWindow,\n    layout,\n    resetKey,\n  ]);\n\n  const measureVisiblePagesNow = React.useCallback(() => {\n    measureFrameRef.current = 0;\n    const metrics = getCurrentScrollMetrics();\n    const renderOverscanPx = getPdfRenderWindowOverscanPx({\n      layout,\n      transition: renderWindowTransition,\n      viewportHeight: metrics.viewportHeight,\n    });\n    const fitPerfectly = shouldFitPdfPerfectly({\n      canFitPerfectly:\n        hasMeasuredScrollRef.current &&\n        Object.is(lastMeasuredLayoutRef.current, layout) &&\n        Object.is(lastMeasuredResetKeyRef.current, resetKey),\n      previousScrollTop: lastMeasuredScrollTopRef.current,\n      renderOverscanPx,\n      scrollTop: metrics.scrollTop,\n      viewportHeight: metrics.viewportHeight,\n    });\n    const currentPageWindow = getPageWindow(metrics, fitPerfectly);\n    const hasScrollMovedSinceLastMeasurement =\n      Math.abs(metrics.scrollTop - lastMeasuredScrollTopRef.current) > 1;\n    const shouldBreakRetentionForScroll =\n      hasScrollMovedSinceLastMeasurement &&\n      shouldHoldPageWindow &&\n      !isLayoutTransitioning &&\n      renderWindowTransition?.source !== \"viewer-shell\";\n    const shouldRetainMeasuredPageWindow =\n      shouldRetainPreviousPageWindow && !shouldBreakRetentionForScroll;\n    const shouldFreezeMeasuredPageWindow =\n      shouldFreezePreviousPageWindow && !shouldBreakRetentionForScroll;\n    lastMeasuredLayoutRef.current = layout;\n    lastMeasuredResetKeyRef.current = resetKey;\n    lastMeasuredScrollTopRef.current = metrics.scrollTop;\n    hasMeasuredScrollRef.current = true;\n    setState((previousState) => {\n      const nextPageWindow = createTransitionPageWindow({\n        currentPageWindow,\n        layout,\n        previousPageWindow: previousState.pageWindow,\n        shouldFreezePreviousPageWindow: shouldFreezeMeasuredPageWindow,\n        shouldRetainPreviousPageWindow: shouldRetainMeasuredPageWindow,\n      });\n      const isTransitionPageWindow = shouldRetainMeasuredPageWindow;\n\n      return Object.is(previousState.layout, layout) &&\n        Object.is(previousState.resetKey, resetKey) &&\n        previousState.isTransitionPageWindow === isTransitionPageWindow &&\n        arePageWindowsEqual(previousState.pageWindow, nextPageWindow)\n        ? previousState\n        : {\n            isTransitionPageWindow,\n            layout,\n            resetKey,\n            pageWindow: nextPageWindow,\n          };\n    });\n    if (shouldHoldPageWindow) {\n      cancelPageWindowRetentionRelease();\n    } else if (\n      shouldReleasePdfPageWindowRetention(transition) &&\n      !hasReleasedPageWindowRetention\n    ) {\n      schedulePageWindowRetentionRelease();\n    } else if (!shouldRetainPreviousPageWindow) {\n      cancelPageWindowRetentionRelease();\n    }\n    if (fitPerfectly && measureFrameRef.current === 0) {\n      measureFrameRef.current = requestAnimationFrame(() =>\n        measureVisiblePagesNowRef.current(),\n      );\n    }\n  }, [\n    getCurrentScrollMetrics,\n    getPageWindow,\n    layout,\n    resetKey,\n    cancelPageWindowRetentionRelease,\n    schedulePageWindowRetentionRelease,\n    hasReleasedPageWindowRetention,\n    isLayoutTransitioning,\n    renderWindowTransition,\n    shouldHoldPageWindow,\n    shouldFreezePreviousPageWindow,\n    shouldRetainPreviousPageWindow,\n    transition,\n  ]);\n  const measureVisiblePagesNowRef = React.useRef(measureVisiblePagesNow);\n  useKeyedLayoutEffect(joinEffectKey([measureVisiblePagesNow]), () => {\n    measureVisiblePagesNowRef.current = measureVisiblePagesNow;\n  });\n\n  const measureVisiblePages = React.useCallback(() => {\n    if (measureFrameRef.current) return;\n    measureFrameRef.current = requestAnimationFrame(() =>\n      measureVisiblePagesNowRef.current(),\n    );\n  }, []);\n\n  useKeyedMountEffect(\n    joinEffectKey([\n      cancelPageWindowRetentionRelease,\n      schedulePageWindowRetentionRelease,\n      shouldHoldPageWindow,\n      shouldRetainPreviousPageWindow,\n      transition?.layoutPolicy,\n      transition?.source,\n    ]),\n    () => {\n      if (shouldHoldPageWindow) {\n        cancelPageWindowRetentionRelease();\n      } else if (\n        shouldReleasePdfPageWindowRetention(transition) &&\n        !hasReleasedPageWindowRetention\n      ) {\n        schedulePageWindowRetentionRelease();\n      } else if (!shouldRetainPreviousPageWindow) {\n        cancelPageWindowRetentionRelease();\n      }\n    },\n  );\n\n  useKeyedMountEffect(joinEffectKey([measureVisiblePagesNow]), () => {\n    if (measureFrameRef.current) {\n      cancelAnimationFrame(measureFrameRef.current);\n      measureFrameRef.current = 0;\n    }\n    measureVisiblePagesNow();\n  });\n\n  useMountEffect(() => () => {\n    if (measureFrameRef.current) {\n      cancelAnimationFrame(measureFrameRef.current);\n    }\n    cancelPageWindowRetentionRelease();\n  });\n\n  return {\n    scrollPageOffset: pageWindow.scrollPageOffset,\n    visiblePageNumbers: pageWindow.visiblePageNumbers,\n    renderPageNumbers: pageWindow.renderPageNumbers,\n    warmPageNumbers: pageWindow.warmPageNumbers,\n    preloadPageNumbers: pageWindow.preloadPageNumbers,\n    measureVisiblePages,\n  };\n}\n\nfunction createTransitionPageWindow({\n  currentPageWindow,\n  layout,\n  previousPageWindow,\n  shouldFreezePreviousPageWindow,\n  shouldRetainPreviousPageWindow,\n}: {\n  currentPageWindow: PdfPageWindow;\n  layout: PdfPageLayoutModel;\n  previousPageWindow: PdfPageWindow;\n  shouldFreezePreviousPageWindow: boolean;\n  shouldRetainPreviousPageWindow: boolean;\n}): PdfPageWindow {\n  if (!shouldRetainPreviousPageWindow) return currentPageWindow;\n\n  if (shouldFreezePreviousPageWindow) {\n    return {\n      scrollPageOffset: currentPageWindow.scrollPageOffset,\n      visiblePageNumbers: previousPageWindow.visiblePageNumbers,\n      renderPageNumbers: previousPageWindow.renderPageNumbers,\n      warmPageNumbers: previousPageWindow.warmPageNumbers,\n      preloadPageNumbers: previousPageWindow.preloadPageNumbers,\n    };\n  }\n\n  return {\n    scrollPageOffset: currentPageWindow.scrollPageOffset,\n    visiblePageNumbers: mergePageNumbers(\n      layout,\n      previousPageWindow.visiblePageNumbers,\n      currentPageWindow.visiblePageNumbers,\n    ),\n    renderPageNumbers: mergePageNumbers(\n      layout,\n      previousPageWindow.renderPageNumbers,\n      currentPageWindow.renderPageNumbers,\n    ),\n    warmPageNumbers: mergePageNumbers(\n      layout,\n      previousPageWindow.warmPageNumbers,\n      currentPageWindow.warmPageNumbers,\n    ),\n    preloadPageNumbers: mergePageNumbers(\n      layout,\n      previousPageWindow.preloadPageNumbers,\n      currentPageWindow.preloadPageNumbers,\n    ),\n  };\n}\n\nfunction mergePageNumbers(\n  layout: PdfPageLayoutModel,\n  previousPageNumbers: readonly number[],\n  currentPageNumbers: readonly number[],\n) {\n  return Array.from(new Set([...previousPageNumbers, ...currentPageNumbers]))\n    .filter((pageNumber) => pageNumber >= 1 && pageNumber <= layout.pageCount)\n    .sort((a, b) => a - b);\n}\n\nfunction arePageWindowsEqual(\n  previousPageWindow: PdfPageWindow,\n  nextPageWindow: PdfPageWindow,\n) {\n  return (\n    previousPageWindow.scrollPageOffset === nextPageWindow.scrollPageOffset &&\n    arePageNumbersEqual(\n      previousPageWindow.visiblePageNumbers,\n      nextPageWindow.visiblePageNumbers,\n    ) &&\n    arePageNumbersEqual(\n      previousPageWindow.renderPageNumbers,\n      nextPageWindow.renderPageNumbers,\n    ) &&\n    arePageNumbersEqual(\n      previousPageWindow.warmPageNumbers,\n      nextPageWindow.warmPageNumbers,\n    ) &&\n    arePageNumbersEqual(\n      previousPageWindow.preloadPageNumbers,\n      nextPageWindow.preloadPageNumbers,\n    )\n  );\n}\n\nfunction arePageNumbersEqual(\n  previousPageNumbers: readonly number[],\n  nextPageNumbers: readonly number[],\n) {\n  if (previousPageNumbers.length !== nextPageNumbers.length) return false;\n  return previousPageNumbers.every(\n    (pageNumber, index) => pageNumber === nextPageNumbers[index],\n  );\n}\n\nfunction shouldFitPdfPerfectly({\n  canFitPerfectly,\n  previousScrollTop,\n  renderOverscanPx,\n  scrollTop,\n  viewportHeight,\n}: {\n  canFitPerfectly: boolean;\n  previousScrollTop: number;\n  renderOverscanPx: number;\n  scrollTop: number;\n  viewportHeight: number;\n}) {\n  return (\n    canFitPerfectly &&\n    viewportHeight > 0 &&\n    Math.abs(scrollTop - previousScrollTop) >\n      viewportHeight + renderOverscanPx * 2\n  );\n}\n\nfunction getPdfRenderWindowOverscanPx({\n  layout,\n  transition,\n  viewportHeight,\n}: {\n  layout: PdfPageLayoutModel;\n  transition?: ViewerDocumentTransition;\n  viewportHeight: number;\n}) {\n  if (transition?.source !== \"viewer-shell\") {\n    return PDF_RENDER_WINDOW_OVERSCAN_PX;\n  }\n\n  return Math.max(\n    PDF_RENDER_WINDOW_OVERSCAN_PX,\n    getSafePositiveNumber(viewportHeight) *\n      PDF_SHELL_MOTION_RENDER_WINDOW_VIEWPORT_MULTIPLIER,\n    getSafePositiveNumber(layout.estimatedHeight) *\n      PDF_SHELL_MOTION_RENDER_WINDOW_PAGE_OVERSCAN,\n  );\n}\n\nfunction shouldRetainPdfPageWindow({\n  isLayoutTransitioning,\n  transition,\n}: {\n  isLayoutTransitioning: boolean;\n  transition?: ViewerDocumentTransition;\n}) {\n  return (\n    isLayoutTransitioning ||\n    (transition?.source === \"viewer-shell\" &&\n      transition.layoutPolicy === \"target\")\n  );\n}\n\nfunction shouldReleasePdfPageWindowRetention(\n  transition?: ViewerDocumentTransition,\n) {\n  return (\n    transition?.source === \"viewer-shell\" &&\n    transition.layoutPolicy === \"target\"\n  );\n}\n\nfunction getSafePositiveNumber(value: number) {\n  return Number.isFinite(value) ? Math.max(0, value) : 0;\n}\n",
      "type": "registry:ui",
      "target": "@ui/pdf-viewer-virtualization.ts"
    },
    {
      "path": "registry/new-york-v4/ui/pdf-viewer-page-sizes.ts",
      "content": "import * as React from \"react\";\n\nimport { useKeyedMountEffect } from \"@/hooks/use-keyed-mount-effect\";\n\nimport type { PdfPageSize } from \"./pdf-viewer-types\";\nimport { joinEffectKey } from \"@/lib/effect-key\";\n\nexport function usePdfPageSizes(resetKey: unknown) {\n  const [state, setState] = React.useState<{\n    resetKey: unknown;\n    pageSizeByNumber: ReadonlyMap<number, PdfPageSize>;\n  }>(() => ({ resetKey, pageSizeByNumber: new Map() }));\n\n  const emptyPageSizeByNumber = React.useMemo<ReadonlyMap<number, PdfPageSize>>(\n    () => new Map(),\n    [],\n  );\n  const pageSizeByNumber = Object.is(state.resetKey, resetKey)\n    ? state.pageSizeByNumber\n    : emptyPageSizeByNumber;\n\n  useKeyedMountEffect(joinEffectKey([\"pdf-page-sizes\", resetKey]), () => {\n    setState((previousState) =>\n      Object.is(previousState.resetKey, resetKey)\n        ? previousState\n        : { resetKey, pageSizeByNumber: new Map() },\n    );\n  });\n\n  const setPageSizes = React.useCallback(\n    (pageSizes: Iterable<readonly [number, PdfPageSize]>) => {\n      setState((previousState) => {\n        const previousPageSizeByNumber = Object.is(\n          previousState.resetKey,\n          resetKey,\n        )\n          ? previousState.pageSizeByNumber\n          : emptyPageSizeByNumber;\n\n        let nextPageSizeByNumber: Map<number, PdfPageSize> | null = null;\n        for (const [pageNumber, size] of pageSizes) {\n          const current = (\n            nextPageSizeByNumber ?? previousPageSizeByNumber\n          ).get(pageNumber);\n          if (current?.width === size.width && current.height === size.height) {\n            continue;\n          }\n\n          nextPageSizeByNumber ??= new Map(previousPageSizeByNumber);\n          nextPageSizeByNumber.set(pageNumber, {\n            width: size.width,\n            height: size.height,\n          });\n        }\n\n        if (!nextPageSizeByNumber) {\n          return previousState;\n        }\n        return { resetKey, pageSizeByNumber: nextPageSizeByNumber };\n      });\n    },\n    [emptyPageSizeByNumber, resetKey],\n  );\n\n  const setPageSize = React.useCallback(\n    (pageNumber: number, size: PdfPageSize) => {\n      setPageSizes([[pageNumber, size]]);\n    },\n    [setPageSizes],\n  );\n\n  return { pageSizeByNumber, setPageSize, setPageSizes };\n}\n",
      "type": "registry:ui",
      "target": "@ui/pdf-viewer-page-sizes.ts"
    },
    {
      "path": "registry/new-york-v4/ui/pdf-viewer-page.tsx",
      "content": "import * as React from \"react\";\n\nimport { readPdfPageResource } from \"@/lib/pdf-document-resource\";\nimport type { PdfDocumentProxy } from \"@/lib/pdf-document-types\";\n\nimport { getPdfCanvasPixelSize } from \"./pdf-viewer-canvas\";\nimport {\n  readPdfRenderedPageCache,\n  readPdfRenderedPageCachePreview,\n  writePdfRenderedPageCache,\n  type PdfRenderedPageCache,\n  type PdfRenderedPageSignature,\n} from \"./pdf-viewer-render-cache\";\nimport { toPdfRenderFailedError } from \"./pdf-viewer-render-error\";\nimport type {\n  PageOverlayProps,\n  PdfPageRenderStatus,\n  PdfPageRenderTiming,\n  PdfPageSize,\n} from \"./pdf-viewer-types\";\nimport { useKeyedMountEffect } from \"@/hooks/use-keyed-mount-effect\";\nimport { joinEffectKey } from \"@/lib/effect-key\";\n\ntype PdfRenderedPage = PdfRenderedPageSignature;\ntype PdfDocumentPage = ReturnType<typeof readPdfPageResource>;\ntype PdfViewportSize = {\n  width: number;\n  height: number;\n};\n\ntype PdfPageProps = {\n  document: PdfDocumentProxy;\n  documentKey: string;\n  pageNumber: number;\n  scale: number;\n  renderScale?: number;\n  isLayoutTransitioning?: boolean;\n  rotation: number;\n  devicePixelRatio: number;\n  renderCache?: PdfRenderedPageCache;\n  renderOverlay?: (props: PageOverlayProps) => React.ReactNode;\n  onRenderTiming?: (timing: PdfPageRenderTiming) => void;\n  onSize?: (pageNumber: number, size: PdfPageSize) => void;\n};\n\ntype PdfPageCanvasProps = {\n  documentKey: string;\n  page: PdfDocumentPage;\n  pageNumber: number;\n  renderScale: number;\n  rotation: number;\n  devicePixelRatio: number;\n  renderCache?: PdfRenderedPageCache;\n  onRenderTiming?: (timing: PdfPageRenderTiming) => void;\n};\n\nexport const PdfPage = React.memo(function PdfPage({\n  document,\n  documentKey,\n  pageNumber,\n  scale,\n  renderScale,\n  isLayoutTransitioning,\n  rotation,\n  devicePixelRatio,\n  renderCache,\n  renderOverlay,\n  onRenderTiming,\n  onSize,\n}: PdfPageProps) {\n  const page = readPdfPageResource(document, pageNumber);\n  const intrinsicViewport = React.useMemo(\n    () => page.getViewport({ scale: 1, rotation: page.rotate ?? 0 }),\n    [page],\n  );\n\n  useKeyedMountEffect(\n    joinEffectKey([\n      intrinsicViewport.height,\n      intrinsicViewport.width,\n      onSize,\n      pageNumber,\n    ]),\n    () => {\n      onSize?.(pageNumber, {\n        width: intrinsicViewport.width,\n        height: intrinsicViewport.height,\n      });\n    },\n  );\n\n  const displaySize = React.useMemo(\n    () =>\n      getPdfDisplayViewportSize({\n        intrinsicViewport,\n        rotation,\n        scale,\n      }),\n    [intrinsicViewport, rotation, scale],\n  );\n  const resolvedRenderScale = renderScale ?? scale;\n\n  return (\n    <div\n      className=\"ring-border relative shadow-sm ring-1\"\n      data-layout-transitioning={isLayoutTransitioning ? \"\" : undefined}\n      style={{ width: displaySize.width, height: displaySize.height }}\n      data-slot=\"pdf-page\"\n      data-page={pageNumber}\n    >\n      <PdfPageCanvas\n        documentKey={documentKey}\n        page={page}\n        pageNumber={pageNumber}\n        renderScale={resolvedRenderScale}\n        rotation={rotation}\n        devicePixelRatio={devicePixelRatio}\n        renderCache={renderCache}\n        onRenderTiming={onRenderTiming}\n      />\n      {renderOverlay ? (\n        <div className=\"pointer-events-none absolute inset-0\">\n          {renderOverlay({\n            pageNumber,\n            width: displaySize.width,\n            height: displaySize.height,\n            scale,\n            rotation,\n          })}\n        </div>\n      ) : null}\n    </div>\n  );\n}, arePdfPagePropsEqual);\n\nconst PdfPageCanvas = React.memo(function PdfPageCanvas({\n  documentKey,\n  page,\n  pageNumber,\n  renderScale,\n  rotation,\n  devicePixelRatio,\n  renderCache,\n  onRenderTiming,\n}: PdfPageCanvasProps) {\n  const renderViewport = React.useMemo(\n    () =>\n      page.getViewport({\n        scale: renderScale,\n        rotation: ((page.rotate ?? 0) + rotation) % 360,\n      }),\n    [page, renderScale, rotation],\n  );\n  const [renderError, setRenderError] = React.useState<unknown>(null);\n  if (renderError) throw renderError;\n\n  const renderedPageRef = React.useRef<PdfRenderedPage | null>(null);\n\n  const canvasRef = React.useCallback(\n    (canvas: HTMLCanvasElement | null) => {\n      if (!canvas) return;\n      const renderSignature = {\n        documentKey,\n        pageNumber,\n        scale: renderScale,\n        rotation,\n        devicePixelRatio,\n        viewportWidth: renderViewport.width,\n        viewportHeight: renderViewport.height,\n      } satisfies PdfRenderedPage;\n      if (\n        renderedPageRef.current &&\n        doesRenderedPageSatisfyRequest(renderedPageRef.current, renderSignature)\n      ) {\n        return;\n      }\n\n      const startedAt = readNow();\n      const reportRenderTiming = (\n        status: PdfPageRenderStatus,\n        source?: PdfPageRenderTiming[\"source\"],\n      ) => {\n        onRenderTiming?.({\n          pageNumber,\n          scale: renderScale,\n          rotation,\n          devicePixelRatio,\n          status,\n          source,\n          durationMs: Math.max(0, readNow() - startedAt),\n        });\n      };\n      const context = canvas.getContext(\"2d\");\n      if (!context) {\n        markCanvasRenderStatus(canvas, renderSignature, \"failed\");\n        reportRenderTiming(\"failed\");\n        setRenderError(\n          toPdfRenderFailedError(new Error(\"Canvas 2D context unavailable.\")),\n        );\n        return;\n      }\n\n      const canvasWidth = getPdfCanvasPixelSize(\n        renderSignature.viewportWidth,\n        devicePixelRatio,\n      );\n      const canvasHeight = getPdfCanvasPixelSize(\n        renderSignature.viewportHeight,\n        devicePixelRatio,\n      );\n      const cachedPage = readPdfRenderedPageCache(renderCache, renderSignature);\n      if (cachedPage) {\n        replaceCanvasImage(\n          canvas,\n          context,\n          cachedPage.canvas,\n          canvasWidth,\n          canvasHeight,\n        );\n        renderedPageRef.current = renderSignature;\n        markCanvasRenderStatus(canvas, renderSignature, \"rendered\", \"cache\");\n        reportRenderTiming(\"rendered\", \"cache\");\n        return;\n      }\n\n      const previewPage = readPdfRenderedPageCachePreview(\n        renderCache,\n        renderSignature,\n      );\n      const canKeepRenderedPageVisible =\n        renderedPageRef.current !== null &&\n        canRenderedPageStayVisibleWhilePending(\n          renderedPageRef.current,\n          renderSignature,\n        );\n      if (previewPage && !canKeepRenderedPageVisible) {\n        replaceCanvasImage(\n          canvas,\n          context,\n          previewPage.canvas,\n          canvasWidth,\n          canvasHeight,\n        );\n        markCanvasRenderStatus(\n          canvas,\n          renderSignature,\n          \"pending\",\n          \"cache-preview\",\n        );\n      } else if (!canKeepRenderedPageVisible) {\n        resizeCanvas(canvas, canvasWidth, canvasHeight);\n        markCanvasRenderStatus(canvas, renderSignature, \"pending\");\n      } else {\n        markCanvasRenderStatus(canvas, renderSignature, \"pending\");\n      }\n\n      const renderCanvas = canvas.ownerDocument.createElement(\"canvas\");\n      renderCanvas.width = canvasWidth;\n      renderCanvas.height = canvasHeight;\n      renderCanvas.style.width = `${renderViewport.width}px`;\n      renderCanvas.style.height = `${renderViewport.height}px`;\n      const renderContext = renderCanvas.getContext(\"2d\");\n      if (!renderContext) {\n        markCanvasRenderStatus(canvas, renderSignature, \"failed\");\n        reportRenderTiming(\"failed\");\n        setRenderError(\n          toPdfRenderFailedError(new Error(\"Canvas 2D context unavailable.\")),\n        );\n        return;\n      }\n\n      let renderTask: ReturnType<typeof page.render>;\n      try {\n        renderTask = page.render({\n          canvas: renderCanvas,\n          canvasContext: renderContext,\n          viewport: renderViewport,\n          transform:\n            devicePixelRatio !== 1\n              ? [devicePixelRatio, 0, 0, devicePixelRatio, 0, 0]\n              : undefined,\n        });\n      } catch (error) {\n        markCanvasRenderStatus(canvas, renderSignature, \"failed\");\n        reportRenderTiming(\"failed\");\n        setRenderError(toPdfRenderFailedError(error));\n        return;\n      }\n      let isActive = true;\n      let didFinishPdfRender = false;\n      renderTask.promise.then(\n        () => {\n          if (!isActive) return;\n          didFinishPdfRender = true;\n          replaceCanvasImage(\n            canvas,\n            context,\n            renderCanvas,\n            canvasWidth,\n            canvasHeight,\n          );\n          renderedPageRef.current = renderSignature;\n          writePdfRenderedPageCache({\n            cache: renderCache,\n            rendered: renderSignature,\n            sourceCanvas: renderCanvas,\n          });\n          markCanvasRenderStatus(canvas, renderSignature, \"rendered\", \"pdfjs\");\n          reportRenderTiming(\"rendered\", \"pdfjs\");\n        },\n        (error) => {\n          if (!isActive) return;\n          didFinishPdfRender = true;\n          markCanvasRenderStatus(canvas, renderSignature, \"failed\");\n          reportRenderTiming(\"failed\");\n          setRenderError(toPdfRenderFailedError(error));\n        },\n      );\n      return () => {\n        isActive = false;\n        if (!didFinishPdfRender) {\n          renderTask.cancel();\n        }\n        if (!didFinishPdfRender) {\n          markCanvasRenderStatus(canvas, renderSignature, \"cancelled\");\n          reportRenderTiming(\"cancelled\");\n        }\n      };\n    },\n    [\n      devicePixelRatio,\n      documentKey,\n      onRenderTiming,\n      page,\n      pageNumber,\n      renderCache,\n      renderScale,\n      renderViewport,\n      rotation,\n    ],\n  );\n\n  return (\n    <canvas\n      ref={canvasRef}\n      style={{ width: \"100%\", height: \"100%\" }}\n      className=\"block bg-white\"\n    />\n  );\n}, arePdfPageCanvasPropsEqual);\n\nfunction arePdfPagePropsEqual(previous: PdfPageProps, next: PdfPageProps) {\n  return (\n    previous.document === next.document &&\n    previous.documentKey === next.documentKey &&\n    previous.pageNumber === next.pageNumber &&\n    previous.scale === next.scale &&\n    previous.renderScale === next.renderScale &&\n    previous.isLayoutTransitioning === next.isLayoutTransitioning &&\n    previous.rotation === next.rotation &&\n    previous.devicePixelRatio === next.devicePixelRatio &&\n    previous.renderCache === next.renderCache &&\n    previous.renderOverlay === next.renderOverlay &&\n    previous.onRenderTiming === next.onRenderTiming &&\n    previous.onSize === next.onSize\n  );\n}\n\nfunction arePdfPageCanvasPropsEqual(\n  previous: PdfPageCanvasProps,\n  next: PdfPageCanvasProps,\n) {\n  return (\n    previous.documentKey === next.documentKey &&\n    previous.page === next.page &&\n    previous.pageNumber === next.pageNumber &&\n    previous.renderScale === next.renderScale &&\n    previous.rotation === next.rotation &&\n    previous.devicePixelRatio === next.devicePixelRatio &&\n    previous.renderCache === next.renderCache &&\n    previous.onRenderTiming === next.onRenderTiming\n  );\n}\n\nfunction getPdfDisplayViewportSize({\n  intrinsicViewport,\n  rotation,\n  scale,\n}: {\n  intrinsicViewport: PdfViewportSize;\n  rotation: number;\n  scale: number;\n}): PdfViewportSize {\n  const isRotated = normalizePdfRotation(rotation) % 180 !== 0;\n  return {\n    width: Math.round(\n      (isRotated ? intrinsicViewport.height : intrinsicViewport.width) * scale,\n    ),\n    height: Math.round(\n      (isRotated ? intrinsicViewport.width : intrinsicViewport.height) * scale,\n    ),\n  };\n}\n\nfunction normalizePdfRotation(rotation: number) {\n  return ((rotation % 360) + 360) % 360;\n}\n\nfunction resizeCanvas(\n  canvas: HTMLCanvasElement,\n  width: number,\n  height: number,\n) {\n  if (canvas.width !== width) canvas.width = width;\n  if (canvas.height !== height) canvas.height = height;\n}\n\nfunction drawCanvasImage(\n  context: CanvasRenderingContext2D,\n  image: HTMLCanvasElement,\n  width: number,\n  height: number,\n) {\n  if (typeof context.setTransform === \"function\") {\n    context.setTransform(1, 0, 0, 1, 0, 0);\n  }\n  if (typeof context.drawImage === \"function\") {\n    context.drawImage(image, 0, 0, width, height);\n  }\n}\n\nfunction replaceCanvasImage(\n  canvas: HTMLCanvasElement,\n  context: CanvasRenderingContext2D,\n  image: HTMLCanvasElement,\n  width: number,\n  height: number,\n) {\n  resizeCanvas(canvas, width, height);\n  drawCanvasImage(context, image, width, height);\n}\n\nfunction markCanvasRenderStatus(\n  canvas: HTMLCanvasElement,\n  rendered: PdfRenderedPage,\n  status: \"pending\" | PdfPageRenderStatus,\n  source?: PdfPageRenderTiming[\"source\"],\n) {\n  canvas.dataset.pdfPageNumber = String(rendered.pageNumber);\n  canvas.dataset.pdfRenderStatus = status;\n  if (source) {\n    canvas.dataset.pdfRenderSource = source;\n  } else {\n    delete canvas.dataset.pdfRenderSource;\n  }\n}\n\nfunction doesRenderedPageSatisfyRequest(\n  rendered: PdfRenderedPage,\n  requested: PdfRenderedPage,\n) {\n  return (\n    rendered.documentKey === requested.documentKey &&\n    rendered.pageNumber === requested.pageNumber &&\n    rendered.rotation === requested.rotation &&\n    rendered.scale >= requested.scale &&\n    rendered.viewportWidth >= requested.viewportWidth &&\n    rendered.viewportHeight >= requested.viewportHeight &&\n    hasMatchingPageAspectRatio(rendered, requested) &&\n    rendered.devicePixelRatio >= requested.devicePixelRatio\n  );\n}\n\nfunction canRenderedPageStayVisibleWhilePending(\n  rendered: PdfRenderedPage,\n  requested: PdfRenderedPage,\n) {\n  return (\n    rendered.documentKey === requested.documentKey &&\n    rendered.pageNumber === requested.pageNumber &&\n    rendered.rotation === requested.rotation &&\n    hasMatchingPageAspectRatio(rendered, requested)\n  );\n}\n\nfunction hasMatchingPageAspectRatio(\n  rendered: PdfRenderedPage,\n  requested: PdfRenderedPage,\n) {\n  const renderedRatio = rendered.viewportWidth / rendered.viewportHeight;\n  const requestedRatio = requested.viewportWidth / requested.viewportHeight;\n  return Math.abs(renderedRatio - requestedRatio) < 0.0001;\n}\n\nfunction readNow() {\n  return typeof performance !== \"undefined\" ? performance.now() : Date.now();\n}\n",
      "type": "registry:ui",
      "target": "@ui/pdf-viewer-page.tsx"
    },
    {
      "path": "registry/new-york-v4/ui/pdf-viewer-canvas.ts",
      "content": "export function getPdfCanvasPixelSize(\n  cssSize: number,\n  devicePixelRatio: number,\n) {\n  if (\n    !Number.isFinite(cssSize) ||\n    !Number.isFinite(devicePixelRatio) ||\n    cssSize <= 0 ||\n    devicePixelRatio <= 0\n  ) {\n    return 1;\n  }\n  return Math.max(1, Math.floor(cssSize * devicePixelRatio));\n}\n",
      "type": "registry:ui",
      "target": "@ui/pdf-viewer-canvas.ts"
    },
    {
      "path": "registry/new-york-v4/ui/pdf-viewer-render-error.ts",
      "content": "import { ViewerFormatError } from \"@/lib/viewer-errors\";\n\nexport function toPdfRenderFailedError(error: unknown) {\n  return new ViewerFormatError({\n    format: \"pdf\",\n    kind: \"render_failed\",\n    message: \"Failed to render PDF page.\",\n    cause: error,\n  });\n}\n",
      "type": "registry:ui",
      "target": "@ui/pdf-viewer-render-error.ts"
    },
    {
      "path": "registry/new-york-v4/ui/pdf-viewer-states.tsx",
      "content": "import * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\nimport { Skeleton } from \"@/components/ui/skeleton\";\nimport { ViewerControlsSkeleton } from \"@/components/ui/viewer-controls\";\nimport type { PdfPageSize } from \"./pdf-viewer-types\";\n\nconst DEFAULT_PDF_FALLBACK_PAGE_SIZE: PdfPageSize = {\n  width: 8.5,\n  height: 11,\n};\n\nexport function PdfViewerFallback({\n  className,\n  bare = false,\n  controls = true,\n  fallbackPageSize,\n}: {\n  className?: string;\n  bare?: boolean;\n  controls?: boolean;\n  fallbackPageSize?: PdfPageSize;\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=\"pdf-viewer\"\n    >\n      {controls ? (\n        <ViewerControlsSkeleton position zoom rotate download />\n      ) : null}\n      <div className=\"min-h-0 flex-1 overflow-auto\">\n        <div className=\"flex flex-col items-center p-4\">\n          <PageAspectSkeleton pageSize={fallbackPageSize} />\n        </div>\n      </div>\n    </div>\n  );\n}\n\nexport function PageSkeleton() {\n  return (\n    <div\n      aria-hidden\n      className=\"ring-border size-full rounded-none bg-white shadow-sm ring-1\"\n      data-slot=\"pdf-page-skeleton\"\n    />\n  );\n}\n\nfunction PageAspectSkeleton({ pageSize }: { pageSize?: PdfPageSize }) {\n  const resolvedPageSize =\n    pageSize &&\n    Number.isFinite(pageSize.width) &&\n    pageSize.width > 0 &&\n    Number.isFinite(pageSize.height) &&\n    pageSize.height > 0\n      ? pageSize\n      : DEFAULT_PDF_FALLBACK_PAGE_SIZE;\n\n  return (\n    <Skeleton\n      aria-hidden\n      className=\"ring-border w-full rounded-none shadow-sm ring-1\"\n      data-slot=\"pdf-page-skeleton\"\n      style={{\n        aspectRatio: `${resolvedPageSize.width} / ${resolvedPageSize.height}`,\n      }}\n    />\n  );\n}\n",
      "type": "registry:ui",
      "target": "@ui/pdf-viewer-states.tsx"
    },
    {
      "path": "registry/new-york-v4/ui/pdf-viewer-thumbnails.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { clearPdfDocumentResource } from \"@/lib/pdf-document-resource\";\nimport { cn } from \"@/lib/utils\";\nimport type { ViewerResource } from \"@/lib/viewer-resource\";\nimport { Spinner } from \"@/components/ui/spinner\";\n\nimport {\n  buildPdfThumbnailLayout,\n  PDF_THUMBNAIL_INITIAL_VIEWPORT_HEIGHT,\n  PDF_THUMBNAIL_OVERSCAN_PX,\n  type PdfThumbnailShape,\n} from \"./pdf-thumbnail-layout\";\nimport {\n  useOptionalFileViewerSidebarMotion,\n  type FileViewerSidebarMotion,\n} from \"./file-viewer-renderer-frame\";\nimport { PdfThumbnailRailViewport } from \"./pdf-thumbnail-rail\";\nimport { usePdfViewerThumbnails } from \"./pdf-viewer-context\";\nimport { useIsClient } from \"./use-is-client\";\nimport { usePdfThumbnailDocument } from \"./use-pdf-thumbnail-document\";\nimport { usePdfThumbnailPageMetrics } from \"./use-pdf-thumbnail-page-metrics\";\nimport { usePdfThumbnailWindow } from \"./use-pdf-thumbnail-window\";\nimport { useThumbnailRailFollow } from \"./use-thumbnail-rail-follow\";\nimport { usePdfRenderedPageCache } from \"./pdf-viewer-render-cache\";\nimport { ViewerErrorBoundary } from \"./viewer-error\";\nimport { useKeyedMountEffect } from \"@/hooks/use-keyed-mount-effect\";\nimport { joinEffectKey } from \"@/lib/effect-key\";\n\nconst PDF_THUMBNAIL_RENDER_RESUME_DELAY_MS = 1400;\nconst PDF_THUMBNAIL_CLOSED_WARM_DELAY_MS = 2200;\n\nexport interface PdfViewerThumbnailsProps {\n  /** Thumbnail image width in CSS pixels. The sidebar shell owns rail width. */\n  thumbnailWidth?: number;\n  /** Preserve page aspect or crop each page preview into a square frame. */\n  thumbnailShape?: PdfThumbnailShape;\n  className?: string;\n}\n\nexport interface PdfThumbnailRailProps {\n  /** Same resource object passed to PdfResourceContent. */\n  resource: ViewerResource;\n  /** 1-based current page; its thumbnail is highlighted. */\n  currentPage?: number | null;\n  /** Click a thumbnail to jump the document to that page. */\n  onSelectPage?: (page: number) => void;\n  /** Thumbnail image width in CSS pixels. The sidebar shell owns rail width. */\n  thumbnailWidth?: number;\n  /** Preserve page aspect or crop each page preview into a square frame. */\n  thumbnailShape?: PdfThumbnailShape;\n  className?: string;\n}\n\nexport function PdfViewerThumbnails({\n  className,\n  thumbnailShape,\n  thumbnailWidth,\n}: PdfViewerThumbnailsProps) {\n  const thumbnails = usePdfViewerThumbnails();\n  const sidebarMotion = useOptionalFileViewerSidebarMotion();\n  const isRenderingSuspended = usePdfThumbnailRenderSuspension(sidebarMotion);\n\n  return (\n    <PdfThumbnailRailShell\n      className={className}\n      currentPage={thumbnails.currentPage}\n      isRenderingSuspended={isRenderingSuspended}\n      onSelectPage={thumbnails.onSelectPage}\n      resource={thumbnails.resource}\n      thumbnailShape={thumbnailShape}\n      thumbnailWidth={thumbnailWidth}\n    />\n  );\n}\n\nexport function PdfThumbnailRail({\n  className,\n  currentPage,\n  onSelectPage,\n  resource,\n  thumbnailShape,\n  thumbnailWidth,\n}: PdfThumbnailRailProps) {\n  return (\n    <PdfThumbnailRailShell\n      className={className}\n      currentPage={currentPage}\n      isRenderingSuspended={false}\n      onSelectPage={onSelectPage}\n      resource={resource}\n      thumbnailShape={thumbnailShape}\n      thumbnailWidth={thumbnailWidth}\n    />\n  );\n}\n\nfunction PdfThumbnailRailShell({\n  className,\n  currentPage,\n  isRenderingSuspended,\n  onSelectPage,\n  resource,\n  thumbnailShape,\n  thumbnailWidth,\n}: PdfThumbnailRailProps & {\n  isRenderingSuspended: boolean;\n}) {\n  const isClient = useIsClient();\n\n  if (!isClient) {\n    return <ThumbnailsFallback className={className} />;\n  }\n\n  return (\n    <ViewerErrorBoundary\n      className=\"h-full\"\n      download={resource.originalDownload}\n      format=\"pdf\"\n      onRetry={() => clearPdfDocumentResource(resource.content)}\n      resetKey={resource.keys.resource}\n      sourceKind={resource.sourceKind}\n      variant=\"inline\"\n    >\n      <React.Suspense fallback={<ThumbnailsFallback className={className} />}>\n        <PdfThumbnailRailInner\n          currentPage={currentPage}\n          isRenderingSuspended={isRenderingSuspended}\n          onSelectPage={onSelectPage}\n          resource={resource}\n          thumbnailShape={thumbnailShape}\n          thumbnailWidth={thumbnailWidth}\n          className={cn(\"h-full\", className)}\n        />\n      </React.Suspense>\n    </ViewerErrorBoundary>\n  );\n}\n\nfunction PdfThumbnailRailInner({\n  resource,\n  currentPage,\n  isRenderingSuspended,\n  onSelectPage,\n  thumbnailShape = \"page\",\n  thumbnailWidth = 120,\n  className,\n}: PdfThumbnailRailProps & {\n  isRenderingSuspended: boolean;\n}) {\n  const doc = usePdfThumbnailDocument(resource);\n  const renderCache = usePdfRenderedPageCache(doc);\n  const pageMetrics = usePdfThumbnailPageMetrics(doc, doc);\n  const { metricByPageNumber, pageCount, requestPageMetrics } = pageMetrics;\n  const viewportRef = React.useRef<HTMLDivElement | null>(null);\n  const activeCurrentPage = isRenderingSuspended ? null : currentPage;\n\n  const layout = React.useMemo(\n    () =>\n      buildPdfThumbnailLayout({\n        pageCount,\n        width: thumbnailWidth,\n        shape: thumbnailShape,\n        metricByPageNumber,\n      }),\n    [metricByPageNumber, pageCount, thumbnailShape, thumbnailWidth],\n  );\n  const thumbnailWindow = usePdfThumbnailWindow({\n    layout,\n    viewportRef,\n    overscanPx: PDF_THUMBNAIL_OVERSCAN_PX,\n    initialViewportHeight: PDF_THUMBNAIL_INITIAL_VIEWPORT_HEIGHT,\n  });\n  const follow = useThumbnailRailFollow({\n    currentPage: activeCurrentPage,\n    layout,\n    viewportRef,\n    resetKey: doc,\n  });\n  useKeyedMountEffect(\n    joinEffectKey([\n      activeCurrentPage,\n      isRenderingSuspended,\n      layout.pageCount,\n      requestPageMetrics,\n      thumbnailWindow.visibleItems,\n    ]),\n    () => {\n      if (isRenderingSuspended) return;\n      requestPageMetrics(\n        getRequestedThumbnailMetricPages({\n          currentPage: activeCurrentPage,\n          pageCount: layout.pageCount,\n          visibleItems: thumbnailWindow.visibleItems,\n        }),\n      );\n    },\n  );\n\n  return (\n    <PdfThumbnailRailViewport\n      doc={doc}\n      documentKey={resource.content.key}\n      isRenderingSuspended={isRenderingSuspended}\n      layout={layout}\n      visibleItems={thumbnailWindow.visibleItems}\n      viewportHeight={thumbnailWindow.viewportHeight}\n      currentPage={activeCurrentPage}\n      viewportRef={viewportRef}\n      onSelectPage={onSelectPage}\n      onPageActivate={follow.onPageActivate}\n      onPointerEnter={follow.onPointerEnter}\n      onPointerLeave={follow.onPointerLeave}\n      onScroll={follow.onScroll}\n      className={className}\n      renderCache={renderCache}\n    />\n  );\n}\n\nfunction usePdfThumbnailRenderSuspension(\n  sidebarMotion: FileViewerSidebarMotion | null,\n) {\n  const isMotionManaged = sidebarMotion?.isMotionManaged === true;\n  const isSidebarOpen = sidebarMotion?.isSidebarOpen === true;\n  const isSidebarInteractive = sidebarMotion?.isSidebarInteractive === true;\n  const isSidebarTransitioning = sidebarMotion?.isSidebarTransitioning === true;\n  const [isOpenSettling, setIsOpenSettling] = React.useState(false);\n  const [isClosedWarmReleased, setIsClosedWarmReleased] = React.useState(false);\n\n  useKeyedMountEffect(\n    joinEffectKey([\n      isMotionManaged,\n      isOpenSettling,\n      isSidebarInteractive,\n      isSidebarOpen,\n      isSidebarTransitioning,\n    ]),\n    () => {\n      if (!isMotionManaged) {\n        setIsOpenSettling(false);\n        setIsClosedWarmReleased(false);\n        return;\n      }\n\n      if (!isSidebarOpen) {\n        setIsOpenSettling(false);\n        setIsClosedWarmReleased(false);\n        if (isSidebarTransitioning) return;\n\n        const timer = window.setTimeout(() => {\n          setIsClosedWarmReleased(true);\n        }, PDF_THUMBNAIL_CLOSED_WARM_DELAY_MS);\n\n        return () => window.clearTimeout(timer);\n      }\n\n      setIsClosedWarmReleased(false);\n\n      if (isSidebarTransitioning || !isSidebarInteractive) {\n        setIsOpenSettling(true);\n        return;\n      }\n\n      if (!isOpenSettling) return;\n\n      const timer = window.setTimeout(() => {\n        setIsOpenSettling(false);\n      }, PDF_THUMBNAIL_RENDER_RESUME_DELAY_MS);\n\n      return () => window.clearTimeout(timer);\n    },\n  );\n\n  if (!isMotionManaged) return false;\n  if (!isSidebarOpen) return !isClosedWarmReleased;\n  if (isSidebarTransitioning || !isSidebarInteractive) return true;\n  return isOpenSettling;\n}\n\nfunction getRequestedThumbnailMetricPages({\n  currentPage,\n  pageCount,\n  visibleItems,\n}: {\n  currentPage: number | null | undefined;\n  pageCount: number;\n  visibleItems: readonly { pageNumber: number }[];\n}) {\n  const pageNumbers = new Set<number>();\n  for (const item of visibleItems) pageNumbers.add(item.pageNumber);\n  if (\n    currentPage != null &&\n    Number.isInteger(currentPage) &&\n    currentPage >= 1 &&\n    currentPage <= pageCount\n  ) {\n    pageNumbers.add(currentPage);\n  }\n\n  return pageNumbers;\n}\n\nfunction ThumbnailsFallback({ className }: { className?: string }) {\n  return (\n    <div\n      className={cn(\n        \"bg-muted/30 flex h-full items-center justify-center\",\n        className,\n      )}\n    >\n      <Spinner className=\"text-muted-foreground size-4\" />\n    </div>\n  );\n}\n",
      "type": "registry:ui",
      "target": "@ui/pdf-viewer-thumbnails.tsx"
    },
    {
      "path": "registry/new-york-v4/ui/pdf-thumbnail-layout.ts",
      "content": "export const PDF_THUMBNAIL_OVERSCAN_PX = 1000;\nexport const PDF_THUMBNAIL_INITIAL_VIEWPORT_HEIGHT = 680;\nexport const PDF_THUMBNAIL_DEFAULT_ASPECT = 4 / 3;\nexport const PDF_THUMBNAIL_LABEL_AND_GAP_HEIGHT = 22;\n\nexport type PdfThumbnailShape = \"page\" | \"square\";\n\nexport interface PdfThumbnailPageMetric {\n  pageNumber: number;\n  width: number;\n  height: number;\n}\n\nexport interface PdfThumbnailLayoutItem {\n  pageNumber: number;\n  pageIndex: number;\n  top: number;\n  height: number;\n  imageWidth: number;\n  imageHeight: number;\n}\n\nexport interface PdfThumbnailLayout {\n  pageCount: number;\n  width: number;\n  shape: PdfThumbnailShape;\n  estimatedImageHeight: number;\n  estimatedItemHeight: number;\n  labelAndGapHeight: number;\n  metricByPageNumber: ReadonlyMap<number, PdfThumbnailPageMetric>;\n  prefixHeightDeltas: readonly PdfThumbnailHeightDelta[];\n  totalHeight: number;\n}\n\nexport interface PdfThumbnailRenderedWindowItem\n  extends PdfThumbnailLayoutItem {\n  windowTop: number;\n}\n\nexport interface PdfThumbnailRenderedWindow {\n  afterHeight: number;\n  beforeHeight: number;\n  height: number;\n  items: readonly PdfThumbnailRenderedWindowItem[];\n  renderedBottom: number;\n  renderedTop: number;\n  stickyInset: number;\n}\n\nexport interface PdfThumbnailPixelWindow {\n  bottom: number;\n  top: number;\n}\n\ninterface PdfThumbnailHeightDelta {\n  pageNumber: number;\n  prefixDeltaAfterPage: number;\n}\n\nexport function buildPdfThumbnailLayout({\n  pageCount,\n  width,\n  metricByPageNumber,\n  labelAndGapHeight = PDF_THUMBNAIL_LABEL_AND_GAP_HEIGHT,\n  shape = \"page\",\n}: {\n  pageCount: number;\n  width: number;\n  metricByPageNumber?: ReadonlyMap<number, PdfThumbnailPageMetric>;\n  labelAndGapHeight?: number;\n  shape?: PdfThumbnailShape;\n}): PdfThumbnailLayout {\n  const safePageCount = normalizePageCount(pageCount);\n  const safeWidth = normalizeWidth(width);\n  const safeLabelAndGapHeight = normalizeSize(labelAndGapHeight, 0);\n  const safeMetricByPageNumber = metricByPageNumber ?? new Map();\n  const estimatedImageHeight = getThumbnailImageHeight({\n    width: safeWidth,\n    metric: safeMetricByPageNumber.get(1),\n    shape,\n  });\n  const estimatedItemHeight =\n    Math.ceil(estimatedImageHeight) + safeLabelAndGapHeight;\n  const prefixHeightDeltas = buildPrefixHeightDeltas({\n    pageCount: safePageCount,\n    width: safeWidth,\n    shape,\n    estimatedItemHeight,\n    metricByPageNumber: safeMetricByPageNumber,\n    labelAndGapHeight: safeLabelAndGapHeight,\n  });\n  const totalHeight =\n    safePageCount * estimatedItemHeight +\n    (prefixHeightDeltas.at(-1)?.prefixDeltaAfterPage ?? 0);\n\n  return {\n    pageCount: safePageCount,\n    width: safeWidth,\n    shape,\n    estimatedImageHeight,\n    estimatedItemHeight,\n    labelAndGapHeight: safeLabelAndGapHeight,\n    metricByPageNumber: safeMetricByPageNumber,\n    prefixHeightDeltas,\n    totalHeight,\n  };\n}\n\nexport function getPdfThumbnailLayoutItem(\n  layout: PdfThumbnailLayout,\n  pageNumber: number,\n): PdfThumbnailLayoutItem | null {\n  const normalizedPage = normalizeThumbnailPage(pageNumber, layout.pageCount);\n  if (normalizedPage == null) return null;\n\n  const metric = layout.metricByPageNumber.get(normalizedPage);\n  const imageHeight = getThumbnailImageHeight({\n    width: layout.width,\n    metric,\n    shape: layout.shape,\n  });\n  const height = Math.ceil(imageHeight) + layout.labelAndGapHeight;\n\n  return {\n    pageNumber: normalizedPage,\n    pageIndex: normalizedPage - 1,\n    top:\n      (normalizedPage - 1) * layout.estimatedItemHeight +\n      getPrefixHeightDeltaBeforePage(layout, normalizedPage),\n    height,\n    imageWidth: layout.width,\n    imageHeight,\n  };\n}\n\nexport function getVisiblePdfThumbnailItems({\n  layout,\n  scrollTop,\n  viewportHeight,\n  overscanPx,\n}: {\n  layout: PdfThumbnailLayout;\n  scrollTop: number;\n  viewportHeight: number;\n  overscanPx: number;\n}) {\n  if (layout.pageCount === 0) return [];\n\n  const window = getPdfThumbnailPixelWindow({\n    layout,\n    scrollTop,\n    viewportHeight,\n    overscanPx,\n  });\n  const firstVisiblePage = findPdfThumbnailPageByOffset(layout, window.top);\n  const lastVisiblePage = findPdfThumbnailPageByOffset(layout, window.bottom);\n  const items: PdfThumbnailLayoutItem[] = [];\n\n  for (\n    let pageNumber = firstVisiblePage;\n    pageNumber <= lastVisiblePage;\n    pageNumber += 1\n  ) {\n    const item = getPdfThumbnailLayoutItem(layout, pageNumber);\n    if (item) items.push(item);\n  }\n\n  return items;\n}\n\nexport function getPdfThumbnailPixelWindow({\n  layout,\n  scrollTop,\n  viewportHeight,\n  overscanPx,\n}: {\n  layout: PdfThumbnailLayout;\n  scrollTop: number;\n  viewportHeight: number;\n  overscanPx: number;\n}): PdfThumbnailPixelWindow {\n  const scrollHeight = normalizeSize(layout.totalHeight, 0);\n  const safeViewportHeight = normalizeSize(viewportHeight, 0);\n  const safeOverscanPx = normalizeSize(overscanPx, 0);\n  const windowHeight = safeViewportHeight + safeOverscanPx * 2;\n\n  if (windowHeight >= scrollHeight) {\n    return {\n      bottom: scrollHeight,\n      top: 0,\n    };\n  }\n\n  const scrollCenter =\n    Math.max(0, finiteNumber(scrollTop)) + safeViewportHeight / 2;\n  let top = scrollCenter - windowHeight / 2;\n  let bottom = top + windowHeight;\n\n  if (top < 0) {\n    top = 0;\n    bottom = windowHeight;\n  }\n  if (bottom > scrollHeight) {\n    bottom = scrollHeight;\n    top = scrollHeight - windowHeight;\n  }\n\n  return {\n    bottom: Math.ceil(Math.max(bottom, top)),\n    top: Math.floor(Math.max(0, top)),\n  };\n}\n\nexport function getPdfThumbnailRenderedWindow({\n  layout,\n  visibleItems,\n  viewportHeight,\n}: {\n  layout: PdfThumbnailLayout;\n  visibleItems: readonly PdfThumbnailLayoutItem[];\n  viewportHeight: number;\n}): PdfThumbnailRenderedWindow | null {\n  if (visibleItems.length === 0 || layout.totalHeight <= 0) return null;\n\n  const safeViewportHeight = Math.max(1, normalizeSize(viewportHeight, 1));\n  let renderedTop = layout.totalHeight;\n  let renderedBottom = 0;\n\n  for (const item of visibleItems) {\n    renderedTop = Math.min(renderedTop, item.top);\n    renderedBottom = Math.max(renderedBottom, item.top + item.height);\n  }\n\n  renderedTop = clamp(renderedTop, 0, layout.totalHeight);\n  renderedBottom = clamp(\n    Math.max(renderedTop, renderedBottom),\n    renderedTop,\n    layout.totalHeight,\n  );\n\n  const height = renderedBottom - renderedTop;\n\n  return {\n    afterHeight: Math.max(0, layout.totalHeight - renderedBottom),\n    beforeHeight: renderedTop,\n    height,\n    items: visibleItems.map((item) => ({\n      ...item,\n      windowTop: item.top - renderedTop,\n    })),\n    renderedBottom,\n    renderedTop,\n    stickyInset: -Math.max(0, height - safeViewportHeight),\n  };\n}\n\nexport function findPdfThumbnailPageByOffset(\n  layout: PdfThumbnailLayout,\n  offset: number,\n) {\n  const safeOffset = Math.max(0, offset);\n  let low = 1;\n  let high = layout.pageCount;\n  let result = 1;\n\n  while (low <= high) {\n    const pageNumber = Math.floor((low + high) / 2);\n    const item = getPdfThumbnailLayoutItem(layout, pageNumber);\n    if (!item) break;\n\n    if (item.top + item.height >= safeOffset) {\n      result = pageNumber;\n      high = pageNumber - 1;\n    } else {\n      low = pageNumber + 1;\n    }\n  }\n\n  return result;\n}\n\nexport function normalizeThumbnailPage(\n  page: number | null | undefined,\n  pageCount: number,\n): number | null {\n  return page != null &&\n    Number.isInteger(page) &&\n    page >= 1 &&\n    page <= pageCount\n    ? page\n    : null;\n}\n\nfunction getThumbnailImageHeight({\n  width,\n  metric,\n  shape,\n}: {\n  width: number;\n  metric: PdfThumbnailPageMetric | undefined;\n  shape: PdfThumbnailShape;\n}) {\n  if (shape === \"square\") return width;\n\n  if (\n    metric &&\n    Number.isFinite(metric.width) &&\n    Number.isFinite(metric.height) &&\n    metric.width > 0 &&\n    metric.height > 0\n  ) {\n    return width * (metric.height / metric.width);\n  }\n\n  return width * PDF_THUMBNAIL_DEFAULT_ASPECT;\n}\n\nfunction buildPrefixHeightDeltas({\n  pageCount,\n  width,\n  shape,\n  estimatedItemHeight,\n  metricByPageNumber,\n  labelAndGapHeight,\n}: {\n  pageCount: number;\n  width: number;\n  shape: PdfThumbnailShape;\n  estimatedItemHeight: number;\n  metricByPageNumber: ReadonlyMap<number, PdfThumbnailPageMetric>;\n  labelAndGapHeight: number;\n}) {\n  let prefixDeltaAfterPage = 0;\n  const prefixHeightDeltas: PdfThumbnailHeightDelta[] = [];\n\n  for (const [pageNumber, metric] of [...metricByPageNumber.entries()].sort(\n    ([left], [right]) => left - right,\n  )) {\n    if (normalizeThumbnailPage(pageNumber, pageCount) == null) continue;\n\n    const measuredItemHeight =\n      Math.ceil(getThumbnailImageHeight({ width, metric, shape })) +\n      labelAndGapHeight;\n    const delta = measuredItemHeight - estimatedItemHeight;\n    if (delta === 0) continue;\n\n    prefixDeltaAfterPage += delta;\n    prefixHeightDeltas.push({ pageNumber, prefixDeltaAfterPage });\n  }\n\n  return prefixHeightDeltas;\n}\n\nfunction getPrefixHeightDeltaBeforePage(\n  layout: PdfThumbnailLayout,\n  pageNumber: number,\n) {\n  let low = 0;\n  let high = layout.prefixHeightDeltas.length - 1;\n  let result = 0;\n\n  while (low <= high) {\n    const mid = Math.floor((low + high) / 2);\n    const delta = layout.prefixHeightDeltas[mid];\n\n    if (delta.pageNumber < pageNumber) {\n      result = delta.prefixDeltaAfterPage;\n      low = mid + 1;\n    } else {\n      high = mid - 1;\n    }\n  }\n\n  return result;\n}\n\nfunction normalizePageCount(pageCount: number) {\n  return Number.isInteger(pageCount) && pageCount > 0 ? pageCount : 0;\n}\n\nfunction normalizeWidth(width: number) {\n  return normalizeSize(width, 0);\n}\n\nfunction normalizeSize(value: number, fallback: number) {\n  return Number.isFinite(value) && value > 0 ? value : fallback;\n}\n\nfunction finiteNumber(value: number) {\n  return Number.isFinite(value) ? value : 0;\n}\n\nfunction clamp(value: number, min: number, max: number) {\n  return Math.min(max, Math.max(min, value));\n}\n",
      "type": "registry:ui",
      "target": "@ui/pdf-thumbnail-layout.ts"
    },
    {
      "path": "registry/new-york-v4/ui/use-pdf-thumbnail-document.ts",
      "content": "\"use client\";\n\nimport { useKeyedMountEffect } from \"@/hooks/use-keyed-mount-effect\";\nimport {\n  readPdfDocumentResource,\n  releasePdfDocumentResource,\n  retainPdfDocumentResource,\n} from \"@/lib/pdf-document-resource\";\nimport type { ViewerResource } from \"@/lib/viewer-resource\";\n\nimport { joinEffectKey } from \"@/lib/effect-key\";\n\nexport function usePdfThumbnailDocument(resource: ViewerResource) {\n  const content = resource.content;\n  const doc = readPdfDocumentResource(content);\n\n  const retainEffectKey = joinEffectKey([\n    \"pdf-thumbnail-document\",\n    content.sourceKind,\n    content.key,\n    doc,\n  ]);\n  useKeyedMountEffect(retainEffectKey, () => {\n    retainPdfDocumentResource(content, doc);\n    return () => releasePdfDocumentResource(content, doc);\n  });\n\n  return doc;\n}\n",
      "type": "registry:ui",
      "target": "@ui/use-pdf-thumbnail-document.ts"
    },
    {
      "path": "registry/new-york-v4/ui/use-pdf-thumbnail-page-metrics.ts",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { useKeyedMountEffect } from \"@/hooks/use-keyed-mount-effect\";\nimport { getPdfPageResource } from \"@/lib/pdf-document-resource\";\nimport type { PdfDocumentProxy } from \"@/lib/pdf-document-types\";\n\nimport { normalizeThumbnailPage } from \"./pdf-thumbnail-layout\";\nimport { joinEffectKey } from \"@/lib/effect-key\";\n\nexport const PDF_THUMBNAIL_PAGE_METRIC_CONCURRENCY = 4;\n\nexport interface PdfThumbnailPageMetric {\n  pageNumber: number;\n  width: number;\n  height: number;\n}\n\nexport interface PdfThumbnailPageMetrics {\n  pageCount: number;\n  metricByPageNumber: ReadonlyMap<number, PdfThumbnailPageMetric>;\n  requestPageMetrics: (pageNumbers: Iterable<number>) => void;\n  status: \"idle\" | \"loading\";\n}\n\ninterface MetricControllerState {\n  documentKey: unknown;\n  pageCount: number;\n  metricByPageNumber: ReadonlyMap<number, PdfThumbnailPageMetric>;\n  queuedPageNumbers: readonly number[];\n  loadingPageNumbers: ReadonlySet<number>;\n  error: unknown;\n}\n\ntype MetricAction =\n  | { type: \"reset\"; documentKey: unknown; pageCount: number }\n  | {\n      type: \"enqueue\";\n      documentKey: unknown;\n      pageCount: number;\n      pageNumbers: readonly number[];\n    }\n  | { type: \"start\"; documentKey: unknown; pageNumbers: readonly number[] }\n  | { type: \"resolve\"; documentKey: unknown; metric: PdfThumbnailPageMetric }\n  | { type: \"reject\"; documentKey: unknown; error: unknown };\n\nexport function usePdfThumbnailPageMetrics(\n  doc: PdfDocumentProxy,\n  resetKey: unknown,\n): PdfThumbnailPageMetrics {\n  const workerSequenceRef = React.useRef(0);\n  const [state, dispatch] = React.useReducer(\n    metricControllerReducer,\n    undefined,\n    () => createMetricControllerState(resetKey, doc.numPages),\n  );\n  const visibleState = Object.is(state.documentKey, resetKey)\n    ? state\n    : createMetricControllerState(resetKey, doc.numPages);\n  const status = getMetricStatus(visibleState);\n\n  const resetEffectKey = joinEffectKey([\"reset\", doc, resetKey]);\n  useKeyedMountEffect(resetEffectKey, () => {\n    workerSequenceRef.current += 1;\n    dispatch({\n      type: \"reset\",\n      documentKey: resetKey,\n      pageCount: doc.numPages,\n    });\n  });\n\n  const requestPageMetrics = React.useCallback(\n    (pageNumbers: Iterable<number>) => {\n      const requestedPageNumbers = Array.from(pageNumbers)\n        .map((pageNumber) => normalizeThumbnailPage(pageNumber, doc.numPages))\n        .filter((pageNumber): pageNumber is number => pageNumber != null);\n\n      if (requestedPageNumbers.length === 0) return;\n\n      dispatch({\n        type: \"enqueue\",\n        documentKey: resetKey,\n        pageCount: doc.numPages,\n        pageNumbers: requestedPageNumbers,\n      });\n    },\n    [doc.numPages, resetKey],\n  );\n\n  const loadEffectKey = joinEffectKey([\n    \"load\",\n    doc,\n    resetKey,\n    state.documentKey,\n    state.error,\n    state.loadingPageNumbers,\n    state.queuedPageNumbers,\n  ]);\n  useKeyedMountEffect(loadEffectKey, () => {\n    if (!Object.is(state.documentKey, resetKey)) return;\n    if (state.error) return;\n\n    const availableSlotCount =\n      PDF_THUMBNAIL_PAGE_METRIC_CONCURRENCY - state.loadingPageNumbers.size;\n    if (availableSlotCount <= 0) return;\n\n    const startingPageNumbers = state.queuedPageNumbers.slice(\n      0,\n      availableSlotCount,\n    );\n    if (startingPageNumbers.length === 0) return;\n\n    const sequence = workerSequenceRef.current;\n    dispatch({\n      type: \"start\",\n      documentKey: resetKey,\n      pageNumbers: startingPageNumbers,\n    });\n\n    for (const pageNumber of startingPageNumbers) {\n      void getPdfPageResource(doc, pageNumber, { retainRejected: true })\n        .then((page) => {\n          if (workerSequenceRef.current !== sequence) return;\n\n          const viewport = page.getViewport({ scale: 1 });\n          dispatch({\n            type: \"resolve\",\n            documentKey: resetKey,\n            metric: {\n              pageNumber,\n              width: viewport.width,\n              height: viewport.height,\n            },\n          });\n        })\n        .catch((error: unknown) => {\n          if (workerSequenceRef.current !== sequence) return;\n          dispatch({ type: \"reject\", documentKey: resetKey, error });\n        });\n    }\n  });\n\n  if (visibleState.error) throw visibleState.error;\n\n  return {\n    pageCount: visibleState.pageCount,\n    metricByPageNumber: visibleState.metricByPageNumber,\n    requestPageMetrics,\n    status,\n  };\n}\n\nfunction metricControllerReducer(\n  state: MetricControllerState,\n  action: MetricAction,\n): MetricControllerState {\n  switch (action.type) {\n    case \"reset\":\n      return createMetricControllerState(action.documentKey, action.pageCount);\n    case \"enqueue\":\n      return enqueuePageMetrics(state, action);\n    case \"start\":\n      return startPageMetrics(state, action);\n    case \"resolve\":\n      return resolvePageMetric(state, action);\n    case \"reject\":\n      if (!Object.is(state.documentKey, action.documentKey)) return state;\n      return {\n        ...state,\n        queuedPageNumbers: [],\n        loadingPageNumbers: new Set(),\n        error: action.error,\n      };\n  }\n}\n\nfunction createMetricControllerState(\n  documentKey: unknown,\n  pageCount: number,\n): MetricControllerState {\n  return {\n    documentKey,\n    pageCount,\n    metricByPageNumber: new Map(),\n    queuedPageNumbers: [],\n    loadingPageNumbers: new Set(),\n    error: null,\n  };\n}\n\nfunction enqueuePageMetrics(\n  state: MetricControllerState,\n  action: Extract<MetricAction, { type: \"enqueue\" }>,\n): MetricControllerState {\n  const baseState = Object.is(state.documentKey, action.documentKey)\n    ? state\n    : createMetricControllerState(action.documentKey, action.pageCount);\n  const queuedPageNumberSet = new Set(baseState.queuedPageNumbers);\n  const queuedPageNumbers = [...baseState.queuedPageNumbers];\n\n  for (const pageNumber of action.pageNumbers) {\n    if (baseState.metricByPageNumber.has(pageNumber)) continue;\n    if (baseState.loadingPageNumbers.has(pageNumber)) continue;\n    if (queuedPageNumberSet.has(pageNumber)) continue;\n\n    queuedPageNumberSet.add(pageNumber);\n    queuedPageNumbers.push(pageNumber);\n  }\n\n  if (queuedPageNumbers.length === baseState.queuedPageNumbers.length) {\n    return baseState;\n  }\n\n  return {\n    ...baseState,\n    queuedPageNumbers,\n  };\n}\n\nfunction startPageMetrics(\n  state: MetricControllerState,\n  action: Extract<MetricAction, { type: \"start\" }>,\n): MetricControllerState {\n  if (!Object.is(state.documentKey, action.documentKey)) return state;\n\n  const startingPageNumberSet = new Set(action.pageNumbers);\n  const queuedPageNumbers = state.queuedPageNumbers.filter(\n    (pageNumber) => !startingPageNumberSet.has(pageNumber),\n  );\n  const loadingPageNumbers = new Set(state.loadingPageNumbers);\n\n  for (const pageNumber of action.pageNumbers) {\n    if (state.metricByPageNumber.has(pageNumber)) continue;\n    loadingPageNumbers.add(pageNumber);\n  }\n\n  return {\n    ...state,\n    queuedPageNumbers,\n    loadingPageNumbers,\n  };\n}\n\nfunction resolvePageMetric(\n  state: MetricControllerState,\n  action: Extract<MetricAction, { type: \"resolve\" }>,\n): MetricControllerState {\n  if (!Object.is(state.documentKey, action.documentKey)) return state;\n\n  const loadingPageNumbers = new Set(state.loadingPageNumbers);\n  loadingPageNumbers.delete(action.metric.pageNumber);\n\n  const metricByPageNumber = new Map(state.metricByPageNumber);\n  metricByPageNumber.set(action.metric.pageNumber, action.metric);\n\n  return {\n    ...state,\n    metricByPageNumber,\n    loadingPageNumbers,\n  };\n}\n\nfunction getMetricStatus(state: MetricControllerState): \"idle\" | \"loading\" {\n  return state.queuedPageNumbers.length > 0 || state.loadingPageNumbers.size > 0\n    ? \"loading\"\n    : \"idle\";\n}\n",
      "type": "registry:ui",
      "target": "@ui/use-pdf-thumbnail-page-metrics.ts"
    },
    {
      "path": "registry/new-york-v4/ui/use-pdf-thumbnail-window.ts",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { useKeyedMountEffect } from \"@/hooks/use-keyed-mount-effect\";\n\nimport {\n  getVisiblePdfThumbnailItems,\n  type PdfThumbnailLayout,\n  type PdfThumbnailLayoutItem,\n} from \"./pdf-thumbnail-layout\";\nimport { joinEffectKey } from \"@/lib/effect-key\";\n\ninterface PdfThumbnailWindowMetrics {\n  scrollTop: number;\n  viewportHeight: number;\n}\n\nexport interface PdfThumbnailWindow {\n  viewportHeight: number;\n  visibleItems: readonly PdfThumbnailLayoutItem[];\n  totalHeight: number;\n}\n\nexport function usePdfThumbnailWindow({\n  layout,\n  viewportRef,\n  overscanPx,\n  initialViewportHeight,\n}: {\n  layout: PdfThumbnailLayout;\n  viewportRef: React.RefObject<HTMLElement | null>;\n  overscanPx: number;\n  initialViewportHeight: number;\n}): PdfThumbnailWindow {\n  const [metrics, setMetrics] = React.useState<PdfThumbnailWindowMetrics>({\n    scrollTop: 0,\n    viewportHeight: initialViewportHeight,\n  });\n  const metricsRef = React.useRef(metrics);\n\n  const setMeasuredMetrics = React.useCallback(\n    (next: PdfThumbnailWindowMetrics) => {\n      const current = metricsRef.current;\n      if (\n        current.scrollTop === next.scrollTop &&\n        current.viewportHeight === next.viewportHeight\n      ) {\n        return;\n      }\n\n      metricsRef.current = next;\n      setMetrics(next);\n    },\n    [],\n  );\n\n  const viewportEffectKey = joinEffectKey([\n    \"pdf-thumbnail-window\",\n    initialViewportHeight,\n    viewportRef,\n  ]);\n  useKeyedMountEffect(viewportEffectKey, () => {\n    const viewport = viewportRef.current;\n    if (!viewport) return;\n\n    let frame = 0;\n    const read = () => {\n      const scrollTop =\n        Number.isFinite(viewport.scrollTop) && viewport.scrollTop > 0\n          ? viewport.scrollTop\n          : 0;\n      const viewportHeight =\n        Number.isFinite(viewport.clientHeight) && viewport.clientHeight > 0\n          ? viewport.clientHeight\n          : initialViewportHeight;\n\n      setMeasuredMetrics({ scrollTop, viewportHeight });\n    };\n    const scheduleRead = () => {\n      if (frame !== 0) return;\n      frame = window.requestAnimationFrame(() => {\n        frame = 0;\n        read();\n      });\n    };\n    const observer =\n      typeof ResizeObserver !== \"undefined\"\n        ? new ResizeObserver(scheduleRead)\n        : null;\n\n    read();\n    observer?.observe(viewport);\n    viewport.addEventListener(\"scroll\", scheduleRead, { passive: true });\n\n    return () => {\n      viewport.removeEventListener(\"scroll\", scheduleRead);\n      observer?.disconnect();\n      if (frame !== 0) window.cancelAnimationFrame(frame);\n    };\n  });\n\n  return React.useMemo(\n    () => ({\n      viewportHeight: metrics.viewportHeight,\n      visibleItems: getVisiblePdfThumbnailItems({\n        layout,\n        scrollTop: metrics.scrollTop,\n        viewportHeight: metrics.viewportHeight,\n        overscanPx,\n      }),\n      totalHeight: layout.totalHeight,\n    }),\n    [layout, metrics, overscanPx],\n  );\n}\n",
      "type": "registry:ui",
      "target": "@ui/use-pdf-thumbnail-window.ts"
    },
    {
      "path": "registry/new-york-v4/ui/use-thumbnail-rail-follow.ts",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { useKeyedMountEffect } from \"@/hooks/use-keyed-mount-effect\";\n\nimport {\n  getPdfThumbnailLayoutItem,\n  normalizeThumbnailPage,\n  type PdfThumbnailLayout,\n} from \"./pdf-thumbnail-layout\";\nimport { joinEffectKey } from \"@/lib/effect-key\";\n\nexport const THUMBNAIL_FOLLOW_MARGIN = 24;\n\ntype ThumbnailFollowSuspension =\n  | { kind: \"none\" }\n  | { kind: \"pointer\" }\n  | { kind: \"user-scroll\" }\n  | { kind: \"rail-navigation\"; targetPage: number };\n\ninterface ThumbnailFollowState {\n  suspension: ThumbnailFollowSuspension;\n  currentPage: number | null;\n  programmaticScrollCount: number;\n}\n\nexport function useThumbnailRailFollow({\n  currentPage,\n  layout,\n  viewportRef,\n  resetKey,\n}: {\n  currentPage: number | null | undefined;\n  layout: PdfThumbnailLayout;\n  viewportRef: React.RefObject<HTMLElement | null>;\n  resetKey: unknown;\n}) {\n  const stateRef = React.useRef<ThumbnailFollowState>({\n    suspension: { kind: \"none\" },\n    currentPage: null,\n    programmaticScrollCount: 0,\n  });\n\n  const scrollPageIntoView = React.useCallback(\n    (page: number, behavior: ScrollBehavior) => {\n      const normalizedPage = normalizeThumbnailPage(page, layout.pageCount);\n      if (normalizedPage == null) return false;\n\n      const viewport = viewportRef.current;\n      if (!viewport) return false;\n\n      const item = getPdfThumbnailLayoutItem(layout, normalizedPage);\n      if (!item) return false;\n\n      const top = item.top - viewport.scrollTop;\n      const bottom = top + item.height;\n      const minTop = THUMBNAIL_FOLLOW_MARGIN;\n      const maxBottom = viewport.clientHeight - THUMBNAIL_FOLLOW_MARGIN;\n      const isAtDocumentStart =\n        item.top <= THUMBNAIL_FOLLOW_MARGIN &&\n        viewport.scrollTop <= THUMBNAIL_FOLLOW_MARGIN;\n\n      if ((top >= minTop || isAtDocumentStart) && bottom <= maxBottom) {\n        return false;\n      }\n\n      const maxScrollTop = Math.max(\n        0,\n        layout.totalHeight - viewport.clientHeight,\n      );\n      const targetTop = Math.min(\n        maxScrollTop,\n        Math.max(0, item.top - viewport.clientHeight / 2 + item.height / 2),\n      );\n\n      if (stateRef.current.suspension.kind !== \"rail-navigation\") {\n        stateRef.current.programmaticScrollCount += 1;\n      }\n      viewport.scrollTo?.({ top: targetTop, behavior });\n      return true;\n    },\n    [layout, viewportRef],\n  );\n\n  const followNow = React.useCallback(() => {\n    const page = normalizeThumbnailPage(currentPage, layout.pageCount);\n    if (page == null) return;\n\n    const state = stateRef.current;\n    if (state.suspension.kind !== \"none\") return;\n\n    scrollPageIntoView(page, \"auto\");\n  }, [currentPage, layout.pageCount, scrollPageIntoView]);\n\n  useKeyedMountEffect(joinEffectKey([\"thumbnail-reset\", resetKey]), () => {\n    const state = stateRef.current;\n    state.suspension = { kind: \"none\" };\n    state.currentPage = null;\n    state.programmaticScrollCount = 0;\n  });\n\n  useKeyedMountEffect(\n    joinEffectKey([\"thumbnail-follow\", followNow, resetKey]),\n    () => {\n      const page = normalizeThumbnailPage(currentPage, layout.pageCount);\n      const state = stateRef.current;\n      const pageChanged = state.currentPage !== page;\n\n      state.currentPage = page;\n      if (state.suspension.kind === \"rail-navigation\") {\n        if (pageChanged || page === state.suspension.targetPage) {\n          state.suspension = { kind: \"none\" };\n          followNow();\n        }\n        return;\n      }\n\n      if (state.suspension.kind === \"user-scroll\" && pageChanged) {\n        state.suspension = { kind: \"none\" };\n      }\n\n      followNow();\n    },\n  );\n\n  const onPointerEnter = React.useCallback(() => {\n    const state = stateRef.current;\n    if (\n      state.suspension.kind === \"user-scroll\" ||\n      state.suspension.kind === \"rail-navigation\"\n    ) {\n      return;\n    }\n    state.suspension = { kind: \"pointer\" };\n  }, []);\n\n  const onPointerLeave = React.useCallback(() => {\n    const state = stateRef.current;\n    if (state.suspension.kind !== \"pointer\") return;\n    state.suspension = { kind: \"none\" };\n    followNow();\n  }, [followNow]);\n\n  const onPageActivate = React.useCallback(\n    (pageNumber: number) => {\n      const targetPage = normalizeThumbnailPage(pageNumber, layout.pageCount);\n      if (targetPage == null) return;\n\n      const state = stateRef.current;\n      state.suspension =\n        state.currentPage === targetPage\n          ? { kind: \"none\" }\n          : { kind: \"rail-navigation\", targetPage };\n      scrollPageIntoView(targetPage, \"smooth\");\n    },\n    [layout.pageCount, scrollPageIntoView],\n  );\n\n  const onScroll = React.useCallback(() => {\n    const state = stateRef.current;\n    if (state.programmaticScrollCount > 0) {\n      state.programmaticScrollCount -= 1;\n      return;\n    }\n\n    if (state.suspension.kind === \"rail-navigation\") return;\n\n    state.suspension = { kind: \"user-scroll\" };\n  }, []);\n\n  return {\n    onPageActivate,\n    onPointerEnter,\n    onPointerLeave,\n    onScroll,\n  };\n}\n",
      "type": "registry:ui",
      "target": "@ui/use-thumbnail-rail-follow.ts"
    },
    {
      "path": "registry/new-york-v4/ui/pdf-thumbnail-rail.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport type { PdfDocumentProxy } from \"@/lib/pdf-document-types\";\nimport { cn } from \"@/lib/utils\";\n\nimport { PdfThumbnailItem } from \"./pdf-thumbnail-item\";\nimport {\n  getPdfThumbnailRenderedWindow,\n  normalizeThumbnailPage,\n  type PdfThumbnailLayout,\n  type PdfThumbnailLayoutItem,\n} from \"./pdf-thumbnail-layout\";\nimport type { PdfRenderedPageCache } from \"./pdf-viewer-render-cache\";\n\nexport function getPdfThumbnailItemId(pageNumber: number) {\n  return `pdf-thumbnail-page-${pageNumber}`;\n}\n\nexport function PdfThumbnailRailViewport({\n  doc,\n  documentKey,\n  isRenderingSuspended = false,\n  layout,\n  visibleItems,\n  viewportHeight,\n  currentPage,\n  viewportRef,\n  onSelectPage,\n  onPageActivate,\n  onPointerEnter,\n  onPointerLeave,\n  onScroll,\n  className,\n  renderCache,\n}: {\n  doc: PdfDocumentProxy;\n  documentKey: string;\n  isRenderingSuspended?: boolean;\n  layout: PdfThumbnailLayout;\n  visibleItems: readonly PdfThumbnailLayoutItem[];\n  viewportHeight: number;\n  currentPage: number | null | undefined;\n  viewportRef: React.RefObject<HTMLDivElement | null>;\n  onSelectPage?: (pageNumber: number) => void;\n  onPageActivate?: (pageNumber: number) => void;\n  onPointerEnter?: () => void;\n  onPointerLeave?: () => void;\n  onScroll?: () => void;\n  className?: string;\n  renderCache?: PdfRenderedPageCache;\n}) {\n  const activePage = normalizeThumbnailPage(currentPage, layout.pageCount);\n  const handleKeyDown = React.useCallback(\n    (event: React.KeyboardEvent<HTMLElement>) => {\n      const page = activePage ?? 1;\n      const nextPage = getKeyboardTargetPage({\n        key: event.key,\n        page,\n        pageCount: layout.pageCount,\n      });\n\n      if (nextPage == null) return;\n      event.preventDefault();\n      onPageActivate?.(nextPage);\n      onSelectPage?.(nextPage);\n    },\n    [activePage, layout.pageCount, onPageActivate, onSelectPage],\n  );\n  const handleSelectPage = React.useCallback(\n    (pageNumber: number) => {\n      onPageActivate?.(pageNumber);\n      onSelectPage?.(pageNumber);\n    },\n    [onPageActivate, onSelectPage],\n  );\n  const renderedWindow = React.useMemo(\n    () =>\n      getPdfThumbnailRenderedWindow({\n        layout,\n        visibleItems,\n        viewportHeight,\n      }),\n    [layout, visibleItems, viewportHeight],\n  );\n\n  return (\n    <nav\n      ref={viewportRef}\n      data-slot=\"pdf-viewer-thumbnails\"\n      aria-label=\"PDF pages\"\n      tabIndex={0}\n      onKeyDown={handleKeyDown}\n      onPointerEnter={onPointerEnter}\n      onPointerLeave={onPointerLeave}\n      onScroll={onScroll}\n      className={cn(\n        \"bg-sidebar text-sidebar-foreground h-full overflow-auto p-2\",\n        className,\n      )}\n      style={{ overflowAnchor: \"none\" }}\n    >\n      <div\n        data-slot=\"pdf-thumbnail-document\"\n        className=\"relative w-full\"\n        style={{\n          contain: \"layout style\",\n          height: layout.totalHeight,\n        }}\n      >\n        {renderedWindow ? (\n          <>\n            <div\n              aria-hidden\n              data-slot=\"pdf-thumbnail-window-before\"\n              style={{\n                contain: \"layout size\",\n                height: renderedWindow.beforeHeight,\n              }}\n            />\n            <div\n              data-slot=\"pdf-thumbnail-sticky-window\"\n              className=\"sticky w-full\"\n              style={{\n                bottom: renderedWindow.stickyInset,\n                contain: \"layout style inline-size\",\n                height: renderedWindow.height,\n                isolation: \"isolate\",\n                top: renderedWindow.stickyInset,\n              }}\n            >\n              <ol\n                data-slot=\"pdf-thumbnail-window\"\n                className=\"relative w-full\"\n                style={{\n                  contain: \"layout style\",\n                  height: renderedWindow.height,\n                }}\n              >\n                {renderedWindow.items.map((item) => (\n                  <li\n                    key={item.pageNumber}\n                    data-index={item.pageIndex}\n                    className=\"absolute top-0 left-0 flex w-full justify-center pb-2\"\n                    style={{\n                      height: item.height,\n                      transform: `translateY(${item.windowTop}px)`,\n                    }}\n                  >\n                    <PdfThumbnailItem\n                      doc={doc}\n                      documentKey={documentKey}\n                      item={item}\n                      active={activePage === item.pageNumber}\n                      isRenderingSuspended={isRenderingSuspended}\n                      itemId={getPdfThumbnailItemId(item.pageNumber)}\n                      onSelectPage={handleSelectPage}\n                      renderCache={renderCache}\n                    />\n                  </li>\n                ))}\n              </ol>\n            </div>\n            <div\n              aria-hidden\n              data-slot=\"pdf-thumbnail-window-after\"\n              style={{\n                contain: \"layout size\",\n                height: renderedWindow.afterHeight,\n              }}\n            />\n          </>\n        ) : null}\n      </div>\n    </nav>\n  );\n}\n\nfunction getKeyboardTargetPage({\n  key,\n  page,\n  pageCount,\n}: {\n  key: string;\n  page: number;\n  pageCount: number;\n}) {\n  switch (key) {\n    case \"ArrowUp\":\n      return Math.max(1, page - 1);\n    case \"ArrowDown\":\n      return Math.min(pageCount, page + 1);\n    case \"Home\":\n      return 1;\n    case \"End\":\n      return pageCount;\n    default:\n      return null;\n  }\n}\n",
      "type": "registry:ui",
      "target": "@ui/pdf-thumbnail-rail.tsx"
    },
    {
      "path": "registry/new-york-v4/ui/pdf-thumbnail-item.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport type { PdfDocumentProxy } from \"@/lib/pdf-document-types\";\nimport { cn } from \"@/lib/utils\";\n\nimport {\n  PdfThumbnailCanvas,\n  PdfThumbnailSkeleton,\n} from \"./pdf-thumbnail-canvas\";\nimport type { PdfThumbnailLayoutItem } from \"./pdf-thumbnail-layout\";\nimport type { PdfRenderedPageCache } from \"./pdf-viewer-render-cache\";\n\nexport function PdfThumbnailItem({\n  doc,\n  documentKey,\n  item,\n  active,\n  isRenderingSuspended = false,\n  itemId,\n  onSelectPage,\n  renderCache,\n}: {\n  doc: PdfDocumentProxy;\n  documentKey: string;\n  item: PdfThumbnailLayoutItem;\n  active: boolean;\n  isRenderingSuspended?: boolean;\n  itemId: string;\n  onSelectPage?: (pageNumber: number) => void;\n  renderCache?: PdfRenderedPageCache;\n}) {\n  return (\n    <button\n      id={itemId}\n      type=\"button\"\n      aria-label={`Page ${item.pageNumber}`}\n      aria-current={active ? \"page\" : undefined}\n      data-active={active}\n      data-page-number={item.pageNumber}\n      onClick={() => onSelectPage?.(item.pageNumber)}\n      className=\"flex flex-shrink-0 flex-col items-center gap-1 outline-none\"\n    >\n      <div\n        className={cn(\n          \"overflow-hidden rounded-sm bg-white ring-2 transition-shadow\",\n          active ? \"ring-primary\" : \"ring-sidebar-border\",\n        )}\n        style={{ width: item.imageWidth, height: item.imageHeight }}\n      >\n        <React.Suspense fallback={<PdfThumbnailSkeleton />}>\n          <PdfThumbnailCanvas\n            doc={doc}\n            documentKey={documentKey}\n            isRenderingSuspended={isRenderingSuspended}\n            pageNumber={item.pageNumber}\n            renderCache={renderCache}\n            width={item.imageWidth}\n          />\n        </React.Suspense>\n      </div>\n      <span\n        className={cn(\n          \"text-[10px] tabular-nums\",\n          active\n            ? \"text-sidebar-foreground font-semibold\"\n            : \"text-sidebar-foreground/70\",\n        )}\n      >\n        {item.pageNumber}\n      </span>\n    </button>\n  );\n}\n",
      "type": "registry:ui",
      "target": "@ui/pdf-thumbnail-item.tsx"
    },
    {
      "path": "registry/new-york-v4/ui/pdf-thumbnail-canvas.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport type { PdfDocumentProxy } from \"@/lib/pdf-document-types\";\nimport { readPdfPageResource } from \"@/lib/pdf-document-resource\";\n\nimport { getPdfCanvasPixelSize } from \"./pdf-viewer-canvas\";\nimport {\n  readPdfRenderedPageCache,\n  writePdfRenderedPageCache,\n  type PdfRenderedPageCache,\n  type PdfRenderedPageSignature,\n} from \"./pdf-viewer-render-cache\";\nimport { toPdfRenderFailedError } from \"./pdf-viewer-render-error\";\n\nconst PDF_THUMBNAIL_MAX_DEVICE_PIXEL_RATIO = 1;\n\nexport function PdfThumbnailCanvas({\n  doc,\n  documentKey,\n  isRenderingSuspended = false,\n  pageNumber,\n  renderCache,\n  width,\n}: {\n  doc: PdfDocumentProxy;\n  documentKey: string;\n  isRenderingSuspended?: boolean;\n  pageNumber: number;\n  renderCache?: PdfRenderedPageCache;\n  width: number;\n}) {\n  const page = readPdfPageResource(doc, pageNumber);\n  const baseViewport = React.useMemo(\n    () => page.getViewport({ scale: 1 }),\n    [page],\n  );\n  const viewport = React.useMemo(\n    () => page.getViewport({ scale: width / baseViewport.width }),\n    [baseViewport.width, page, width],\n  );\n  const dpr = Math.min(\n    (typeof window !== \"undefined\" ? window.devicePixelRatio : 1) || 1,\n    PDF_THUMBNAIL_MAX_DEVICE_PIXEL_RATIO,\n  );\n  const [renderError, setRenderError] = React.useState<unknown>(null);\n  if (renderError) throw renderError;\n\n  const canvasRef = React.useCallback(\n    (canvas: HTMLCanvasElement | null) => {\n      if (!canvas) return;\n      const context = canvas.getContext(\"2d\");\n      if (!context) {\n        setRenderError(\n          toPdfRenderFailedError(new Error(\"Canvas 2D context unavailable.\")),\n        );\n        return;\n      }\n\n      const renderSignature = {\n        documentKey,\n        pageNumber,\n        scale: width / baseViewport.width,\n        rotation: 0,\n        devicePixelRatio: dpr,\n        viewportWidth: viewport.width,\n        viewportHeight: viewport.height,\n      } satisfies PdfRenderedPageSignature;\n      const canvasWidth = getPdfCanvasPixelSize(viewport.width, dpr);\n      const canvasHeight = getPdfCanvasPixelSize(viewport.height, dpr);\n      const cachedPage = readPdfRenderedPageCache(renderCache, renderSignature);\n\n      resizeCanvas(canvas, canvasWidth, canvasHeight);\n      if (isRenderingSuspended) {\n        markCanvasRenderStatus(canvas, renderSignature, \"suspended\");\n        return;\n      }\n\n      if (cachedPage) {\n        drawCanvasImage(context, cachedPage.canvas, canvasWidth, canvasHeight);\n        markCanvasRenderStatus(canvas, renderSignature, \"rendered\", \"cache\");\n        return;\n      }\n      markCanvasRenderStatus(canvas, renderSignature, \"pending\");\n\n      let task: ReturnType<typeof page.render>;\n      try {\n        task = page.render({\n          canvas,\n          canvasContext: context,\n          viewport,\n          transform: dpr !== 1 ? [dpr, 0, 0, dpr, 0, 0] : undefined,\n        });\n      } catch (error) {\n        setRenderError(toPdfRenderFailedError(error));\n        return;\n      }\n\n      let isActive = true;\n      let didFinishPdfRender = false;\n      task.promise.then(\n        () => {\n          if (!isActive) return;\n          didFinishPdfRender = true;\n          writePdfRenderedPageCache({\n            cache: renderCache,\n            rendered: renderSignature,\n            sourceCanvas: canvas,\n          });\n          markCanvasRenderStatus(canvas, renderSignature, \"rendered\", \"pdfjs\");\n        },\n        (error) => {\n          if (!isActive) return;\n          didFinishPdfRender = true;\n          markCanvasRenderStatus(canvas, renderSignature, \"failed\");\n          setRenderError(toPdfRenderFailedError(error));\n        },\n      );\n\n      return () => {\n        isActive = false;\n        if (!didFinishPdfRender) {\n          task.cancel();\n          markCanvasRenderStatus(canvas, renderSignature, \"cancelled\");\n        }\n      };\n    },\n    [\n      baseViewport.width,\n      documentKey,\n      dpr,\n      isRenderingSuspended,\n      page,\n      pageNumber,\n      renderCache,\n      viewport,\n      width,\n    ],\n  );\n\n  return (\n    <canvas\n      ref={canvasRef}\n      style={{ width: viewport.width, height: viewport.height }}\n      className=\"block\"\n    />\n  );\n}\n\nfunction resizeCanvas(\n  canvas: HTMLCanvasElement,\n  width: number,\n  height: number,\n) {\n  if (canvas.width !== width) canvas.width = width;\n  if (canvas.height !== height) canvas.height = height;\n}\n\nfunction drawCanvasImage(\n  context: CanvasRenderingContext2D,\n  image: HTMLCanvasElement,\n  width: number,\n  height: number,\n) {\n  if (typeof context.setTransform === \"function\") {\n    context.setTransform(1, 0, 0, 1, 0, 0);\n  }\n  if (typeof context.drawImage === \"function\") {\n    context.drawImage(image, 0, 0, width, height);\n  }\n}\n\nfunction markCanvasRenderStatus(\n  canvas: HTMLCanvasElement,\n  rendered: PdfRenderedPageSignature,\n  status: \"pending\" | \"rendered\" | \"cancelled\" | \"failed\" | \"suspended\",\n  source?: \"cache\" | \"pdfjs\",\n) {\n  canvas.dataset.pdfPageNumber = String(rendered.pageNumber);\n  canvas.dataset.pdfRenderStatus = status;\n  if (source) {\n    canvas.dataset.pdfRenderSource = source;\n  } else {\n    delete canvas.dataset.pdfRenderSource;\n  }\n}\n\nexport function PdfThumbnailSkeleton() {\n  return <div className=\"bg-muted aspect-[3/4] w-full\" />;\n}\n",
      "type": "registry:ui",
      "target": "@ui/pdf-thumbnail-canvas.tsx"
    },
    {
      "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/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/viewer-download.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { Download } from \"lucide-react\";\n\nimport { cn } from \"@/lib/utils\";\nimport {\n  ViewerDownloadError,\n  type ViewerDownloadAction,\n  type ViewerDownloadPayload,\n} from \"@/lib/viewer-download-actions\";\n\nimport { Spinner } from \"@/components/ui/spinner\";\n\nimport { Button, buttonVariants } from \"./button\";\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuTrigger,\n} from \"./dropdown-menu\";\nimport { useKeyedMountEffect } from \"@/hooks/use-keyed-mount-effect\";\nimport { joinEffectKey } from \"@/lib/effect-key\";\n\nexport interface TriggerViewerDownloadOptions {\n  signal?: AbortSignal;\n}\n\nexport async function triggerViewerDownload(\n  action: ViewerDownloadAction,\n  options?: TriggerViewerDownloadOptions,\n): Promise<void> {\n  if (action.isDisabled) {\n    throw new ViewerDownloadError({\n      actionId: action.id,\n      kind: \"disabled\",\n      message: \"This download is disabled.\",\n    });\n  }\n\n  let payload: ViewerDownloadPayload;\n  try {\n    payload = await action.getPayload(options);\n  } catch (error) {\n    if (isAbortError(error)) {\n      throw new ViewerDownloadError({\n        actionId: action.id,\n        kind: \"aborted\",\n        message: \"Download was cancelled.\",\n        cause: error,\n      });\n    }\n    throw new ViewerDownloadError({\n      actionId: action.id,\n      kind: \"payload_failed\",\n      message: \"Could not prepare this download.\",\n      cause: error,\n    });\n  }\n\n  if (payload.kind === \"none\") return;\n  if (payload.kind === \"href\") {\n    try {\n      clickDownload(payload.href, action.fileName);\n    } catch (error) {\n      throw new ViewerDownloadError({\n        actionId: action.id,\n        kind: \"unsupported\",\n        message: \"Could not start this download.\",\n        cause: error,\n      });\n    }\n    return;\n  }\n\n  try {\n    const blob =\n      payload.kind === \"blob\"\n        ? payload.blob\n        : new Blob([payload.text], {\n            type: payload.mimeType ?? \"text/plain;charset=utf-8\",\n          });\n    const url = URL.createObjectURL(blob);\n    try {\n      clickDownload(url, action.fileName);\n    } finally {\n      URL.revokeObjectURL(url);\n    }\n  } catch (error) {\n    throw new ViewerDownloadError({\n      actionId: action.id,\n      kind: \"unsupported\",\n      message: \"Could not start this download.\",\n      cause: error,\n    });\n  }\n}\n\nexport type ViewerDownloadErrorHandler = (\n  error: ViewerDownloadError,\n  action: ViewerDownloadAction,\n) => void;\n\nexport interface ViewerDownloadTrigger {\n  pendingActionId: string | null;\n  triggerDownload: (action: ViewerDownloadAction) => void;\n}\n\nexport interface ViewerDownloadTriggerOptions {\n  /** Reports non-aborted action failures; visible failure UI belongs to consumers. */\n  onError?: ViewerDownloadErrorHandler;\n  resetKey?: unknown;\n}\n\nexport interface ViewerDownloadControlProps {\n  actions: Array<ViewerDownloadAction | null | undefined>;\n  variant?: React.ComponentProps<typeof Button>[\"variant\"];\n  size?: React.ComponentProps<typeof Button>[\"size\"];\n  className?: string;\n  showLabel?: boolean;\n  /** Reports non-aborted action failures; visible failure UI belongs to consumers. */\n  onError?: ViewerDownloadErrorHandler;\n}\n\nexport interface ViewerDownloadButtonProps\n  extends Omit<ViewerDownloadControlProps, \"actions\"> {\n  action: ViewerDownloadAction | null;\n}\n\nexport interface ViewerDownloadMenuProps\n  extends Omit<ViewerDownloadControlProps, \"actions\"> {\n  actions: ViewerDownloadAction[];\n}\n\nexport function useViewerDownloadHref(\n  action: ViewerDownloadAction | null,\n): string | null {\n  const shouldCreateHref = action?.origin !== \"derived\";\n  const payload = shouldCreateHref ? getSynchronousPayload(action) : null;\n\n  return shouldCreateHref && payload?.kind === \"href\" ? payload.href : null;\n}\n\nexport function useViewerDownloadTrigger({\n  onError,\n  resetKey = \"\",\n}: ViewerDownloadTriggerOptions = {}): ViewerDownloadTrigger {\n  const [pendingActionId, setPendingActionId] = React.useState<string | null>(\n    null,\n  );\n  const abortControllerRef = React.useRef<AbortController | null>(null);\n\n  useKeyedMountEffect(joinEffectKey([resetKey]), () => {\n    return () => {\n      abortControllerRef.current?.abort();\n      abortControllerRef.current = null;\n    };\n  });\n\n  const triggerDownload = React.useCallback(\n    (action: ViewerDownloadAction) => {\n      abortControllerRef.current?.abort();\n      const abortController = new AbortController();\n      abortControllerRef.current = abortController;\n      setPendingActionId(action.id);\n      void triggerViewerDownload(action, { signal: abortController.signal })\n        .catch((error) => {\n          reportDownloadError(error, action, onError);\n        })\n        .finally(() => {\n          if (abortControllerRef.current === abortController) {\n            abortControllerRef.current = null;\n            setPendingActionId(null);\n          }\n        });\n    },\n    [onError],\n  );\n\n  return { pendingActionId, triggerDownload };\n}\n\nexport function ViewerDownloadControl({\n  actions,\n  variant = \"ghost\",\n  size = \"iconSm\",\n  className = \"size-7\",\n  showLabel = false,\n  onError,\n}: ViewerDownloadControlProps) {\n  const enabledActions = actions.filter(\n    (action): action is ViewerDownloadAction => Boolean(action),\n  );\n\n  if (enabledActions.length <= 1) {\n    return (\n      <ViewerDownloadButton\n        action={enabledActions[0] ?? null}\n        variant={variant}\n        size={size}\n        className={className}\n        showLabel={showLabel}\n        onError={onError}\n      />\n    );\n  }\n\n  return (\n    <ViewerDownloadMenu\n      actions={enabledActions}\n      variant={variant}\n      size={size}\n      className={className}\n      showLabel={showLabel}\n      onError={onError}\n    />\n  );\n}\n\nexport function ViewerDownloadButton({\n  action,\n  variant = \"ghost\",\n  size = \"iconSm\",\n  className = \"size-7\",\n  showLabel = false,\n  onError,\n}: ViewerDownloadButtonProps) {\n  const href = useViewerDownloadHref(action);\n  const label = action?.label ?? \"Download\";\n  const disabled = !action || action.isDisabled;\n  const hasDownloadHref = Boolean(href);\n  const { pendingActionId, triggerDownload } = useViewerDownloadTrigger({\n    onError,\n    resetKey: action,\n  });\n  const isPending = Boolean(action && pendingActionId === action.id);\n\n  const handleClick = React.useCallback(() => {\n    if (!action || hasDownloadHref) return;\n    triggerDownload(action);\n  }, [action, hasDownloadHref, triggerDownload]);\n\n  if (href) {\n    return (\n      <a\n        href={href}\n        download={action?.fileName}\n        className={cn(buttonVariants({ variant, size }), className)}\n        aria-label={label}\n        title={label}\n        data-slot=\"button\"\n      >\n        <Download className={showLabel ? \"mr-1.5 size-4\" : undefined} />\n        {showLabel ? label : null}\n      </a>\n    );\n  }\n\n  return (\n    <Button\n      variant={variant}\n      size={size}\n      className={className}\n      aria-label={label}\n      title={label}\n      disabled={disabled || isPending}\n      onClick={handleClick}\n    >\n      {isPending ? (\n        <Spinner className=\"size-4 animate-spin\" />\n      ) : (\n        <Download className={showLabel ? \"mr-1.5 size-4\" : undefined} />\n      )}\n      {showLabel ? label : null}\n    </Button>\n  );\n}\n\nexport function ViewerDownloadMenu({\n  actions,\n  variant = \"ghost\",\n  size = \"iconSm\",\n  className = \"size-7\",\n  showLabel = false,\n  onError,\n}: ViewerDownloadMenuProps) {\n  const actionSetKey = actions.map((action) => action.id).join(\"\\u0000\");\n  const label = \"Download\";\n  const { pendingActionId, triggerDownload } = useViewerDownloadTrigger({\n    onError,\n    resetKey: actionSetKey,\n  });\n\n  return (\n    <DropdownMenu>\n      <DropdownMenuTrigger asChild>\n        <Button\n          variant={variant}\n          size={size}\n          className={className}\n          aria-label={label}\n          title={label}\n          disabled={pendingActionId != null}\n        >\n          {pendingActionId != null ? (\n            <Spinner className=\"size-4 animate-spin\" />\n          ) : (\n            <Download className={showLabel ? \"mr-1.5 size-4\" : undefined} />\n          )}\n          {showLabel ? label : null}\n        </Button>\n      </DropdownMenuTrigger>\n      <DropdownMenuContent align=\"end\">\n        {actions.map((action) => (\n          <DropdownMenuItem\n            key={action.id}\n            disabled={action.isDisabled || pendingActionId != null}\n            onClick={() => triggerDownload(action)}\n          >\n            <Download />\n            <span>{action.label}</span>\n          </DropdownMenuItem>\n        ))}\n      </DropdownMenuContent>\n    </DropdownMenu>\n  );\n}\n\nfunction clickDownload(href: string, fileName: string) {\n  const anchor = document.createElement(\"a\");\n  anchor.href = href;\n  anchor.download = fileName;\n  anchor.rel = \"noreferrer\";\n  document.body.appendChild(anchor);\n  anchor.click();\n  anchor.remove();\n}\n\nfunction getSynchronousPayload(\n  action: ViewerDownloadAction | null,\n): ViewerDownloadPayload | null {\n  if (!action || action.isDisabled) return null;\n  const payload = action.getPayload();\n  return payload instanceof Promise ? null : payload;\n}\n\nfunction reportDownloadError(\n  error: unknown,\n  action: ViewerDownloadAction,\n  onError: ViewerDownloadErrorHandler | undefined,\n) {\n  if (!(error instanceof ViewerDownloadError)) return;\n  if (error.kind === \"aborted\") return;\n  onError?.(error, action);\n}\n\nfunction isAbortError(error: unknown) {\n  return error instanceof DOMException && error.name === \"AbortError\";\n}\n",
      "type": "registry:ui",
      "target": "@ui/viewer-download.tsx"
    },
    {
      "path": "registry/new-york-v4/ui/pdf-viewer-telemetry.ts",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { joinEffectKey } from \"@/lib/effect-key\";\nimport { useKeyedMountEffect } from \"@/hooks/use-keyed-mount-effect\";\n\nimport type { FileViewerRendererFrame } from \"./file-viewer-renderer-contract\";\nimport type { PdfPageLayoutModel } from \"./pdf-viewer-layout\";\nimport {\n  getPdfZoomMotionFlightRecords,\n  type PdfZoomMotionFlightRecord,\n} from \"./pdf-viewer-zoom-motion\";\n\ntype PdfViewerTelemetryMetricId =\n  | \"blink\"\n  | \"horizontal-back-and-forth\"\n  | \"vertical-overshoot\"\n  | \"settle-jitter\"\n  | \"resize-linearity\"\n  | \"scroll-stability\"\n  | \"renderer-continuity\"\n  | \"canvas-continuity\"\n  | \"raster-headroom\"\n  | \"virtual-window-retention\";\n\ntype PdfViewerTelemetryMetric = {\n  budget: string;\n  detail: string;\n  id: PdfViewerTelemetryMetricId;\n  label: string;\n  passed: boolean;\n  value: string;\n};\n\ntype PdfViewerTelemetrySample = {\n  activeRenderPageNumbers: number[];\n  canvasBitmapHeight: number | null;\n  canvasBitmapWidth: number | null;\n  canvasCssHeight: number | null;\n  canvasCssWidth: number | null;\n  canvasInkRatio: number | null;\n  canvasPixelSignature: string | null;\n  canvasRenderSource: string | null;\n  canvasRenderStatus: string | null;\n  clientHeight: number;\n  clientWidth: number;\n  devicePixelRatio: number;\n  displayScale: number;\n  documentKey: string;\n  elapsedMs: number;\n  gapWidth: number | null;\n  hasBlink: boolean;\n  layoutInlineSize: number | null;\n  logicalScrollTop: number;\n  motionPhase: FileViewerRendererFrame[\"phase\"];\n  physicalScrollTop: number;\n  primaryPageBottom: number | null;\n  primaryPageCenterX: number | null;\n  primaryPageHeight: number | null;\n  primaryPageLeft: number | null;\n  primaryPageNumber: number | null;\n  primaryPageTop: number | null;\n  primaryPageWidth: number | null;\n  renderPageNumbers: number[];\n  renderScale: number;\n  scrollHeight: number;\n  scrollPageOffset: number;\n  sidebarOpen: string | null;\n  sidebarState: string | null;\n  sidebarWidth: number | null;\n  timestamp: number;\n  toInlineSize: number | null;\n  transform: string | null;\n  visiblePageNumbers: number[];\n  visibleRenderedPageNumbers: number[];\n  visualHeight: number | null;\n  visualLeft: number | null;\n  visualTop: number | null;\n  visualWidth: number | null;\n};\n\ntype PdfViewerTelemetryRun = {\n  action: \"close\" | \"open\";\n  after: PdfViewerTelemetrySample;\n  before: PdfViewerTelemetrySample;\n  samples: PdfViewerTelemetrySample[];\n};\n\ntype PdfViewerTelemetryResult = {\n  durationMs: number;\n  metrics: PdfViewerTelemetryMetric[];\n  runs: PdfViewerTelemetryRun[];\n  sampledFrameCount: number;\n  status: \"failed\" | \"passed\";\n};\n\ntype PdfViewerTelemetryRunOptions = {\n  sampleFrameCount?: number;\n  settleFrameCount?: number;\n};\n\ntype PdfViewerTelemetryState = {\n  activeRenderPageNumbers: readonly number[];\n  displayScale: number;\n  documentKey: string;\n  getDocumentSurfaceElement: () => HTMLElement | null;\n  getScrollMetrics: () => {\n    physicalScrollHeight: number;\n    physicalScrollTop: number;\n    scrollPageOffset: number;\n    scrollTop: number;\n    viewportHeight: number;\n  };\n  getViewportElement: () => HTMLDivElement | null;\n  layout: PdfPageLayoutModel;\n  renderPageNumbers: readonly number[];\n  renderScale: number;\n  pageDevicePixelRatio: number;\n  rendererFrame: FileViewerRendererFrame;\n  visiblePageNumbers: readonly number[];\n};\n\ntype PdfViewerTelemetryRuntime = {\n  getLastResult: () => PdfViewerTelemetryResult | null;\n  /** Flight recorder for toolbar zoom relaxes (pdf-viewer-zoom-motion). */\n  getZoomFlightRecords: () => readonly PdfZoomMotionFlightRecord[];\n  readSample: () => PdfViewerTelemetrySample | null;\n  runShellMotion: (\n    options?: PdfViewerTelemetryRunOptions,\n  ) => Promise<PdfViewerTelemetryResult | null>;\n};\n\ndeclare global {\n  interface Window {\n    __pdfViewerTelemetry?: PdfViewerTelemetryRuntime;\n  }\n}\n\nconst PDF_TELEMETRY_READING_MARKER_RATIO = 0.2;\nconst PDF_TELEMETRY_REVERSAL_EPSILON_PX = 1;\nconst PDF_TELEMETRY_SETTLE_EPSILON_PX = 1;\nconst PDF_TELEMETRY_DEFAULT_SAMPLE_FRAMES = 36;\nconst PDF_TELEMETRY_DEFAULT_SETTLE_FRAMES = 12;\n\nexport function usePdfViewerTelemetry(state: PdfViewerTelemetryState) {\n  const stateRef = React.useRef(state);\n  const lastResultRef = React.useRef<PdfViewerTelemetryResult | null>(null);\n  stateRef.current = state;\n\n  useKeyedMountEffect(\n    joinEffectKey([\"pdf-viewer-telemetry\", state.documentKey]),\n    () => {\n      if (typeof window === \"undefined\") return;\n\n      const previousRuntime = window.__pdfViewerTelemetry;\n      const runtime: PdfViewerTelemetryRuntime = {\n        getLastResult: () => lastResultRef.current,\n        getZoomFlightRecords: () => getPdfZoomMotionFlightRecords(),\n        readSample: () =>\n          readPdfViewerTelemetrySample({\n            startedAt: performance.now(),\n            state: stateRef.current,\n          }),\n        runShellMotion: async (options) => {\n          const result = await runPdfViewerShellMotionTelemetry({\n            options,\n            stateRef,\n          });\n          lastResultRef.current = result;\n          if (result) logPdfViewerTelemetryResult(result);\n          return result;\n        },\n      };\n\n      window.__pdfViewerTelemetry = runtime;\n\n      return () => {\n        if (window.__pdfViewerTelemetry === runtime) {\n          window.__pdfViewerTelemetry = previousRuntime;\n        }\n      };\n    },\n  );\n}\n\nasync function runPdfViewerShellMotionTelemetry({\n  options,\n  stateRef,\n}: {\n  options?: PdfViewerTelemetryRunOptions;\n  stateRef: React.MutableRefObject<PdfViewerTelemetryState>;\n}): Promise<PdfViewerTelemetryResult | null> {\n  const state = stateRef.current;\n  const viewportElement = state.getViewportElement();\n  const shell = getPdfViewerTelemetryShell(viewportElement);\n  if (!shell) return null;\n\n  const sampleFrameCount =\n    options?.sampleFrameCount ?? PDF_TELEMETRY_DEFAULT_SAMPLE_FRAMES;\n  const settleFrameCount =\n    options?.settleFrameCount ?? PDF_TELEMETRY_DEFAULT_SETTLE_FRAMES;\n  const startedAt = performance.now();\n  const runs: PdfViewerTelemetryRun[] = [];\n  const firstAction =\n    shell.root.dataset.fileViewerSidebarOpen === \"true\" ? \"close\" : \"open\";\n  const secondAction = firstAction === \"close\" ? \"open\" : \"close\";\n\n  runs.push(\n    await samplePdfViewerShellMotionRun({\n      action: firstAction,\n      sampleFrameCount,\n      settleFrameCount,\n      shell,\n      startedAt,\n      stateRef,\n    }),\n  );\n  runs.push(\n    await samplePdfViewerShellMotionRun({\n      action: secondAction,\n      sampleFrameCount,\n      settleFrameCount,\n      shell,\n      startedAt,\n      stateRef,\n    }),\n  );\n\n  const metrics = collectPdfViewerTelemetryMetrics(runs);\n  const status = metrics.every((metric) => metric.passed) ? \"passed\" : \"failed\";\n\n  return {\n    durationMs: performance.now() - startedAt,\n    metrics,\n    runs,\n    sampledFrameCount: runs.reduce(\n      (count, run) => count + run.samples.length,\n      0,\n    ),\n    status,\n  };\n}\n\nasync function samplePdfViewerShellMotionRun({\n  action,\n  sampleFrameCount,\n  settleFrameCount,\n  shell,\n  startedAt,\n  stateRef,\n}: {\n  action: PdfViewerTelemetryRun[\"action\"];\n  sampleFrameCount: number;\n  settleFrameCount: number;\n  shell: PdfViewerTelemetryShell;\n  startedAt: number;\n  stateRef: React.MutableRefObject<PdfViewerTelemetryState>;\n}): Promise<PdfViewerTelemetryRun> {\n  const before = readPdfViewerTelemetrySample({\n    startedAt,\n    state: stateRef.current,\n  });\n  if (!before) throw new Error(\"PDF telemetry sample unavailable before run.\");\n\n  const samples: PdfViewerTelemetrySample[] = [];\n  shell.trigger.click();\n\n  for (let index = 0; index < sampleFrameCount; index += 1) {\n    await nextPdfTelemetryAnimationFrame();\n    const sample = readPdfViewerTelemetrySample({\n      startedAt,\n      state: stateRef.current,\n    });\n    if (sample) samples.push(sample);\n  }\n\n  for (let index = 0; index < settleFrameCount; index += 1) {\n    await nextPdfTelemetryAnimationFrame();\n  }\n\n  const after = readPdfViewerTelemetrySample({\n    startedAt,\n    state: stateRef.current,\n  });\n  if (!after) throw new Error(\"PDF telemetry sample unavailable after run.\");\n\n  return { action, after, before, samples };\n}\n\nfunction readPdfViewerTelemetrySample({\n  startedAt,\n  state,\n}: {\n  startedAt: number;\n  state: PdfViewerTelemetryState;\n}): PdfViewerTelemetrySample | null {\n  const viewportElement = state.getViewportElement();\n  const surfaceElement = state.getDocumentSurfaceElement();\n  if (!viewportElement || !surfaceElement) return null;\n\n  const pdfRoot = surfaceElement.closest<HTMLElement>(\n    '[data-slot=\"pdf-viewer\"]',\n  );\n  const shell = getPdfViewerTelemetryShell(viewportElement);\n  const viewportRect = viewportElement.getBoundingClientRect();\n  const surfaceRect = surfaceElement.getBoundingClientRect();\n  const metrics = state.getScrollMetrics();\n  const pages = readPdfViewerTelemetryPages({ pdfRoot, viewportRect });\n  const marker =\n    viewportRect.top + viewportRect.height * PDF_TELEMETRY_READING_MARKER_RATIO;\n  const primaryPage = getPdfTelemetryPrimaryPage({ marker, pages });\n  const visibleRenderedPageNumbers = pages\n    .filter((page) => page.isVisible && page.hasCanvasPixels)\n    .map((page) => page.pageNumber);\n  const transform =\n    surfaceElement.style.transform ||\n    getComputedStyle(surfaceElement).transform;\n\n  return {\n    activeRenderPageNumbers: [...state.activeRenderPageNumbers],\n    canvasBitmapHeight: primaryPage?.canvasBitmapHeight ?? null,\n    canvasBitmapWidth: primaryPage?.canvasBitmapWidth ?? null,\n    canvasCssHeight: primaryPage?.canvasCssHeight ?? null,\n    canvasCssWidth: primaryPage?.canvasCssWidth ?? null,\n    canvasInkRatio: primaryPage?.canvasInkRatio ?? null,\n    canvasPixelSignature: primaryPage?.canvasPixelSignature ?? null,\n    canvasRenderSource: primaryPage?.canvasRenderSource ?? null,\n    canvasRenderStatus: primaryPage?.canvasRenderStatus ?? null,\n    clientHeight: viewportElement.clientHeight,\n    clientWidth: viewportElement.clientWidth,\n    devicePixelRatio: state.pageDevicePixelRatio,\n    displayScale: state.displayScale,\n    documentKey: state.documentKey,\n    elapsedMs: Math.max(0, performance.now() - startedAt),\n    gapWidth: shell?.gap.getBoundingClientRect().width ?? null,\n    hasBlink:\n      !primaryPage ||\n      !primaryPage.hasCanvasPixels ||\n      primaryPage.canvasRenderStatus === \"failed\" ||\n      primaryPage.canvasRenderStatus === \"cancelled\" ||\n      visibleRenderedPageNumbers.length === 0,\n    layoutInlineSize: state.rendererFrame.layoutInlineSize,\n    logicalScrollTop: metrics.scrollTop,\n    motionPhase: state.rendererFrame.phase,\n    physicalScrollTop: viewportElement.scrollTop,\n    primaryPageBottom:\n      primaryPage == null ? null : primaryPage.bottom - viewportRect.top,\n    primaryPageCenterX:\n      primaryPage == null\n        ? null\n        : primaryPage.left - viewportRect.left + primaryPage.width / 2,\n    primaryPageHeight: primaryPage?.height ?? null,\n    primaryPageLeft:\n      primaryPage == null ? null : primaryPage.left - viewportRect.left,\n    primaryPageNumber: primaryPage?.pageNumber ?? null,\n    primaryPageTop:\n      primaryPage == null ? null : primaryPage.top - viewportRect.top,\n    primaryPageWidth: primaryPage?.width ?? null,\n    renderPageNumbers: [...state.renderPageNumbers],\n    renderScale: state.renderScale,\n    scrollHeight: viewportElement.scrollHeight,\n    scrollPageOffset: metrics.scrollPageOffset,\n    sidebarOpen: shell?.root.dataset.fileViewerSidebarOpen ?? null,\n    sidebarState: shell?.root.dataset.fileViewerSidebarState ?? null,\n    sidebarWidth: shell?.sidebar?.getBoundingClientRect().width ?? null,\n    timestamp: performance.now(),\n    toInlineSize: state.rendererFrame.toInlineSize,\n    transform: transform && transform !== \"none\" ? transform : null,\n    visiblePageNumbers: [...state.visiblePageNumbers],\n    visibleRenderedPageNumbers,\n    visualHeight: surfaceRect.height,\n    visualLeft: surfaceRect.left - viewportRect.left,\n    visualTop: surfaceRect.top - viewportRect.top,\n    visualWidth: surfaceRect.width,\n  };\n}\n\ntype PdfViewerTelemetryShell = {\n  gap: HTMLElement;\n  root: HTMLElement;\n  sidebar: HTMLElement | null;\n  trigger: HTMLButtonElement;\n};\n\nfunction getPdfViewerTelemetryShell(\n  viewportElement: HTMLElement | null,\n): PdfViewerTelemetryShell | null {\n  const root = viewportElement?.closest<HTMLElement>(\n    '[data-slot=\"file-viewer-root\"]',\n  );\n  const trigger = root?.querySelector<HTMLButtonElement>(\n    '[data-slot=\"file-viewer-sidebar-trigger\"]',\n  );\n  const gap = root?.querySelector<HTMLElement>(\n    '[data-slot=\"file-viewer-sidebar-gap\"]',\n  );\n\n  if (!root || !trigger || !gap) return null;\n\n  return {\n    gap,\n    root,\n    sidebar: root.querySelector<HTMLElement>(\n      '[data-slot=\"file-viewer-sidebar\"]',\n    ),\n    trigger,\n  };\n}\n\ntype PdfViewerTelemetryPage = {\n  bottom: number;\n  canvasBitmapHeight: number | null;\n  canvasBitmapWidth: number | null;\n  canvasCssHeight: number | null;\n  canvasCssWidth: number | null;\n  canvasInkRatio: number | null;\n  canvasPixelSignature: string | null;\n  canvasRenderSource: string | null;\n  canvasRenderStatus: string | null;\n  hasCanvasPixels: boolean;\n  height: number;\n  isVisible: boolean;\n  left: number;\n  pageNumber: number;\n  top: number;\n  width: number;\n};\n\nfunction readPdfViewerTelemetryPages({\n  pdfRoot,\n  viewportRect,\n}: {\n  pdfRoot: HTMLElement | null;\n  viewportRect: DOMRect;\n}): PdfViewerTelemetryPage[] {\n  if (!pdfRoot) return [];\n\n  return Array.from(\n    pdfRoot.querySelectorAll<HTMLElement>('[data-slot=\"pdf-page\"]'),\n  )\n    .map((element) => {\n      const rect = element.getBoundingClientRect();\n      const canvas = element.querySelector(\"canvas\");\n      const canvasRect = canvas?.getBoundingClientRect();\n      const canvasBitmapWidth =\n        canvas instanceof HTMLCanvasElement ? canvas.width : null;\n      const canvasBitmapHeight =\n        canvas instanceof HTMLCanvasElement ? canvas.height : null;\n      const pixelSample =\n        canvas instanceof HTMLCanvasElement\n          ? readPdfTelemetryCanvasPixels(canvas)\n          : null;\n\n      return {\n        bottom: rect.bottom,\n        canvasBitmapHeight,\n        canvasBitmapWidth,\n        canvasCssHeight: canvasRect?.height ?? null,\n        canvasCssWidth: canvasRect?.width ?? null,\n        canvasInkRatio: pixelSample?.inkRatio ?? null,\n        canvasPixelSignature: pixelSample?.signature ?? null,\n        canvasRenderSource: canvas?.dataset.pdfRenderSource ?? null,\n        canvasRenderStatus: canvas?.dataset.pdfRenderStatus ?? null,\n        hasCanvasPixels:\n          (canvasBitmapWidth ?? 0) > 0 && (canvasBitmapHeight ?? 0) > 0,\n        height: rect.height,\n        isVisible:\n          rect.width > 1 &&\n          rect.height > 1 &&\n          rect.bottom > viewportRect.top + 1 &&\n          rect.top < viewportRect.bottom - 1,\n        left: rect.left,\n        pageNumber: Number(element.dataset.page ?? 0),\n        top: rect.top,\n        width: rect.width,\n      };\n    })\n    .filter((page) => page.pageNumber > 0)\n    .sort((left, right) => left.pageNumber - right.pageNumber);\n}\n\nfunction getPdfTelemetryPrimaryPage({\n  marker,\n  pages,\n}: {\n  marker: number;\n  pages: readonly PdfViewerTelemetryPage[];\n}) {\n  const visiblePages = pages.filter((page) => page.isVisible);\n  if (visiblePages.length === 0) return null;\n\n  return (\n    visiblePages.find((page) => page.top <= marker && page.bottom >= marker) ??\n    visiblePages.reduce((nearest, page) =>\n      Math.abs(page.top - marker) < Math.abs(nearest.top - marker)\n        ? page\n        : nearest,\n    )\n  );\n}\n\n// Downsample the page canvas to 8x8 with a GPU-side drawImage and do a single\n// 64-pixel readback: per-point getImageData forces one full GPU->CPU sync per\n// call and would inflate the main-thread work this telemetry measures. The\n// downsampled cells average their region, so any text or figure reads as\n// non-white ink while a wiped buffer reads fully blank — presence and status\n// attributes cannot distinguish those two.\nconst PDF_TELEMETRY_CANVAS_PROBE_SIZE = 8;\nlet pdfTelemetryCanvasProbe: {\n  canvas: HTMLCanvasElement;\n  context: CanvasRenderingContext2D;\n} | null = null;\n\nfunction getPdfTelemetryCanvasProbe() {\n  if (pdfTelemetryCanvasProbe) return pdfTelemetryCanvasProbe;\n  const canvas = document.createElement(\"canvas\");\n  canvas.width = PDF_TELEMETRY_CANVAS_PROBE_SIZE;\n  canvas.height = PDF_TELEMETRY_CANVAS_PROBE_SIZE;\n  const context = canvas.getContext(\"2d\", { willReadFrequently: true });\n  if (!context) return null;\n  pdfTelemetryCanvasProbe = { canvas, context };\n  return pdfTelemetryCanvasProbe;\n}\n\nfunction readPdfTelemetryCanvasPixels(canvas: HTMLCanvasElement) {\n  if (canvas.width <= 0 || canvas.height <= 0) return null;\n  const probe = getPdfTelemetryCanvasProbe();\n  if (!probe) return null;\n\n  const size = PDF_TELEMETRY_CANVAS_PROBE_SIZE;\n  let hash = 2166136261;\n  let inkCount = 0;\n\n  try {\n    probe.context.clearRect(0, 0, size, size);\n    probe.context.drawImage(canvas, 0, 0, size, size);\n    const { data } = probe.context.getImageData(0, 0, size, size);\n    for (let index = 0; index < data.length; index += 4) {\n      const red = data[index];\n      const green = data[index + 1];\n      const blue = data[index + 2];\n      const alpha = data[index + 3];\n      const luminance = red * 0.2126 + green * 0.7152 + blue * 0.0722;\n      if (alpha > 0 && luminance < 245) inkCount += 1;\n      hash ^= red;\n      hash = Math.imul(hash, 16777619);\n      hash ^= green;\n      hash = Math.imul(hash, 16777619);\n      hash ^= blue;\n      hash = Math.imul(hash, 16777619);\n      hash ^= alpha;\n      hash = Math.imul(hash, 16777619);\n    }\n  } catch {\n    return null;\n  }\n\n  return {\n    inkRatio: inkCount / (size * size),\n    signature: hash.toString(16),\n  };\n}\n\nfunction collectPdfViewerTelemetryMetrics(\n  runs: readonly PdfViewerTelemetryRun[],\n): PdfViewerTelemetryMetric[] {\n  return [\n    collectPdfTelemetryBlinkMetric(runs),\n    collectPdfTelemetryHorizontalMetric(runs),\n    collectPdfTelemetryVerticalMetric(runs),\n    collectPdfTelemetrySettleJitterMetric(runs),\n    collectPdfTelemetryResizeLinearityMetric(runs),\n    collectPdfTelemetryScrollStabilityMetric(runs),\n    collectPdfTelemetryRendererContinuityMetric(runs),\n    collectPdfTelemetryCanvasContinuityMetric(runs),\n    collectPdfTelemetryRasterHeadroomMetric(runs),\n    collectPdfTelemetryVirtualWindowRetentionMetric(runs),\n  ];\n}\n\nfunction collectPdfTelemetryBlinkMetric(\n  runs: readonly PdfViewerTelemetryRun[],\n): PdfViewerTelemetryMetric {\n  const frames = getPdfTelemetryAllSamples(runs).filter(\n    (sample) => sample.hasBlink,\n  ).length;\n\n  return {\n    budget: \"0 blink frames\",\n    detail:\n      \"A blink frame has no visible rendered page, a missing canvas, or a failed visible canvas.\",\n    id: \"blink\",\n    label: \"Blink\",\n    passed: frames === 0,\n    value: `${frames} frames`,\n  };\n}\n\nfunction collectPdfTelemetryHorizontalMetric(\n  runs: readonly PdfViewerTelemetryRun[],\n): PdfViewerTelemetryMetric {\n  const reversals = Math.max(\n    0,\n    ...runs.map((run) =>\n      Math.max(\n        countPdfTelemetryReversals(\n          getPdfTelemetryMotionSamples(run).map(\n            (sample) => sample.primaryPageLeft,\n          ),\n          PDF_TELEMETRY_REVERSAL_EPSILON_PX,\n        ),\n        countPdfTelemetryReversals(\n          getPdfTelemetryMotionSamples(run).map(\n            (sample) => sample.primaryPageCenterX,\n          ),\n          PDF_TELEMETRY_REVERSAL_EPSILON_PX,\n        ),\n      ),\n    ),\n  );\n  const overshoot = Math.max(\n    0,\n    ...runs.flatMap((run) => [\n      getPdfTelemetryRunOvershoot(run, (sample) => sample.primaryPageLeft),\n      getPdfTelemetryRunOvershoot(run, (sample) => sample.primaryPageCenterX),\n    ]),\n  );\n\n  return {\n    budget: \"0 x reversals over 1px, <= 2px x overshoot\",\n    detail: \"Tracks the visible page left edge and center during shell motion.\",\n    id: \"horizontal-back-and-forth\",\n    label: \"Horizontal back and forth\",\n    passed: reversals === 0 && overshoot <= 2,\n    value: `${reversals} reversals / ${formatPdfTelemetryNumber(\n      overshoot,\n      2,\n    )}px`,\n  };\n}\n\nfunction collectPdfTelemetryVerticalMetric(\n  runs: readonly PdfViewerTelemetryRun[],\n): PdfViewerTelemetryMetric {\n  const overshoot = Math.max(\n    0,\n    ...runs.flatMap((run) => [\n      getPdfTelemetryRunOvershoot(run, (sample) => sample.primaryPageTop),\n      getPdfTelemetryRunOvershoot(run, (sample) => sample.logicalScrollTop),\n    ]),\n  );\n\n  return {\n    budget: \"<= 4px\",\n    detail:\n      \"Visible page top and logical scroll position should stay inside their start/end intervals.\",\n    id: \"vertical-overshoot\",\n    label: \"Vertical overshoot\",\n    passed: overshoot <= 4,\n    value: `${formatPdfTelemetryNumber(overshoot, 2)}px`,\n  };\n}\n\nfunction collectPdfTelemetrySettleJitterMetric(\n  runs: readonly PdfViewerTelemetryRun[],\n): PdfViewerTelemetryMetric {\n  const range = Math.max(\n    0,\n    ...runs.flatMap((run) => {\n      const settled = getPdfTelemetrySettledSamples(run);\n      return [\n        getPdfTelemetryRange(settled.map((sample) => sample.primaryPageLeft)),\n        getPdfTelemetryRange(settled.map((sample) => sample.primaryPageTop)),\n      ];\n    }),\n  );\n\n  return {\n    budget: \"<= 1px settled range\",\n    detail:\n      \"After endpoint geometry is reached, the visible page and logical scroll should not tremble.\",\n    id: \"settle-jitter\",\n    label: \"Settle jitter\",\n    passed: range <= 1,\n    value: `${formatPdfTelemetryNumber(range, 2)}px`,\n  };\n}\n\nfunction collectPdfTelemetryResizeLinearityMetric(\n  runs: readonly PdfViewerTelemetryRun[],\n): PdfViewerTelemetryMetric {\n  const maxError = Math.max(\n    0,\n    ...runs.map((run) => getPdfTelemetryResizeProgressError(run)),\n  );\n\n  return {\n    budget: \"<= 20% progress error\",\n    detail:\n      \"Visible page width should progress with sidebar width instead of waiting for settle.\",\n    id: \"resize-linearity\",\n    label: \"Resize linearity\",\n    passed: maxError <= 0.2,\n    value: `${formatPdfTelemetryNumber(maxError * 100, 1)}%`,\n  };\n}\n\nfunction collectPdfTelemetryScrollStabilityMetric(\n  runs: readonly PdfViewerTelemetryRun[],\n): PdfViewerTelemetryMetric {\n  const maxSlidingRange = Math.max(\n    0,\n    ...runs.map((run) =>\n      getPdfTelemetryRange(\n        getPdfTelemetryMotionSamples(run).map(\n          (sample) => sample.physicalScrollTop,\n        ),\n      ),\n    ),\n  );\n\n  return {\n    budget: \"<= 1px physical scroll range while sliding\",\n    detail:\n      \"The shell motion should not emit native scroll drift while the PDF surface is being transformed.\",\n    id: \"scroll-stability\",\n    label: \"Scroll stability\",\n    passed: maxSlidingRange <= 1,\n    value: `${formatPdfTelemetryNumber(maxSlidingRange, 2)}px`,\n  };\n}\n\nfunction collectPdfTelemetryRendererContinuityMetric(\n  runs: readonly PdfViewerTelemetryRun[],\n): PdfViewerTelemetryMetric {\n  let missingPrimaryFrames = 0;\n  let emptyRenderedFrames = 0;\n\n  for (const run of runs) {\n    const primaryPageNumber = run.before.primaryPageNumber;\n    for (const sample of getPdfTelemetryLifecycleSamples(run)) {\n      if (sample.visibleRenderedPageNumbers.length === 0)\n        emptyRenderedFrames += 1;\n      if (\n        primaryPageNumber != null &&\n        !sample.visibleRenderedPageNumbers.includes(primaryPageNumber)\n      ) {\n        missingPrimaryFrames += 1;\n      }\n    }\n  }\n\n  return {\n    budget: \"0 blank rendered frames, 0 primary-page drops\",\n    detail:\n      \"The virtualized window must keep the visible rendered page mounted throughout motion and settle.\",\n    id: \"renderer-continuity\",\n    label: \"Renderer continuity\",\n    passed: emptyRenderedFrames === 0 && missingPrimaryFrames === 0,\n    value: `${emptyRenderedFrames} blank / ${missingPrimaryFrames} drops`,\n  };\n}\n\nfunction collectPdfTelemetryCanvasContinuityMetric(\n  runs: readonly PdfViewerTelemetryRun[],\n): PdfViewerTelemetryMetric {\n  let missingCanvasFrames = 0;\n  let failedCanvasFrames = 0;\n  let whiteoutFrames = 0;\n\n  for (const sample of getPdfTelemetryAllSamples(runs)) {\n    if (\n      sample.canvasBitmapWidth == null ||\n      sample.canvasBitmapHeight == null ||\n      sample.canvasBitmapWidth <= 0 ||\n      sample.canvasBitmapHeight <= 0\n    ) {\n      missingCanvasFrames += 1;\n    }\n    if (\n      sample.canvasRenderStatus === \"failed\" ||\n      sample.canvasRenderStatus === \"cancelled\"\n    ) {\n      failedCanvasFrames += 1;\n    }\n  }\n\n  // A whiteout is the blink presence checks cannot see: the same primary page\n  // keeps a sized, \"ready\" canvas whose buffer momentarily holds zero ink —\n  // a resize wiped it before the redraw landed.\n  for (const run of runs) {\n    const samples = [run.before, ...run.samples, run.after];\n    for (let index = 1; index < samples.length; index += 1) {\n      const previous = samples[index - 1];\n      const current = samples[index];\n      if (\n        previous.primaryPageNumber != null &&\n        previous.primaryPageNumber === current.primaryPageNumber &&\n        previous.canvasInkRatio != null &&\n        previous.canvasInkRatio > 0 &&\n        current.canvasInkRatio === 0\n      ) {\n        whiteoutFrames += 1;\n      }\n    }\n  }\n\n  return {\n    budget:\n      \"0 missing canvas frames, 0 failed/cancelled visible canvases, 0 whiteouts\",\n    detail:\n      \"The primary visible page canvas must keep pixels while target raster work is pending — including its buffer contents, which must never wipe to blank mid-motion.\",\n    id: \"canvas-continuity\",\n    label: \"Canvas continuity\",\n    passed:\n      missingCanvasFrames === 0 &&\n      failedCanvasFrames === 0 &&\n      whiteoutFrames === 0,\n    value: `${missingCanvasFrames} missing / ${failedCanvasFrames} failed / ${whiteoutFrames} whiteouts`,\n  };\n}\n\nfunction collectPdfTelemetryRasterHeadroomMetric(\n  runs: readonly PdfViewerTelemetryRun[],\n): PdfViewerTelemetryMetric {\n  const minHeadroom = Math.min(\n    0,\n    ...runs.flatMap((run) => {\n      const maxCssWidth = Math.max(\n        0,\n        ...getPdfTelemetryLifecycleSamples(run).map(\n          (sample) => sample.primaryPageWidth ?? 0,\n        ),\n      );\n      return getPdfTelemetryLifecycleSamples(run).map((sample) => {\n        if (\n          sample.canvasBitmapWidth == null ||\n          sample.canvasBitmapWidth <= 0 ||\n          sample.canvasCssWidth == null ||\n          sample.canvasCssWidth <= 0 ||\n          sample.primaryPageWidth == null ||\n          sample.primaryPageWidth <= 0\n        ) {\n          return 0;\n        }\n\n        return sample.canvasBitmapWidth / sample.devicePixelRatio - maxCssWidth;\n      });\n    }),\n  );\n\n  return {\n    budget: \">= -1px visible canvas backing headroom\",\n    detail:\n      \"Visible canvases should keep enough backing resolution for the largest page width in the sidebar cycle.\",\n    id: \"raster-headroom\",\n    label: \"Raster headroom\",\n    passed: minHeadroom >= -1,\n    value: `${formatPdfTelemetryNumber(minHeadroom, 2)}px`,\n  };\n}\n\nfunction collectPdfTelemetryVirtualWindowRetentionMetric(\n  runs: readonly PdfViewerTelemetryRun[],\n): PdfViewerTelemetryMetric {\n  let droppedFrames = 0;\n\n  for (const run of runs) {\n    const primaryPageNumber = run.before.primaryPageNumber;\n    if (primaryPageNumber == null) {\n      droppedFrames += 1;\n      continue;\n    }\n\n    for (const sample of getPdfTelemetryLifecycleSamples(run)) {\n      if (!sample.renderPageNumbers.includes(primaryPageNumber)) {\n        droppedFrames += 1;\n      }\n    }\n  }\n\n  return {\n    budget: \"0 retained-window drops\",\n    detail:\n      \"The virtual render window must retain the pre-motion reading page until the shell motion settles.\",\n    id: \"virtual-window-retention\",\n    label: \"Virtual window retention\",\n    passed: droppedFrames === 0,\n    value: `${droppedFrames} drops`,\n  };\n}\n\nfunction logPdfViewerTelemetryResult(result: PdfViewerTelemetryResult) {\n  const metrics = result.metrics.map((metric) => ({\n    budget: metric.budget,\n    detail: metric.detail,\n    id: metric.id,\n    label: metric.label,\n    passed: metric.passed,\n    value: metric.value,\n  }));\n  const summary = {\n    durationMs: Number(result.durationMs.toFixed(1)),\n    failedMetricIds: metrics\n      .filter((metric) => !metric.passed)\n      .map((metric) => metric.id),\n    metrics,\n    passedMetricCount: metrics.filter((metric) => metric.passed).length,\n    sampledFrameCount: result.sampledFrameCount,\n    status: result.status,\n    totalMetricCount: metrics.length,\n  };\n\n  console.info(\"[pdf-viewer:telemetry] result\", JSON.stringify(summary));\n  console.info(\"[pdf-viewer:telemetry] full result\", JSON.stringify(result));\n  console.table(metrics);\n}\n\nfunction getPdfTelemetryResizeProgressError(run: PdfViewerTelemetryRun) {\n  const beforeGap = run.before.gapWidth;\n  const afterGap = run.after.gapWidth;\n  const beforeWidth = run.before.primaryPageWidth;\n  const afterWidth = run.after.primaryPageWidth;\n\n  if (\n    beforeGap == null ||\n    afterGap == null ||\n    beforeWidth == null ||\n    afterWidth == null ||\n    Math.abs(afterGap - beforeGap) <= 8 ||\n    Math.abs(afterWidth - beforeWidth) <= 8\n  ) {\n    return 0;\n  }\n\n  return Math.max(\n    0,\n    ...getPdfTelemetryMotionSamples(run).map((sample) => {\n      const gapProgress = getPdfTelemetryProgress(\n        sample.gapWidth,\n        beforeGap,\n        afterGap,\n      );\n      const widthProgress = getPdfTelemetryProgress(\n        sample.primaryPageWidth,\n        beforeWidth,\n        afterWidth,\n      );\n      return Math.abs(gapProgress - widthProgress);\n    }),\n  );\n}\n\nfunction getPdfTelemetryProgress(\n  value: number | null,\n  start: number,\n  end: number,\n) {\n  if (value == null) return 1;\n  const travel = end - start;\n  if (Math.abs(travel) <= 0.001) return 1;\n  return clampPdfTelemetryNumber((value - start) / travel, 0, 1);\n}\n\nfunction getPdfTelemetryRunOvershoot(\n  run: PdfViewerTelemetryRun,\n  getValue: (sample: PdfViewerTelemetrySample) => number | null,\n) {\n  const start = getValue(run.before);\n  const end = getValue(run.after);\n  if (start == null || end == null) return 0;\n\n  const min = Math.min(start, end);\n  const max = Math.max(start, end);\n\n  return Math.max(\n    0,\n    ...getPdfTelemetryMotionSamples(run).map((sample) => {\n      const value = getValue(sample);\n      if (value == null) return 0;\n      if (value < min) return min - value;\n      if (value > max) return value - max;\n      return 0;\n    }),\n  );\n}\n\nfunction getPdfTelemetrySettledSamples(run: PdfViewerTelemetryRun) {\n  const afterGap = run.after.gapWidth;\n  const afterWidth = run.after.primaryPageWidth;\n  const settledIndex = run.samples.findIndex(\n    (sample) =>\n      afterGap != null &&\n      afterWidth != null &&\n      sample.gapWidth != null &&\n      sample.primaryPageWidth != null &&\n      Math.abs(sample.gapWidth - afterGap) <= PDF_TELEMETRY_SETTLE_EPSILON_PX &&\n      Math.abs(sample.primaryPageWidth - afterWidth) <=\n        PDF_TELEMETRY_SETTLE_EPSILON_PX,\n  );\n\n  return settledIndex < 0\n    ? [run.after]\n    : [...run.samples.slice(settledIndex), run.after];\n}\n\nfunction getPdfTelemetryMotionSamples(run: PdfViewerTelemetryRun) {\n  return run.samples.filter((sample) => sample.motionPhase === \"sliding\");\n}\n\nfunction getPdfTelemetryLifecycleSamples(run: PdfViewerTelemetryRun) {\n  return [...run.samples, run.after];\n}\n\nfunction getPdfTelemetryAllSamples(runs: readonly PdfViewerTelemetryRun[]) {\n  return runs.flatMap((run) => [run.before, ...run.samples, run.after]);\n}\n\nfunction countPdfTelemetryReversals(\n  values: readonly (number | null)[],\n  epsilon: number,\n) {\n  let direction = 0;\n  let reversals = 0;\n  let previous: number | null = null;\n\n  for (const value of values) {\n    if (value == null || !Number.isFinite(value)) continue;\n    if (previous === null) {\n      previous = value;\n      continue;\n    }\n\n    const delta = value - previous;\n    previous = value;\n    if (Math.abs(delta) <= epsilon) continue;\n\n    const nextDirection = delta > 0 ? 1 : -1;\n    if (direction !== 0 && nextDirection !== direction) reversals += 1;\n    direction = nextDirection;\n  }\n\n  return reversals;\n}\n\nfunction getPdfTelemetryRange(values: readonly (number | null)[]) {\n  const finiteValues = values.filter(\n    (value): value is number => value != null && Number.isFinite(value),\n  );\n  if (finiteValues.length === 0) return 0;\n  return Math.max(...finiteValues) - Math.min(...finiteValues);\n}\n\nfunction nextPdfTelemetryAnimationFrame() {\n  return new Promise<void>((resolve) => requestAnimationFrame(() => resolve()));\n}\n\nfunction clampPdfTelemetryNumber(value: number, min: number, max: number) {\n  return Math.min(max, Math.max(min, value));\n}\n\nfunction formatPdfTelemetryNumber(value: number, fractionDigits: number) {\n  return Number.isFinite(value) ? Number(value.toFixed(fractionDigits)) : 0;\n}\n",
      "type": "registry:ui",
      "target": "@ui/pdf-viewer-telemetry.ts"
    }
  ],
  "type": "registry:ui"
}