{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "pdf-viewer",
  "title": "PDF Viewer",
  "description": "Multi-mode PDF viewer with single page, continuous scroll, and book layout modes",
  "dependencies": [
    "react-pdf",
    "pdfjs-dist"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/default/blocks/pdf/pdf-viewer/components/elements/pdf-viewer.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { Document, Page, pdfjs } from \"react-pdf\";\n\nimport { cn } from \"@/lib/utils\";\n\nimport \"react-pdf/dist/Page/AnnotationLayer.css\";\nimport \"react-pdf/dist/Page/TextLayer.css\";\n\n// Set up PDF.js worker\npdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs`;\n\ntype ViewMode = \"single\" | \"scroll\" | \"book\";\n\ninterface PdfViewerProps {\n  /** URL to the PDF file or File object */\n  file: string | File;\n  /** Initial viewing mode */\n  mode?: ViewMode;\n  /** Initial zoom level (0.5 to 2.0) */\n  initialZoom?: number;\n  /** Custom className */\n  className?: string;\n}\n\nexport function PdfViewer({\n  file,\n  mode = \"single\",\n  initialZoom = 1.0,\n  className,\n}: PdfViewerProps) {\n  const [numPages, setNumPages] = React.useState<number>(0);\n  const [currentPage, setCurrentPage] = React.useState<number>(1);\n  const [viewMode, setViewMode] = React.useState<ViewMode>(mode);\n  const [zoom, setZoom] = React.useState<number>(initialZoom);\n  const [pageWidth, setPageWidth] = React.useState<number>(0);\n  const containerRef = React.useRef<HTMLDivElement>(null);\n\n  function onDocumentLoadSuccess({ numPages }: { numPages: number }) {\n    setNumPages(numPages);\n    setCurrentPage(1);\n  }\n\n  // Calculate page width based on container and zoom\n  React.useEffect(() => {\n    if (!containerRef.current) return;\n\n    const updateWidth = () => {\n      if (containerRef.current) {\n        const containerWidth = containerRef.current.clientWidth;\n        const baseWidth =\n          viewMode === \"book\" ? containerWidth / 2 - 40 : containerWidth - 40;\n        setPageWidth(baseWidth * zoom);\n      }\n    };\n\n    updateWidth();\n    window.addEventListener(\"resize\", updateWidth);\n    return () => window.removeEventListener(\"resize\", updateWidth);\n  }, [viewMode, zoom]);\n\n  const goToPreviousPage = () => {\n    setCurrentPage((prev) => Math.max(prev - (viewMode === \"book\" ? 2 : 1), 1));\n  };\n\n  const goToNextPage = () => {\n    setCurrentPage((prev) =>\n      Math.min(\n        prev + (viewMode === \"book\" ? 2 : 1),\n        viewMode === \"book\" ? numPages - 1 : numPages,\n      ),\n    );\n  };\n\n  const handleZoomIn = () => setZoom((prev) => Math.min(prev + 0.25, 2.0));\n  const handleZoomOut = () => setZoom((prev) => Math.max(prev - 0.25, 0.5));\n  const handleFitWidth = () => setZoom(1.0);\n\n  const handlePageInput = (e: React.ChangeEvent<HTMLInputElement>) => {\n    const page = Number.parseInt(e.target.value, 10);\n    if (!Number.isNaN(page) && page >= 1 && page <= numPages) {\n      setCurrentPage(page);\n    }\n  };\n\n  // For book mode: determine if we should show single page (cover) or two pages\n  const showCoverAlone = viewMode === \"book\" && currentPage === 1;\n  const bookSecondPage = showCoverAlone ? null : currentPage + 1;\n\n  return (\n    <div\n      data-slot=\"pdf-viewer\"\n      className={cn(\n        \"flex flex-col border border-border rounded-lg bg-background overflow-hidden\",\n        className,\n      )}\n    >\n      {/* Toolbar */}\n      <div className=\"flex items-center justify-between gap-4 p-3 border-b border-border bg-muted/50\">\n        {/* Mode Switcher */}\n        <div className=\"flex items-center gap-1 border border-border rounded-md p-1 bg-background\">\n          <button\n            type=\"button\"\n            onClick={() => setViewMode(\"single\")}\n            className={cn(\n              \"px-3 py-1.5 text-xs font-medium rounded transition-colors\",\n              viewMode === \"single\"\n                ? \"bg-primary text-primary-foreground\"\n                : \"text-muted-foreground hover:text-foreground hover:bg-muted\",\n            )}\n          >\n            Single\n          </button>\n          <button\n            type=\"button\"\n            onClick={() => setViewMode(\"scroll\")}\n            className={cn(\n              \"px-3 py-1.5 text-xs font-medium rounded transition-colors\",\n              viewMode === \"scroll\"\n                ? \"bg-primary text-primary-foreground\"\n                : \"text-muted-foreground hover:text-foreground hover:bg-muted\",\n            )}\n          >\n            Scroll\n          </button>\n          <button\n            type=\"button\"\n            onClick={() => setViewMode(\"book\")}\n            className={cn(\n              \"px-3 py-1.5 text-xs font-medium rounded transition-colors\",\n              viewMode === \"book\"\n                ? \"bg-primary text-primary-foreground\"\n                : \"text-muted-foreground hover:text-foreground hover:bg-muted\",\n            )}\n          >\n            Book\n          </button>\n        </div>\n\n        {/* Page Navigation */}\n        {viewMode !== \"scroll\" && (\n          <div className=\"flex items-center gap-2\">\n            <button\n              type=\"button\"\n              onClick={goToPreviousPage}\n              disabled={currentPage <= 1}\n              className=\"px-2 py-1 text-sm border border-border rounded bg-background hover:bg-muted disabled:opacity-50 disabled:cursor-not-allowed\"\n            >\n              ←\n            </button>\n            <div className=\"flex items-center gap-1 text-sm\">\n              <input\n                type=\"number\"\n                min={1}\n                max={numPages}\n                value={currentPage}\n                onChange={handlePageInput}\n                className=\"w-12 px-2 py-1 text-center border border-border rounded bg-background\"\n              />\n              <span className=\"text-muted-foreground\">/ {numPages}</span>\n            </div>\n            <button\n              type=\"button\"\n              onClick={goToNextPage}\n              disabled={currentPage >= numPages}\n              className=\"px-2 py-1 text-sm border border-border rounded bg-background hover:bg-muted disabled:opacity-50 disabled:cursor-not-allowed\"\n            >\n              →\n            </button>\n          </div>\n        )}\n\n        {/* Zoom Controls */}\n        <div className=\"flex items-center gap-2\">\n          <button\n            type=\"button\"\n            onClick={handleZoomOut}\n            disabled={zoom <= 0.5}\n            className=\"px-2 py-1 text-sm border border-border rounded bg-background hover:bg-muted disabled:opacity-50 disabled:cursor-not-allowed\"\n          >\n            −\n          </button>\n          <span className=\"text-sm text-muted-foreground min-w-[3rem] text-center\">\n            {Math.round(zoom * 100)}%\n          </span>\n          <button\n            type=\"button\"\n            onClick={handleZoomIn}\n            disabled={zoom >= 2.0}\n            className=\"px-2 py-1 text-sm border border-border rounded bg-background hover:bg-muted disabled:opacity-50 disabled:cursor-not-allowed\"\n          >\n            +\n          </button>\n          <button\n            type=\"button\"\n            onClick={handleFitWidth}\n            className=\"px-2 py-1 text-xs border border-border rounded bg-background hover:bg-muted\"\n          >\n            Fit\n          </button>\n        </div>\n      </div>\n\n      {/* PDF Document */}\n      <div\n        ref={containerRef}\n        className={cn(\n          \"flex-1 overflow-auto bg-muted/30\",\n          viewMode === \"scroll\" && \"p-4\",\n          viewMode !== \"scroll\" && \"flex items-start justify-center p-4\",\n        )}\n      >\n        <Document\n          file={file}\n          onLoadSuccess={onDocumentLoadSuccess}\n          loading={\n            <div className=\"flex items-center justify-center p-8\">\n              <div className=\"text-sm text-muted-foreground\">\n                Loading PDF...\n              </div>\n            </div>\n          }\n          error={\n            <div className=\"flex items-center justify-center p-8\">\n              <div className=\"text-sm text-destructive\">\n                Failed to load PDF. Please check the file or URL.\n              </div>\n            </div>\n          }\n          className={cn(\n            viewMode === \"scroll\" && \"space-y-4\",\n            viewMode === \"book\" && \"flex gap-4\",\n          )}\n        >\n          {viewMode === \"scroll\" && (\n            <>\n              {Array.from(new Array(numPages), (_, index) => (\n                <div key={`page_${index + 1}`} className=\"flex justify-center\">\n                  <Page\n                    pageNumber={index + 1}\n                    width={pageWidth}\n                    className=\"shadow-lg\"\n                    loading={\n                      <div className=\"h-[800px] w-full bg-background animate-pulse rounded\" />\n                    }\n                  />\n                </div>\n              ))}\n            </>\n          )}\n\n          {viewMode === \"single\" && (\n            <div className=\"flex justify-center\">\n              <Page\n                pageNumber={currentPage}\n                width={pageWidth}\n                className=\"shadow-lg\"\n                loading={\n                  <div className=\"h-[800px] w-full bg-background animate-pulse rounded\" />\n                }\n              />\n            </div>\n          )}\n\n          {viewMode === \"book\" && (\n            <>\n              <div className=\"flex justify-end\">\n                <Page\n                  pageNumber={currentPage}\n                  width={pageWidth}\n                  className=\"shadow-lg\"\n                  loading={\n                    <div className=\"h-[800px] w-full bg-background animate-pulse rounded\" />\n                  }\n                />\n              </div>\n              {!showCoverAlone &&\n                bookSecondPage &&\n                bookSecondPage <= numPages && (\n                  <div className=\"flex justify-start\">\n                    <Page\n                      pageNumber={bookSecondPage}\n                      width={pageWidth}\n                      className=\"shadow-lg\"\n                      loading={\n                        <div className=\"h-[800px] w-full bg-background animate-pulse rounded\" />\n                      }\n                    />\n                  </div>\n                )}\n            </>\n          )}\n        </Document>\n      </div>\n    </div>\n  );\n}\n\nexport type { PdfViewerProps, ViewMode };\n",
      "type": "registry:component"
    }
  ],
  "docs": "Display PDFs from URL or File with three viewing modes: single page navigation, continuous scroll, or book layout with smart cover handling. Includes zoom controls and page navigation.",
  "categories": [
    "pdf"
  ],
  "type": "registry:ui"
}