@monime/pdfio 0.1.10 → 0.1.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/PdfViewer.d.ts.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +400 -314
- package/dist/index.js.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +1 -1
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":[],"sources":["../src/lib/Icons.tsx","../src/lib/PdfViewer.tsx"],"sourcesContent":["type IconProps = {\n size?: number;\n className?: string;\n};\n\nconst Icon = ({\n size = 24,\n className,\n children,\n}: IconProps & { children: React.ReactNode }) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n >\n {children}\n </svg>\n);\n\nexport const Icons = {\n ChevronLeft: ({ size = 24, className }: IconProps) => (\n <Icon size={size} className={className}>\n <path d=\"m15 18-6-6 6-6\" />\n </Icon>\n ),\n ChevronRight: ({ size = 24, className }: IconProps) => (\n <Icon size={size} className={className}>\n <path d=\"m9 18 6-6-6-6\" />\n </Icon>\n ),\n ZoomIn: ({ size = 24, className }: IconProps) => (\n <Icon size={size} className={className}>\n <circle cx=\"11\" cy=\"11\" r=\"8\" />\n <line x1=\"21\" x2=\"16.65\" y1=\"21\" y2=\"16.65\" />\n <line x1=\"11\" x2=\"11\" y1=\"8\" y2=\"14\" />\n <line x1=\"8\" x2=\"14\" y1=\"11\" y2=\"11\" />\n </Icon>\n ),\n ZoomOut: ({ size = 24, className }: IconProps) => (\n <Icon size={size} className={className}>\n <circle cx=\"11\" cy=\"11\" r=\"8\" />\n <line x1=\"21\" x2=\"16.65\" y1=\"21\" y2=\"16.65\" />\n <line x1=\"8\" x2=\"14\" y1=\"11\" y2=\"11\" />\n </Icon>\n ),\n Maximize: ({ size = 24, className }: IconProps) => (\n <Icon size={size} className={className}>\n <path d=\"M8 3H5a2 2 0 0 0-2 2v3\" />\n <path d=\"M21 8V5a2 2 0 0 0-2-2h-3\" />\n <path d=\"M3 16v3a2 2 0 0 0 2 2h3\" />\n <path d=\"M16 21h3a2 2 0 0 0 2-2v-3\" />\n </Icon>\n ),\n Minimize: ({ size = 24, className }: IconProps) => (\n <Icon size={size} className={className}>\n <path d=\"M8 3v3a2 2 0 0 1-2 2H3\" />\n <path d=\"M21 8h-3a2 2 0 0 1-2-2V3\" />\n <path d=\"M3 16h3a2 2 0 0 1 2 2v3\" />\n <path d=\"M16 21v-3a2 2 0 0 1 2-2h3\" />\n </Icon>\n ),\n ArrowLeftRight: ({ size = 24, className }: IconProps) => (\n <Icon size={size} className={className}>\n <path d=\"M8 3 4 7l4 4\" />\n <path d=\"M4 7h16\" />\n <path d=\"m16 21 4-4-4-4\" />\n <path d=\"M20 17H4\" />\n </Icon>\n ),\n ArrowUpDown: ({ size = 24, className }: IconProps) => (\n <Icon size={size} className={className}>\n <path d=\"m21 16-4 4-4-4\" />\n <path d=\"M17 20V4\" />\n <path d=\"m3 8 4-4 4 4\" />\n <path d=\"M7 4v16\" />\n </Icon>\n ),\n Loader2: ({ size = 24, className }: IconProps) => (\n <Icon size={size} className={className}>\n <path d=\"M21 12a9 9 0 1 1-6.219-8.56\" />\n </Icon>\n ),\n AlertTriangle: ({ size = 24, className }: IconProps) => (\n <Icon size={size} className={className}>\n <path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z\" />\n <line x1=\"12\" x2=\"12\" y1=\"9\" y2=\"13\" />\n <line x1=\"12\" x2=\"12.01\" y1=\"17\" y2=\"17\" />\n </Icon>\n ),\n FileText: ({ size = 24, className }: IconProps) => (\n <Icon size={size} className={className}>\n <path d=\"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z\" />\n <path d=\"M14 2v4a2 2 0 0 0 2 2h4\" />\n <path d=\"M10 9H8\" />\n <path d=\"M16 13H8\" />\n <path d=\"M16 17H8\" />\n </Icon>\n ),\n Download: ({ size = 24, className }: IconProps) => (\n <Icon size={size} className={className}>\n <path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\" />\n <polyline points=\"7 10 12 15 17 10\" />\n <line x1=\"12\" x2=\"12\" y1=\"15\" y2=\"3\" />\n </Icon>\n ),\n Printer: ({ size = 24, className }: IconProps) => (\n <Icon size={size} className={className}>\n <polyline points=\"6 9 6 2 18 2 18 9\" />\n <path d=\"M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2\" />\n <rect width=\"12\" height=\"8\" x=\"6\" y=\"14\" />\n </Icon>\n ),\n PanelLeft: ({ size = 24, className }: IconProps) => (\n <Icon size={size} className={className}>\n <rect width=\"18\" height=\"18\" x=\"3\" y=\"3\" rx=\"2\" />\n <path d=\"M9 3v18\" />\n </Icon>\n ),\n};\n","\"use client\";\n\nimport {\n useCallback,\n useEffect,\n useRef,\n useState,\n type KeyboardEvent,\n} from \"react\";\nimport type { PDFDocumentProxy, RenderTask } from \"pdfjs-dist\";\nimport { Icons } from \"./Icons\";\n\n// ── Watermark PDF generation ─────────────────────────────────────────────────\n\nfunction parseCssColor(cssColor: string): [number, number, number, number] {\n const canvas = document.createElement(\"canvas\");\n canvas.width = canvas.height = 1;\n const ctx = canvas.getContext(\"2d\")!;\n ctx.fillStyle = cssColor;\n ctx.fillRect(0, 0, 1, 1);\n const d = ctx.getImageData(0, 0, 1, 1).data;\n return [d[0] / 255, d[1] / 255, d[2] / 255, d[3] / 255];\n}\n\n/**\n * Generates a PDF with the watermark text baked into every page.\n * If no watermark config is provided the original PDF bytes are returned as-is.\n */\nexport async function generateWatermarkedPdf(\n src: NonNullable<PDFViewerProps[\"src\"]>,\n watermark?: WatermarkConfig | string,\n): Promise<Uint8Array> {\n const { PDFDocument, rgb, degrees, StandardFonts } = await import(\"pdf-lib\");\n\n // Resolve source to raw bytes\n let pdfBytes: ArrayBuffer;\n if (src instanceof File || src instanceof Blob) {\n pdfBytes = await src.arrayBuffer();\n } else if (src instanceof ArrayBuffer) {\n pdfBytes = src;\n } else {\n const url = src instanceof URL ? src.toString() : src;\n const resp = await fetch(url);\n pdfBytes = await resp.arrayBuffer();\n }\n\n const pdfDoc = await PDFDocument.load(pdfBytes);\n\n const watermarkConfig: WatermarkConfig | undefined =\n watermark === undefined\n ? undefined\n : typeof watermark === \"string\"\n ? { text: watermark }\n : watermark;\n\n if (!watermarkConfig) {\n return pdfDoc.save();\n }\n\n const {\n text,\n color = \"rgba(0,0,0,0.12)\",\n fontSize = 28,\n angle = -35,\n gap = 80,\n fontWeight = \"600\",\n } = watermarkConfig;\n\n const [cr, cg, cb, ca] = parseCssColor(color);\n const font = await pdfDoc.embedFont(\n Number(fontWeight) >= 600\n ? StandardFonts.HelveticaBold\n : StandardFonts.Helvetica,\n );\n\n const estimatedCharWidth = 0.55;\n const tileW = text.length * fontSize * estimatedCharWidth + gap;\n const tileH = fontSize * 3;\n // Rotation in radians — angle follows the SVG convention (CW positive).\n // pdf-lib uses CCW positive, so we negate when calling degrees().\n const rad = (angle * Math.PI) / 180;\n const cosA = Math.cos(rad);\n const sinA = Math.sin(rad);\n\n for (const page of pdfDoc.getPages()) {\n const { width, height } = page.getSize();\n const cx = width / 2;\n // pdf-lib Y-origin is bottom-left; page centre in pdf-lib coords:\n const cy = height / 2;\n const diag = Math.sqrt(width * width + height * height);\n const cols = Math.ceil(diag / tileW) + 2;\n const rows = Math.ceil(diag / tileH) + 2;\n const textWidth = font.widthOfTextAtSize(text, fontSize);\n\n for (let col = -cols; col <= cols; col++) {\n for (let row = -rows; row <= rows; row++) {\n // Tile position in the rotated coordinate frame (centred at page centre).\n const rx = col * tileW;\n // Negate ry so the tiling direction mirrors the SVG (SVG Y goes down).\n const ry = -(row * tileH);\n\n // Rotate back to page coordinate space.\n const px = cx + rx * cosA - ry * sinA;\n const py = cy + rx * sinA + ry * cosA;\n\n // Skip tiles that land outside the page (generous margin).\n const margin = fontSize * 4;\n if (\n px < -margin ||\n px > width + margin ||\n py < -margin ||\n py > height + margin\n ) {\n continue;\n }\n\n page.drawText(text, {\n x: px - textWidth / 2,\n y: py - fontSize / 2,\n size: fontSize,\n font,\n color: rgb(cr, cg, cb),\n opacity: ca,\n // pdf-lib rotates CCW; SVG rotates CW — negate to match visuals.\n rotate: degrees(-angle),\n });\n }\n }\n }\n\n return pdfDoc.save();\n}\n\nconst MIN_SCALE = 0.25;\nconst MAX_SCALE = 4;\nconst ZOOM_STEP = 0.25;\nconst PAGE_GAP = 16; // px between pages\nconst PRESETS = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 2, 3, 4];\nconst THUMB_WIDTH = 120; // target thumbnail width in px\n\nfunction clamp(v: number, min: number, max: number) {\n return Math.min(max, Math.max(min, v));\n}\n\nexport interface WatermarkConfig {\n text: string;\n /** CSS color string — default: \"rgba(0,0,0,0.12)\" */\n color?: string;\n /** Font size in px at scale=1 — default: 28 */\n fontSize?: number;\n /** Rotation angle in degrees — default: -35 */\n angle?: number;\n /** Horizontal gap between repeated labels in px — default: 80 */\n gap?: number;\n /** Font family — default: \"sans-serif\" */\n fontFamily?: string;\n /** Font weight — default: \"600\" */\n fontWeight?: string;\n}\n\nexport interface PDFViewerProps {\n src?: string | ArrayBuffer | URL | Blob | File;\n /** Pass a string for a quick watermark, or a WatermarkConfig for full control. */\n watermark?: string | WatermarkConfig;\n print?: boolean;\n download?: boolean;\n /** Show the thumbnail sidebar (default: true) */\n sidebar?: boolean;\n}\n\ntype PageSize = { width: number; height: number };\n\nfunction WatermarkOverlay({\n config,\n width,\n height,\n}: {\n config: WatermarkConfig;\n width: number;\n height: number;\n}) {\n const {\n text,\n color = \"rgba(0,0,0,0.12)\",\n fontSize = 28,\n angle = -35,\n gap = 80,\n fontFamily = \"sans-serif\",\n fontWeight = \"600\",\n } = config;\n\n // Estimate text width so tiles don't overlap horizontally.\n const estimatedTextWidth = text.length * fontSize * 0.55;\n const tileW = estimatedTextWidth + gap;\n const tileH = fontSize * 3;\n // Unique id per text so multiple viewers on the same page don't clash.\n const patternId = `wm-${text.replace(/\\W/g, \"\").slice(0, 12)}-${Math.round(fontSize)}`;\n\n return (\n <svg\n aria-hidden=\"true\"\n style={{\n position: \"absolute\",\n inset: 0,\n width,\n height,\n pointerEvents: \"none\",\n userSelect: \"none\",\n }}\n width={width}\n height={height}\n >\n <defs>\n <pattern\n id={patternId}\n x=\"0\"\n y=\"0\"\n width={tileW}\n height={tileH}\n patternUnits=\"userSpaceOnUse\"\n patternTransform={`rotate(${angle}, ${width / 2}, ${height / 2})`}\n >\n <text\n x={tileW / 2}\n y={tileH / 2}\n textAnchor=\"middle\"\n dominantBaseline=\"middle\"\n fill={color}\n fontSize={fontSize}\n fontFamily={fontFamily}\n fontWeight={fontWeight}\n letterSpacing=\"0.05em\"\n >\n {text}\n </text>\n </pattern>\n </defs>\n <rect width={width} height={height} fill={`url(#${patternId})`} />\n </svg>\n );\n}\n\nexport function PDFViewer({\n src,\n watermark,\n print = true,\n download = true,\n sidebar = true,\n}: PDFViewerProps) {\n // ── Refs ────────────────────────────────────────────────────────────────────\n const containerRef = useRef<HTMLDivElement>(null);\n const scrollRef = useRef<HTMLDivElement>(null);\n const sidebarScrollRef = useRef<HTMLDivElement>(null);\n const pageInputRef = useRef<HTMLInputElement>(null);\n const pdfRef = useRef<PDFDocumentProxy | null>(null);\n const pageWrapperRefs = useRef(new Map<number, HTMLDivElement>());\n const canvasRefs = useRef(new Map<number, HTMLCanvasElement>());\n const renderTasksRef = useRef(new Map<number, RenderTask>());\n const thumbCanvasRefs = useRef(new Map<number, HTMLCanvasElement>());\n const thumbRenderTasksRef = useRef(new Map<number, RenderTask>());\n const thumbWrapperRefs = useRef(new Map<number, HTMLDivElement>());\n\n // ── State ───────────────────────────────────────────────────────────────────\n const [numPages, setNumPages] = useState(0);\n const [pageSizes, setPageSizes] = useState<PageSize[]>([]);\n const [currentPage, setCurrentPage] = useState(1);\n const [scale, setScale] = useState(1.0);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [isFullscreen, setIsFullscreen] = useState(false);\n const [isEditingPage, setIsEditingPage] = useState(false);\n const [pageInput, setPageInput] = useState(\"\");\n const [isExporting, setIsExporting] = useState(false);\n const [sidebarOpen, setSidebarOpen] = useState(sidebar);\n\n // ── Load document + collect page sizes ─────────────────────────────────────\n\n useEffect(() => {\n let cancelled = false;\n\n async function load() {\n if (src === undefined) {\n setLoading(false);\n setError(null);\n setNumPages(0);\n setPageSizes([]);\n setCurrentPage(1);\n return;\n }\n\n setLoading(true);\n setError(null);\n setNumPages(0);\n setPageSizes([]);\n setCurrentPage(1);\n\n try {\n const { GlobalWorkerOptions, getDocument } = await import(\"pdfjs-dist\");\n GlobalWorkerOptions.workerSrc = new URL(\n \"pdfjs-dist/build/pdf.worker.min.mjs\",\n import.meta.url,\n ).toString();\n\n let objectUrl: string | undefined;\n if (src instanceof Blob || src instanceof File) {\n objectUrl = URL.createObjectURL(src);\n src = objectUrl;\n }\n\n try {\n const loadTask = getDocument(src);\n const pdf = await loadTask.promise;\n if (cancelled) {\n void pdf.destroy();\n return;\n }\n\n void pdfRef.current?.destroy();\n pdfRef.current = pdf;\n\n // Collect natural sizes at scale=1 for all pages\n const sizes: PageSize[] = [];\n for (let i = 1; i <= pdf.numPages; i++) {\n if (cancelled) return;\n const p = await pdf.getPage(i);\n const vp = p.getViewport({ scale: 1 });\n sizes.push({ width: vp.width, height: vp.height });\n }\n if (cancelled) return;\n\n setNumPages(pdf.numPages);\n setPageSizes(sizes);\n } finally {\n if (objectUrl) URL.revokeObjectURL(objectUrl);\n }\n } catch (e) {\n if (!cancelled)\n setError(e instanceof Error ? e.message : \"Failed to load PDF.\");\n } finally {\n if (!cancelled) setLoading(false);\n }\n }\n\n void load();\n return () => {\n cancelled = true;\n };\n }, [src]);\n\n // ── Render all pages whenever pageSizes or scale changes ───────────────────\n\n useEffect(() => {\n const pdf = pdfRef.current;\n if (!pdf || pageSizes.length === 0) return;\n\n let cancelled = false;\n\n async function renderAll() {\n for (let pageNum = 1; pageNum <= pageSizes.length; pageNum++) {\n if (cancelled) return;\n\n const canvas = canvasRefs.current.get(pageNum);\n if (!canvas) continue;\n\n renderTasksRef.current.get(pageNum)?.cancel();\n\n try {\n const pdfPage = await pdf!.getPage(pageNum);\n if (cancelled) return;\n\n const viewport = pdfPage.getViewport({ scale });\n const dpr = window.devicePixelRatio || 1;\n\n canvas.width = Math.floor(viewport.width * dpr);\n canvas.height = Math.floor(viewport.height * dpr);\n canvas.style.width = `${viewport.width}px`;\n canvas.style.height = `${viewport.height}px`;\n\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) continue;\n\n const task = pdfPage.render({\n canvas,\n canvasContext: ctx,\n viewport,\n transform: dpr !== 1 ? [dpr, 0, 0, dpr, 0, 0] : undefined,\n });\n renderTasksRef.current.set(pageNum, task);\n await task.promise;\n } catch {\n // cancelled render — ignore\n }\n }\n }\n\n void renderAll();\n\n return () => {\n cancelled = true;\n renderTasksRef.current.forEach((t) => t.cancel());\n renderTasksRef.current.clear();\n };\n }, [pageSizes, scale]);\n\n // ── Scroll listener — track current page ──────────────────────────────────\n\n useEffect(() => {\n const container = scrollRef.current;\n if (!container || pageSizes.length === 0) return;\n\n function update() {\n const mid = container!.scrollTop + container!.clientHeight / 2;\n let found = 1;\n for (let i = 1; i <= pageSizes.length; i++) {\n const el = pageWrapperRefs.current.get(i);\n if (!el) continue;\n if (el.offsetTop <= mid) found = i;\n else break;\n }\n setCurrentPage(found);\n }\n\n update(); // set on mount\n container.addEventListener(\"scroll\", update, { passive: true });\n return () => container.removeEventListener(\"scroll\", update);\n }, [pageSizes.length]);\n\n // ── Render thumbnails whenever pageSizes change or sidebar opens ───────────\n\n useEffect(() => {\n if (!sidebarOpen) return;\n const pdf = pdfRef.current;\n if (!pdf || pageSizes.length === 0) return;\n\n let cancelled = false;\n\n async function renderThumbs() {\n for (let pageNum = 1; pageNum <= pageSizes.length; pageNum++) {\n if (cancelled) return;\n\n const canvas = thumbCanvasRefs.current.get(pageNum);\n if (!canvas) continue;\n\n thumbRenderTasksRef.current.get(pageNum)?.cancel();\n\n try {\n const pdfPage = await pdf!.getPage(pageNum);\n if (cancelled) return;\n\n const size = pageSizes[pageNum - 1];\n const thumbScale = THUMB_WIDTH / size.width;\n const viewport = pdfPage.getViewport({ scale: thumbScale });\n const dpr = window.devicePixelRatio || 1;\n\n canvas.width = Math.floor(viewport.width * dpr);\n canvas.height = Math.floor(viewport.height * dpr);\n canvas.style.width = `${viewport.width}px`;\n canvas.style.height = `${viewport.height}px`;\n\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) continue;\n\n const task = pdfPage.render({\n canvas,\n canvasContext: ctx,\n viewport,\n transform: dpr !== 1 ? [dpr, 0, 0, dpr, 0, 0] : undefined,\n });\n thumbRenderTasksRef.current.set(pageNum, task);\n await task.promise;\n } catch {\n // cancelled render — ignore\n }\n }\n }\n\n void renderThumbs();\n\n return () => {\n cancelled = true;\n thumbRenderTasksRef.current.forEach((t) => t.cancel());\n thumbRenderTasksRef.current.clear();\n };\n }, [pageSizes, sidebarOpen]);\n\n // ── Auto-scroll sidebar to keep active thumbnail visible ──────────────────\n\n useEffect(() => {\n if (!sidebarOpen) return;\n const container = sidebarScrollRef.current;\n const thumb = thumbWrapperRefs.current.get(currentPage);\n if (!container || !thumb) return;\n const thumbTop = thumb.offsetTop;\n const thumbBottom = thumbTop + thumb.offsetHeight;\n const scrollTop = container.scrollTop;\n const containerHeight = container.clientHeight;\n if (thumbTop < scrollTop || thumbBottom > scrollTop + containerHeight) {\n container.scrollTop =\n thumbTop - containerHeight / 2 + thumb.offsetHeight / 2;\n }\n }, [currentPage, sidebarOpen]);\n\n // ── Fullscreen ──────────────────────────────────────────────────────────────\n\n useEffect(() => {\n function onChange() {\n setIsFullscreen(!!document.fullscreenElement);\n }\n document.addEventListener(\"fullscreenchange\", onChange);\n return () => document.removeEventListener(\"fullscreenchange\", onChange);\n }, []);\n\n // ── Ref callbacks ───────────────────────────────────────────────────────────\n\n const setPageWrapper = useCallback(\n (pageNum: number) => (el: HTMLDivElement | null) => {\n if (el) pageWrapperRefs.current.set(pageNum, el);\n else pageWrapperRefs.current.delete(pageNum);\n },\n [],\n );\n\n const setCanvas = useCallback(\n (pageNum: number) => (el: HTMLCanvasElement | null) => {\n if (el) canvasRefs.current.set(pageNum, el);\n else canvasRefs.current.delete(pageNum);\n },\n [],\n );\n\n const setThumbCanvas = useCallback(\n (pageNum: number) => (el: HTMLCanvasElement | null) => {\n if (el) thumbCanvasRefs.current.set(pageNum, el);\n else thumbCanvasRefs.current.delete(pageNum);\n },\n [],\n );\n\n const setThumbWrapper = useCallback(\n (pageNum: number) => (el: HTMLDivElement | null) => {\n if (el) thumbWrapperRefs.current.set(pageNum, el);\n else thumbWrapperRefs.current.delete(pageNum);\n },\n [],\n );\n\n // ── Actions ─────────────────────────────────────────────────────────────────\n\n const scrollToPage = useCallback(\n (n: number) => {\n const p = clamp(n, 1, numPages);\n const target = pageWrapperRefs.current.get(p);\n const container = scrollRef.current;\n if (!target || !container) return;\n container.scrollTop = target.offsetTop - PAGE_GAP;\n },\n [numPages],\n );\n\n const zoomTo = useCallback(\n (s: number) => setScale(clamp(s, MIN_SCALE, MAX_SCALE)),\n [],\n );\n\n function fitToWidth() {\n const container = scrollRef.current;\n if (!container || pageSizes.length === 0) return;\n const available = container.clientWidth - PAGE_GAP * 2;\n zoomTo(available / pageSizes[0].width);\n }\n\n function fitToHeight() {\n const container = scrollRef.current;\n if (!container || pageSizes.length === 0) return;\n const available = container.clientHeight - PAGE_GAP * 2;\n zoomTo(available / pageSizes[0].height);\n }\n\n function toggleFullscreen() {\n if (isFullscreen) document.exitFullscreen();\n else containerRef.current?.requestFullscreen();\n }\n\n function startEditingPage() {\n setPageInput(String(currentPage));\n setIsEditingPage(true);\n setTimeout(() => pageInputRef.current?.select(), 0);\n }\n\n function commitPageEdit() {\n const n = parseInt(pageInput, 10);\n if (!isNaN(n)) scrollToPage(n);\n setIsEditingPage(false);\n }\n\n function handlePageKey(e: KeyboardEvent<HTMLInputElement>) {\n if (e.key === \"Enter\") commitPageEdit();\n else if (e.key === \"Escape\") setIsEditingPage(false);\n }\n\n const canPrev = currentPage > 1;\n const canNext = currentPage < numPages;\n\n // Normalise the watermark prop into a config object (or undefined).\n const watermarkConfig: WatermarkConfig | undefined =\n watermark === undefined\n ? undefined\n : typeof watermark === \"string\"\n ? { text: watermark }\n : watermark;\n\n async function handleDownloadWithWatermark() {\n if (!src || isExporting) return;\n setIsExporting(true);\n try {\n const bytes = await generateWatermarkedPdf(src, watermarkConfig);\n const blob = new Blob([bytes.buffer.slice(0) as ArrayBuffer], {\n type: \"application/pdf\",\n });\n const url = URL.createObjectURL(blob);\n const a = document.createElement(\"a\");\n a.href = url;\n a.download =\n src instanceof File ? src.name || \"document.pdf\" : \"document.pdf\";\n a.click();\n setTimeout(() => URL.revokeObjectURL(url), 10_000);\n } finally {\n setIsExporting(false);\n }\n }\n\n async function handlePrintWithWatermark() {\n if (!src || isExporting) return;\n setIsExporting(true);\n try {\n const bytes = await generateWatermarkedPdf(src, watermarkConfig);\n const blob = new Blob([bytes.buffer.slice(0) as ArrayBuffer], {\n type: \"application/pdf\",\n });\n const url = URL.createObjectURL(blob);\n const iframe = document.createElement(\"iframe\");\n iframe.style.cssText =\n \"position:fixed;width:0;height:0;opacity:0;border:0\";\n iframe.src = url;\n document.body.appendChild(iframe);\n iframe.onload = () => {\n iframe.contentWindow?.focus();\n iframe.contentWindow?.print();\n setTimeout(() => {\n document.body.removeChild(iframe);\n URL.revokeObjectURL(url);\n }, 60_000);\n };\n } finally {\n setIsExporting(false);\n }\n }\n\n // ── Render ──────────────────────────────────────────────────────────────────\n\n return (\n <div\n ref={containerRef}\n className=\"flex h-full flex-col bg-white text-neutral-800 border border-gray-200\"\n >\n {/* ── Toolbar ──────────────────────────────────────────────────────── */}\n <div className=\"flex shrink-0 items-center gap-1 border-b border-neutral-200 bg-white px-3 py-2 shadow-sm\">\n {/* Sidebar toggle */}\n {sidebar && (\n <>\n <button\n type=\"button\"\n onClick={() => setSidebarOpen((o) => !o)}\n title={sidebarOpen ? \"Hide thumbnails\" : \"Show thumbnails\"}\n className={`flex h-8 w-8 items-center justify-center rounded-md transition hover:bg-neutral-100 ${sidebarOpen ? \"bg-neutral-100 text-blue-600\" : \"\"}`}\n >\n <Icons.PanelLeft size={16} />\n </button>\n <div className=\"mx-1 h-5 w-px bg-neutral-200\" />\n </>\n )}\n\n {/* Prev page */}\n <button\n type=\"button\"\n onClick={() => scrollToPage(currentPage - 1)}\n disabled={!canPrev}\n title=\"Previous page\"\n className=\"flex h-8 w-8 items-center justify-center rounded-md transition hover:bg-neutral-100 disabled:cursor-not-allowed disabled:opacity-30\"\n >\n <Icons.ChevronLeft size={18} />\n </button>\n\n {/* Page indicator / jump input */}\n {isEditingPage ? (\n <input\n ref={pageInputRef}\n type=\"number\"\n min={1}\n max={numPages}\n value={pageInput}\n onChange={(e) => setPageInput(e.target.value)}\n onBlur={commitPageEdit}\n onKeyDown={handlePageKey}\n className=\"w-14 rounded-md border border-neutral-300 bg-white px-2 py-0.5 text-center text-sm outline-none focus:border-blue-500\"\n />\n ) : (\n <button\n type=\"button\"\n onClick={numPages > 0 ? startEditingPage : undefined}\n disabled={numPages === 0}\n title=\"Click to jump to page\"\n className=\"flex items-center gap-1 rounded-md px-2 py-0.5 text-sm transition hover:bg-neutral-100 disabled:opacity-40\"\n >\n <span className=\"font-medium tabular-nums\">\n {numPages > 0 ? currentPage : \"—\"}\n </span>\n <span className=\"text-neutral-400\">/</span>\n <span className=\"tabular-nums text-neutral-500\">\n {numPages > 0 ? numPages : \"—\"}\n </span>\n </button>\n )}\n\n {/* Next page */}\n <button\n type=\"button\"\n onClick={() => scrollToPage(currentPage + 1)}\n disabled={!canNext}\n title=\"Next page\"\n className=\"flex h-8 w-8 items-center justify-center rounded-md transition hover:bg-neutral-100 disabled:cursor-not-allowed disabled:opacity-30\"\n >\n <Icons.ChevronRight size={18} />\n </button>\n\n <div className=\"mx-2 h-5 w-px bg-neutral-200\" />\n\n {/* Zoom out */}\n <button\n type=\"button\"\n onClick={() => zoomTo(scale - ZOOM_STEP)}\n disabled={scale <= MIN_SCALE}\n title=\"Zoom out\"\n className=\"flex h-8 w-8 items-center justify-center rounded-md transition hover:bg-neutral-100 disabled:cursor-not-allowed disabled:opacity-30\"\n >\n <Icons.ZoomOut size={16} />\n </button>\n\n {/* Zoom preset dropdown — always shows current value */}\n <select\n value={scale}\n onChange={(e) => zoomTo(parseFloat(e.target.value))}\n title=\"Zoom level\"\n className=\"w-[5.5rem] rounded-md border border-neutral-300 bg-white px-1.5 py-0.5 text-xs text-neutral-700 outline-none focus:border-blue-500\"\n >\n {!PRESETS.some((p) => Math.abs(p - scale) < 0.001) && (\n <option value={scale}>{Math.round(scale * 100)}%</option>\n )}\n {PRESETS.map((v) => (\n <option key={v} value={v}>\n {Math.round(v * 100)}%\n </option>\n ))}\n </select>\n\n {/* Zoom in */}\n <button\n type=\"button\"\n onClick={() => zoomTo(scale + ZOOM_STEP)}\n disabled={scale >= MAX_SCALE}\n title=\"Zoom in\"\n className=\"flex h-8 w-8 items-center justify-center rounded-md transition hover:bg-neutral-100 disabled:cursor-not-allowed disabled:opacity-30\"\n >\n <Icons.ZoomIn size={16} />\n </button>\n\n <div className=\"mx-1 h-5 w-px bg-neutral-200\" />\n\n {/* Fit to width */}\n <button\n type=\"button\"\n onClick={fitToWidth}\n disabled={pageSizes.length === 0}\n title=\"Fit to width\"\n className=\"flex h-8 w-8 items-center justify-center rounded-md transition hover:bg-neutral-100 disabled:cursor-not-allowed disabled:opacity-30\"\n >\n <Icons.ArrowLeftRight size={15} />\n </button>\n\n {/* Fit to height */}\n <button\n type=\"button\"\n onClick={fitToHeight}\n disabled={pageSizes.length === 0}\n title=\"Fit to height\"\n className=\"flex h-8 w-8 items-center justify-center rounded-md transition hover:bg-neutral-100 disabled:cursor-not-allowed disabled:opacity-30\"\n >\n <Icons.ArrowUpDown size={15} />\n </button>\n\n <div className=\"flex-1\" />\n\n {/* Download with watermark */}\n {download && (\n <button\n type=\"button\"\n onClick={handleDownloadWithWatermark}\n disabled={numPages === 0 || isExporting}\n title={watermarkConfig ? \"Download with watermark\" : \"Download\"}\n className=\"flex h-8 w-8 items-center justify-center rounded-md transition hover:bg-neutral-100 disabled:cursor-not-allowed disabled:opacity-30\"\n >\n {isExporting ? (\n <Icons.Loader2 size={15} className=\"animate-spin\" />\n ) : (\n <Icons.Download size={15} />\n )}\n </button>\n )}\n\n {/* Print with watermark */}\n {print && (\n <button\n type=\"button\"\n onClick={handlePrintWithWatermark}\n disabled={numPages === 0 || isExporting}\n title={watermarkConfig ? \"Print with watermark\" : \"Print\"}\n className=\"flex h-8 w-8 items-center justify-center rounded-md transition hover:bg-neutral-100 disabled:cursor-not-allowed disabled:opacity-30\"\n >\n <Icons.Printer size={15} />\n </button>\n )}\n\n <div className=\"mx-1 h-5 w-px bg-neutral-200\" />\n\n {/* Fullscreen */}\n <button\n type=\"button\"\n onClick={toggleFullscreen}\n title={isFullscreen ? \"Exit fullscreen\" : \"Enter fullscreen\"}\n className=\"flex h-8 w-8 items-center justify-center rounded-md transition hover:bg-neutral-100\"\n >\n {isFullscreen ? (\n <Icons.Minimize size={16} />\n ) : (\n <Icons.Maximize size={16} />\n )}\n </button>\n </div>\n\n {/* ── Body: sidebar + scroll area ──────────────────────────────────── */}\n <div className=\"flex flex-1 min-h-0\">\n {/* Sidebar thumbnails */}\n {sidebar && sidebarOpen && (\n <div\n ref={sidebarScrollRef}\n className=\"flex flex-col gap-1 w-[152px] shrink-0 overflow-y-auto border-r border-neutral-200 bg-neutral-50 py-2 px-2\"\n >\n {pageSizes.map((_, idx) => {\n const pageNum = idx + 1;\n const isActive = currentPage === pageNum;\n return (\n <div\n key={pageNum}\n ref={setThumbWrapper(pageNum)}\n onClick={() => scrollToPage(pageNum)}\n title={`Page ${pageNum}`}\n className={`flex flex-col items-center gap-1.5 cursor-pointer rounded-lg p-1.5 transition-colors ${\n isActive ? \"bg-blue-50\" : \"hover:bg-neutral-100\"\n }`}\n >\n <div\n className={`rounded shadow-sm overflow-hidden ring-2 transition-colors ${\n isActive ? \"ring-blue-500\" : \"ring-transparent\"\n }`}\n >\n <canvas\n ref={setThumbCanvas(pageNum)}\n className=\"block\"\n onContextMenu={(e) => e.preventDefault()}\n />\n </div>\n <span\n className={`text-[11px] tabular-nums leading-none ${\n isActive\n ? \"text-blue-600 font-semibold\"\n : \"text-neutral-400\"\n }`}\n >\n {pageNum}\n </span>\n </div>\n );\n })}\n </div>\n )}\n\n {/* ── Scroll area ──────────────────────────────────────────────────── */}\n <div\n ref={scrollRef}\n className=\"flex-1 overflow-y-auto overflow-x-auto bg-white\"\n >\n {/* Loading */}\n {loading && (\n <div className=\"flex h-full items-center justify-center\">\n <div className=\"flex flex-col items-center gap-3 text-neutral-400\">\n <Icons.Loader2 size={32} className=\"animate-spin\" />\n <span className=\"text-sm\">Loading document…</span>\n </div>\n </div>\n )}\n\n {/* Error */}\n {!loading && error && (\n <div className=\"flex h-full items-center justify-center\">\n <div className=\"flex flex-col items-center gap-3 rounded-xl border border-red-200 bg-red-50 px-8 py-10 text-red-600\">\n <Icons.AlertTriangle size={32} />\n <p className=\"max-w-xs text-center text-sm\">{error}</p>\n </div>\n </div>\n )}\n\n {/* Empty */}\n {!loading && !error && numPages === 0 && (\n <div className=\"flex h-full items-center justify-center\">\n <div className=\"flex flex-col items-center gap-3 text-neutral-300\">\n <Icons.FileText size={48} />\n <span className=\"text-sm\">No document loaded</span>\n </div>\n </div>\n )}\n\n {/* Pages */}\n {!loading && !error && pageSizes.length > 0 && (\n <div\n className=\"flex flex-col items-center py-4\"\n style={{ gap: PAGE_GAP }}\n >\n {pageSizes.map((size, idx) => {\n const pageNum = idx + 1;\n return (\n <div\n key={pageNum}\n ref={setPageWrapper(pageNum)}\n data-page={pageNum}\n className=\"relative flex-shrink-0\"\n style={{\n width: Math.floor(size.width * scale),\n height: Math.floor(size.height * scale),\n }}\n >\n <canvas\n ref={setCanvas(pageNum)}\n className=\"block shadow-md ring-1 ring-black/10\"\n onContextMenu={(e) => e.preventDefault()}\n />\n {watermarkConfig && (\n <WatermarkOverlay\n config={{\n ...watermarkConfig,\n fontSize: (watermarkConfig.fontSize ?? 28) * scale,\n gap: (watermarkConfig.gap ?? 80) * scale,\n }}\n width={Math.floor(size.width * scale)}\n height={Math.floor(size.height * scale)}\n />\n )}\n </div>\n );\n })}\n </div>\n )}\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":[],"sources":["../src/lib/Icons.tsx","../src/lib/PdfViewer.tsx"],"sourcesContent":["type IconProps = {\n size?: number;\n className?: string;\n};\n\nconst Icon = ({\n size = 24,\n className,\n children,\n}: IconProps & { children: React.ReactNode }) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n width={size}\n height={size}\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n className={className}\n >\n {children}\n </svg>\n);\n\nexport const Icons = {\n ChevronLeft: ({ size = 24, className }: IconProps) => (\n <Icon size={size} className={className}>\n <path d=\"m15 18-6-6 6-6\" />\n </Icon>\n ),\n ChevronRight: ({ size = 24, className }: IconProps) => (\n <Icon size={size} className={className}>\n <path d=\"m9 18 6-6-6-6\" />\n </Icon>\n ),\n ZoomIn: ({ size = 24, className }: IconProps) => (\n <Icon size={size} className={className}>\n <circle cx=\"11\" cy=\"11\" r=\"8\" />\n <line x1=\"21\" x2=\"16.65\" y1=\"21\" y2=\"16.65\" />\n <line x1=\"11\" x2=\"11\" y1=\"8\" y2=\"14\" />\n <line x1=\"8\" x2=\"14\" y1=\"11\" y2=\"11\" />\n </Icon>\n ),\n ZoomOut: ({ size = 24, className }: IconProps) => (\n <Icon size={size} className={className}>\n <circle cx=\"11\" cy=\"11\" r=\"8\" />\n <line x1=\"21\" x2=\"16.65\" y1=\"21\" y2=\"16.65\" />\n <line x1=\"8\" x2=\"14\" y1=\"11\" y2=\"11\" />\n </Icon>\n ),\n Maximize: ({ size = 24, className }: IconProps) => (\n <Icon size={size} className={className}>\n <path d=\"M8 3H5a2 2 0 0 0-2 2v3\" />\n <path d=\"M21 8V5a2 2 0 0 0-2-2h-3\" />\n <path d=\"M3 16v3a2 2 0 0 0 2 2h3\" />\n <path d=\"M16 21h3a2 2 0 0 0 2-2v-3\" />\n </Icon>\n ),\n Minimize: ({ size = 24, className }: IconProps) => (\n <Icon size={size} className={className}>\n <path d=\"M8 3v3a2 2 0 0 1-2 2H3\" />\n <path d=\"M21 8h-3a2 2 0 0 1-2-2V3\" />\n <path d=\"M3 16h3a2 2 0 0 1 2 2v3\" />\n <path d=\"M16 21v-3a2 2 0 0 1 2-2h3\" />\n </Icon>\n ),\n ArrowLeftRight: ({ size = 24, className }: IconProps) => (\n <Icon size={size} className={className}>\n <path d=\"M8 3 4 7l4 4\" />\n <path d=\"M4 7h16\" />\n <path d=\"m16 21 4-4-4-4\" />\n <path d=\"M20 17H4\" />\n </Icon>\n ),\n ArrowUpDown: ({ size = 24, className }: IconProps) => (\n <Icon size={size} className={className}>\n <path d=\"m21 16-4 4-4-4\" />\n <path d=\"M17 20V4\" />\n <path d=\"m3 8 4-4 4 4\" />\n <path d=\"M7 4v16\" />\n </Icon>\n ),\n Loader2: ({ size = 24, className }: IconProps) => (\n <Icon size={size} className={className}>\n <path d=\"M21 12a9 9 0 1 1-6.219-8.56\" />\n </Icon>\n ),\n AlertTriangle: ({ size = 24, className }: IconProps) => (\n <Icon size={size} className={className}>\n <path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z\" />\n <line x1=\"12\" x2=\"12\" y1=\"9\" y2=\"13\" />\n <line x1=\"12\" x2=\"12.01\" y1=\"17\" y2=\"17\" />\n </Icon>\n ),\n FileText: ({ size = 24, className }: IconProps) => (\n <Icon size={size} className={className}>\n <path d=\"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z\" />\n <path d=\"M14 2v4a2 2 0 0 0 2 2h4\" />\n <path d=\"M10 9H8\" />\n <path d=\"M16 13H8\" />\n <path d=\"M16 17H8\" />\n </Icon>\n ),\n Download: ({ size = 24, className }: IconProps) => (\n <Icon size={size} className={className}>\n <path d=\"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4\" />\n <polyline points=\"7 10 12 15 17 10\" />\n <line x1=\"12\" x2=\"12\" y1=\"15\" y2=\"3\" />\n </Icon>\n ),\n Printer: ({ size = 24, className }: IconProps) => (\n <Icon size={size} className={className}>\n <polyline points=\"6 9 6 2 18 2 18 9\" />\n <path d=\"M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2\" />\n <rect width=\"12\" height=\"8\" x=\"6\" y=\"14\" />\n </Icon>\n ),\n PanelLeft: ({ size = 24, className }: IconProps) => (\n <Icon size={size} className={className}>\n <rect width=\"18\" height=\"18\" x=\"3\" y=\"3\" rx=\"2\" />\n <path d=\"M9 3v18\" />\n </Icon>\n ),\n};\n","\"use client\";\n\nimport {\n useCallback,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n type KeyboardEvent,\n} from \"react\";\nimport type { PDFDocumentProxy, RenderTask } from \"pdfjs-dist\";\nimport { Icons } from \"./Icons\";\n\n// ── Watermark PDF generation ─────────────────────────────────────────────────\n\nfunction parseCssColor(cssColor: string): [number, number, number, number] {\n const canvas = document.createElement(\"canvas\");\n canvas.width = canvas.height = 1;\n const ctx = canvas.getContext(\"2d\")!;\n ctx.fillStyle = cssColor;\n ctx.fillRect(0, 0, 1, 1);\n const d = ctx.getImageData(0, 0, 1, 1).data;\n return [d[0] / 255, d[1] / 255, d[2] / 255, d[3] / 255];\n}\n\n/**\n * Generates a PDF with the watermark text baked into every page.\n * If no watermark config is provided the original PDF bytes are returned as-is.\n */\nexport async function generateWatermarkedPdf(\n src: NonNullable<PDFViewerProps[\"src\"]>,\n watermark?: WatermarkConfig | string,\n): Promise<Uint8Array> {\n const { PDFDocument, rgb, degrees, StandardFonts } = await import(\"pdf-lib\");\n\n // Resolve source to raw bytes\n let pdfBytes: ArrayBuffer;\n if (src instanceof File || src instanceof Blob) {\n pdfBytes = await src.arrayBuffer();\n } else if (src instanceof ArrayBuffer) {\n pdfBytes = src;\n } else {\n const url = src instanceof URL ? src.toString() : src;\n const resp = await fetch(url);\n pdfBytes = await resp.arrayBuffer();\n }\n\n const pdfDoc = await PDFDocument.load(pdfBytes);\n\n const watermarkConfig: WatermarkConfig | undefined =\n watermark === undefined\n ? undefined\n : typeof watermark === \"string\"\n ? { text: watermark }\n : watermark;\n\n if (!watermarkConfig) {\n return pdfDoc.save();\n }\n\n const {\n text,\n color = \"rgba(0,0,0,0.12)\",\n fontSize = 28,\n angle = -35,\n gap = 80,\n fontWeight = \"600\",\n } = watermarkConfig;\n\n const [cr, cg, cb, ca] = parseCssColor(color);\n const font = await pdfDoc.embedFont(\n Number(fontWeight) >= 600\n ? StandardFonts.HelveticaBold\n : StandardFonts.Helvetica,\n );\n\n const estimatedCharWidth = 0.55;\n const tileW = text.length * fontSize * estimatedCharWidth + gap;\n const tileH = fontSize * 3;\n // Rotation in radians — angle follows the SVG/CW convention.\n const rad = (angle * Math.PI) / 180;\n const cosA = Math.cos(rad);\n const sinA = Math.sin(rad);\n\n // Actual font metric width used for centering each label.\n const textWidth = font.widthOfTextAtSize(text, fontSize);\n\n // pdf-lib rotation is CCW; cos(-angle)=cosA (even), sin(-angle)=-sinA (odd).\n const cosPdf = cosA;\n const sinPdf = -sinA;\n // Vertical half-span from baseline to visual text centre (em-box middle,\n // matching SVG dominantBaseline=\"middle\").\n const centerOffset = fontSize / 2;\n\n for (const page of pdfDoc.getPages()) {\n const { width, height } = page.getSize();\n const cx = width / 2;\n const cy = height / 2; // same value in both SVG (y-down) and PDF (y-up)\n const diag = Math.sqrt(width * width + height * height);\n const cols = Math.ceil(diag / tileW) + 2;\n const rows = Math.ceil(diag / tileH) + 2;\n\n // Mirror SVG's patternTransform=\"rotate(angle, cx, cy)\":\n // the unrotated pattern is anchored at the page top-left (0,0), so find\n // the tile indices that straddle the page centre and iterate from there.\n const nCenter = Math.floor(cx / tileW);\n const mCenter = Math.floor(cy / tileH);\n\n for (let n = nCenter - cols; n <= nCenter + cols; n++) {\n for (let m = mCenter - rows; m <= mCenter + rows; m++) {\n // Tile centre in SVG unrotated page coords (Y-down, origin top-left).\n const ux = n * tileW + tileW / 2;\n const uy = m * tileH + tileH / 2;\n\n // Apply SVG patternTransform: rotate(angle, cx, cy).\n const dx = ux - cx;\n const dy = uy - cy; // SVG Y-down\n const xRot = cx + dx * cosA - dy * sinA;\n const yRotSvg = cy + dx * sinA + dy * cosA;\n\n // Convert SVG y-down to PDF y-up.\n const pxC = xRot;\n const pyC = height - yRotSvg;\n\n // Skip tiles that land well outside the page.\n const margin = Math.max(textWidth, fontSize) * 2;\n if (\n pxC < -margin ||\n pxC > width + margin ||\n pyC < -margin ||\n pyC > height + margin\n ) {\n continue;\n }\n\n // pdf-lib rotates text around its draw origin (bottom-left corner).\n // Back-rotate the text-centre offset so the rotated label ends up\n // centred exactly at (pxC, pyC), matching SVG textAnchor=\"middle\" +\n // dominantBaseline=\"middle\".\n const drawX = pxC - (textWidth / 2) * cosPdf + centerOffset * sinPdf;\n const drawY = pyC - (textWidth / 2) * sinPdf - centerOffset * cosPdf;\n\n page.drawText(text, {\n x: drawX,\n y: drawY,\n size: fontSize,\n font,\n color: rgb(cr, cg, cb),\n opacity: ca,\n // pdf-lib CCW vs SVG CW — negate to match visuals.\n rotate: degrees(-angle),\n });\n }\n }\n }\n\n return pdfDoc.save();\n}\n\nconst MIN_SCALE = 0.25;\nconst MAX_SCALE = 4;\nconst ZOOM_STEP = 0.25;\nconst PAGE_GAP = 16; // px between pages\nconst PRESETS = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 2, 3, 4];\nconst THUMB_WIDTH = 120; // target thumbnail width in px\n\nfunction clamp(v: number, min: number, max: number) {\n return Math.min(max, Math.max(min, v));\n}\n\nexport interface WatermarkConfig {\n text: string;\n /** CSS color string — default: \"rgba(0,0,0,0.12)\" */\n color?: string;\n /** Font size in px at scale=1 — default: 28 */\n fontSize?: number;\n /** Rotation angle in degrees — default: -35 */\n angle?: number;\n /** Horizontal gap between repeated labels in px — default: 80 */\n gap?: number;\n /** Font family — default: \"sans-serif\" */\n fontFamily?: string;\n /** Font weight — default: \"600\" */\n fontWeight?: string;\n}\n\nexport interface PDFViewerProps {\n src?: string | ArrayBuffer | URL | Blob | File;\n /** Pass a string for a quick watermark, or a WatermarkConfig for full control. */\n watermark?: string | WatermarkConfig;\n print?: boolean;\n download?: boolean;\n /** Show the thumbnail sidebar (default: true) */\n sidebar?: boolean;\n}\n\ntype PageSize = { width: number; height: number };\n\nfunction WatermarkOverlay({\n config,\n width,\n height,\n}: {\n config: WatermarkConfig;\n width: number;\n height: number;\n}) {\n const {\n text,\n color = \"rgba(0,0,0,0.12)\",\n fontSize = 28,\n angle = -35,\n gap = 80,\n fontFamily = \"sans-serif\",\n fontWeight = \"600\",\n } = config;\n\n // Estimate text width so tiles don't overlap horizontally.\n const estimatedTextWidth = text.length * fontSize * 0.55;\n const tileW = estimatedTextWidth + gap;\n const tileH = fontSize * 3;\n // Unique id per text so multiple viewers on the same page don't clash.\n const patternId = `wm-${text.replace(/\\W/g, \"\").slice(0, 12)}-${Math.round(fontSize)}`;\n\n return (\n <svg\n aria-hidden=\"true\"\n style={{\n position: \"absolute\",\n inset: 0,\n width,\n height,\n pointerEvents: \"none\",\n userSelect: \"none\",\n }}\n width={width}\n height={height}\n >\n <defs>\n <pattern\n id={patternId}\n x=\"0\"\n y=\"0\"\n width={tileW}\n height={tileH}\n patternUnits=\"userSpaceOnUse\"\n patternTransform={`rotate(${angle}, ${width / 2}, ${height / 2})`}\n >\n <text\n x={tileW / 2}\n y={tileH / 2}\n textAnchor=\"middle\"\n dominantBaseline=\"middle\"\n fill={color}\n fontSize={fontSize}\n fontFamily={fontFamily}\n fontWeight={fontWeight}\n letterSpacing=\"0.05em\"\n >\n {text}\n </text>\n </pattern>\n </defs>\n <rect width={width} height={height} fill={`url(#${patternId})`} />\n </svg>\n );\n}\n\nexport function PDFViewer({\n src,\n watermark,\n print = true,\n download = true,\n sidebar = true,\n}: PDFViewerProps) {\n // ── Refs ────────────────────────────────────────────────────────────────────\n const containerRef = useRef<HTMLDivElement>(null);\n const scrollRef = useRef<HTMLDivElement>(null);\n const sidebarScrollRef = useRef<HTMLDivElement>(null);\n const pageInputRef = useRef<HTMLInputElement>(null);\n const pdfRef = useRef<PDFDocumentProxy | null>(null);\n const pageWrapperRefs = useRef(new Map<number, HTMLDivElement>());\n const canvasRefs = useRef(new Map<number, HTMLCanvasElement>());\n const renderTasksRef = useRef(new Map<number, RenderTask>());\n const thumbCanvasRefs = useRef(new Map<number, HTMLCanvasElement>());\n const thumbRenderTasksRef = useRef(new Map<number, RenderTask>());\n const thumbWrapperRefs = useRef(new Map<number, HTMLDivElement>());\n // Tracks which thumbnail pages have already been rendered so the lazy\n // IntersectionObserver doesn't re-render them on every sidebar open.\n const renderedThumbsRef = useRef(new Set<number>());\n // Scroll anchor captured just before a zoom so the viewport stays centred\n // on the same spot after the page wrappers resize.\n const zoomAnchorRef = useRef<{\n pageNum: number;\n /** 0–1 fraction of how far into the page the viewport centre was */\n pageOffsetRatio: number;\n } | null>(null);\n\n // ── State ───────────────────────────────────────────────────────────────────\n const [numPages, setNumPages] = useState(0);\n const [pageSizes, setPageSizes] = useState<PageSize[]>([]);\n const [currentPage, setCurrentPage] = useState(1);\n const [scale, setScale] = useState(1.0);\n // Debounced copy of `scale` used to trigger canvas re-renders only after the\n // user stops zooming, preventing a new render on every intermediate step.\n const [renderScale, setRenderScale] = useState(1.0);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [isFullscreen, setIsFullscreen] = useState(false);\n const [isEditingPage, setIsEditingPage] = useState(false);\n const [pageInput, setPageInput] = useState(\"\");\n const [isExporting, setIsExporting] = useState(false);\n const [sidebarOpen, setSidebarOpen] = useState(sidebar);\n // Set of page numbers currently near the viewport (populated by IntersectionObserver).\n const [visiblePages, setVisiblePages] = useState<Set<number>>(\n () => new Set(),\n );\n\n // ── Load document + collect page sizes ─────────────────────────────────────\n\n useEffect(() => {\n let cancelled = false;\n\n async function load() {\n if (src === undefined) {\n setLoading(false);\n setError(null);\n setNumPages(0);\n setPageSizes([]);\n setCurrentPage(1);\n setVisiblePages(new Set());\n renderedThumbsRef.current.clear();\n return;\n }\n\n setLoading(true);\n setError(null);\n setNumPages(0);\n setPageSizes([]);\n setCurrentPage(1);\n setVisiblePages(new Set());\n renderedThumbsRef.current.clear();\n\n try {\n const { GlobalWorkerOptions, getDocument } = await import(\"pdfjs-dist\");\n GlobalWorkerOptions.workerSrc = new URL(\n \"pdfjs-dist/build/pdf.worker.min.mjs\",\n import.meta.url,\n ).toString();\n\n let objectUrl: string | undefined;\n if (src instanceof Blob || src instanceof File) {\n objectUrl = URL.createObjectURL(src);\n src = objectUrl;\n }\n\n try {\n const loadTask = getDocument(src);\n const pdf = await loadTask.promise;\n if (cancelled) {\n void pdf.destroy();\n return;\n }\n\n void pdfRef.current?.destroy();\n pdfRef.current = pdf;\n\n // Collect natural sizes at scale=1 for all pages in parallel.\n // Batching in groups of 10 avoids spawning too many concurrent\n // pdfjs tasks while still being significantly faster than serial.\n const BATCH = 10;\n const sizes: PageSize[] = new Array(pdf.numPages);\n for (let start = 1; start <= pdf.numPages; start += BATCH) {\n if (cancelled) return;\n const end = Math.min(start + BATCH - 1, pdf.numPages);\n await Promise.all(\n Array.from({ length: end - start + 1 }, async (_, k) => {\n const i = start + k;\n const p = await pdf.getPage(i);\n const vp = p.getViewport({ scale: 1 });\n sizes[i - 1] = { width: vp.width, height: vp.height };\n }),\n );\n }\n if (cancelled) return;\n\n setNumPages(pdf.numPages);\n setPageSizes(sizes);\n } finally {\n if (objectUrl) URL.revokeObjectURL(objectUrl);\n }\n } catch (e) {\n if (!cancelled)\n setError(e instanceof Error ? e.message : \"Failed to load PDF.\");\n } finally {\n if (!cancelled) setLoading(false);\n }\n }\n\n void load();\n return () => {\n cancelled = true;\n };\n }, [src]);\n\n // ── Debounce scale → renderScale (150 ms) ─────────────────────────────────\n // Prevents triggering expensive canvas re-renders on every zoom step.\n\n useEffect(() => {\n const t = setTimeout(() => setRenderScale(scale), 150);\n return () => clearTimeout(t);\n }, [scale]);\n\n // ── IntersectionObserver — track which pages are near the viewport ─────────\n // Uses a generous rootMargin so pages are rendered before they scroll into\n // view and canvas memory is released only when a page is well off-screen.\n\n useEffect(() => {\n if (pageSizes.length === 0) return;\n const scrollContainer = scrollRef.current;\n if (!scrollContainer) return;\n\n const observer = new IntersectionObserver(\n (entries) => {\n setVisiblePages((prev) => {\n const next = new Set(prev);\n for (const entry of entries) {\n const pageNum = Number((entry.target as HTMLElement).dataset.page);\n if (!pageNum) continue;\n if (entry.isIntersecting) {\n next.add(pageNum);\n } else {\n next.delete(pageNum);\n // Free GPU memory for pages that left the extended viewport.\n const canvas = canvasRefs.current.get(pageNum);\n if (canvas && canvas.width > 1) {\n renderTasksRef.current.get(pageNum)?.cancel();\n renderTasksRef.current.delete(pageNum);\n canvas.width = 1;\n canvas.height = 1;\n }\n }\n }\n return next;\n });\n },\n // Preload/keep 600 px above and below the visible area.\n { root: scrollContainer, rootMargin: \"600px 0px\" },\n );\n\n pageWrapperRefs.current.forEach((el) => observer.observe(el));\n return () => observer.disconnect();\n }, [pageSizes]);\n\n // ── Render only visible pages whenever the visible set or zoom changes ─────\n\n useEffect(() => {\n const pdf = pdfRef.current;\n if (!pdf || pageSizes.length === 0 || visiblePages.size === 0) return;\n\n let cancelled = false;\n const sorted = [...visiblePages].sort((a, b) => a - b);\n\n async function renderVisible() {\n for (const pageNum of sorted) {\n if (cancelled) return;\n\n const canvas = canvasRefs.current.get(pageNum);\n if (!canvas) continue;\n\n renderTasksRef.current.get(pageNum)?.cancel();\n\n try {\n const pdfPage = await pdf!.getPage(pageNum);\n if (cancelled) return;\n\n const viewport = pdfPage.getViewport({ scale: renderScale });\n const dpr = window.devicePixelRatio || 1;\n\n canvas.width = Math.floor(viewport.width * dpr);\n canvas.height = Math.floor(viewport.height * dpr);\n canvas.style.width = `${viewport.width}px`;\n canvas.style.height = `${viewport.height}px`;\n\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) continue;\n\n const task = pdfPage.render({\n canvas,\n canvasContext: ctx,\n viewport,\n transform: dpr !== 1 ? [dpr, 0, 0, dpr, 0, 0] : undefined,\n });\n renderTasksRef.current.set(pageNum, task);\n await task.promise;\n } catch {\n // cancelled render — ignore\n }\n }\n }\n\n void renderVisible();\n\n return () => {\n cancelled = true;\n renderTasksRef.current.forEach((t) => t.cancel());\n renderTasksRef.current.clear();\n };\n }, [pageSizes, renderScale, visiblePages]);\n\n // ── Scroll listener — track current page ──────────────────────────────────\n\n useEffect(() => {\n const container = scrollRef.current;\n if (!container || pageSizes.length === 0) return;\n\n function update() {\n const mid = container!.scrollTop + container!.clientHeight / 2;\n let found = 1;\n for (let i = 1; i <= pageSizes.length; i++) {\n const el = pageWrapperRefs.current.get(i);\n if (!el) continue;\n if (el.offsetTop <= mid) found = i;\n else break;\n }\n setCurrentPage(found);\n }\n\n update(); // set on mount\n container.addEventListener(\"scroll\", update, { passive: true });\n return () => container.removeEventListener(\"scroll\", update);\n }, [pageSizes.length]);\n\n // ── Lazy-render thumbnails as they scroll into the sidebar viewport ─────────\n // An IntersectionObserver fires once per thumbnail entry; already-rendered\n // ones (tracked in renderedThumbsRef) are skipped to avoid redundant work.\n\n useEffect(() => {\n if (!sidebarOpen || pageSizes.length === 0) return;\n const pdf = pdfRef.current;\n if (!pdf) return;\n const sidebarContainer = sidebarScrollRef.current;\n if (!sidebarContainer) return;\n\n // Reset rendered set when the document changes so every thumbnail is\n // re-rendered fresh for the new document.\n renderedThumbsRef.current.clear();\n\n const observer = new IntersectionObserver(\n (entries) => {\n for (const entry of entries) {\n if (!entry.isIntersecting) continue;\n const pageNum = Number(\n (entry.target as HTMLElement).dataset.thumbPage,\n );\n if (!pageNum || renderedThumbsRef.current.has(pageNum)) continue;\n renderedThumbsRef.current.add(pageNum);\n\n void (async () => {\n const canvas = thumbCanvasRefs.current.get(pageNum);\n if (!canvas) return;\n thumbRenderTasksRef.current.get(pageNum)?.cancel();\n try {\n const pdfPage = await pdf.getPage(pageNum);\n const size = pageSizes[pageNum - 1];\n const thumbScale = THUMB_WIDTH / size.width;\n const viewport = pdfPage.getViewport({ scale: thumbScale });\n const dpr = window.devicePixelRatio || 1;\n canvas.width = Math.floor(viewport.width * dpr);\n canvas.height = Math.floor(viewport.height * dpr);\n canvas.style.width = `${viewport.width}px`;\n canvas.style.height = `${viewport.height}px`;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return;\n const task = pdfPage.render({\n canvas,\n canvasContext: ctx,\n viewport,\n transform: dpr !== 1 ? [dpr, 0, 0, dpr, 0, 0] : undefined,\n });\n thumbRenderTasksRef.current.set(pageNum, task);\n await task.promise;\n } catch {\n // Rendering failed — allow retry next time the thumbnail enters view.\n renderedThumbsRef.current.delete(pageNum);\n }\n })();\n }\n },\n { root: sidebarContainer, rootMargin: \"200px 0px\" },\n );\n\n thumbWrapperRefs.current.forEach((el) => observer.observe(el));\n\n return () => {\n observer.disconnect();\n thumbRenderTasksRef.current.forEach((t) => t.cancel());\n thumbRenderTasksRef.current.clear();\n };\n }, [pageSizes, sidebarOpen]);\n\n // ── Auto-scroll sidebar to keep active thumbnail visible ──────────────────\n\n useEffect(() => {\n if (!sidebarOpen) return;\n const container = sidebarScrollRef.current;\n const thumb = thumbWrapperRefs.current.get(currentPage);\n if (!container || !thumb) return;\n const thumbTop = thumb.offsetTop;\n const thumbBottom = thumbTop + thumb.offsetHeight;\n const scrollTop = container.scrollTop;\n const containerHeight = container.clientHeight;\n if (thumbTop < scrollTop || thumbBottom > scrollTop + containerHeight) {\n container.scrollTop =\n thumbTop - containerHeight / 2 + thumb.offsetHeight / 2;\n }\n }, [currentPage, sidebarOpen]);\n\n // ── Fullscreen ──────────────────────────────────────────────────────────────\n\n useEffect(() => {\n function onChange() {\n const fullscreen = !!document.fullscreenElement;\n setIsFullscreen(fullscreen);\n if (!fullscreen) setScale(1.0);\n }\n document.addEventListener(\"fullscreenchange\", onChange);\n return () => document.removeEventListener(\"fullscreenchange\", onChange);\n }, []);\n\n // ── Ref callbacks ───────────────────────────────────────────────────────────\n\n const setPageWrapper = useCallback(\n (pageNum: number) => (el: HTMLDivElement | null) => {\n if (el) pageWrapperRefs.current.set(pageNum, el);\n else pageWrapperRefs.current.delete(pageNum);\n },\n [],\n );\n\n const setCanvas = useCallback(\n (pageNum: number) => (el: HTMLCanvasElement | null) => {\n if (el) canvasRefs.current.set(pageNum, el);\n else canvasRefs.current.delete(pageNum);\n },\n [],\n );\n\n const setThumbCanvas = useCallback(\n (pageNum: number) => (el: HTMLCanvasElement | null) => {\n if (el) thumbCanvasRefs.current.set(pageNum, el);\n else thumbCanvasRefs.current.delete(pageNum);\n },\n [],\n );\n\n const setThumbWrapper = useCallback(\n (pageNum: number) => (el: HTMLDivElement | null) => {\n if (el) thumbWrapperRefs.current.set(pageNum, el);\n else thumbWrapperRefs.current.delete(pageNum);\n },\n [],\n );\n\n // ── Actions ─────────────────────────────────────────────────────────────────\n\n const scrollToPage = useCallback(\n (n: number) => {\n const p = clamp(n, 1, numPages);\n const target = pageWrapperRefs.current.get(p);\n const container = scrollRef.current;\n if (!target || !container) return;\n container.scrollTop = target.offsetTop - container.offsetTop;\n },\n [numPages],\n );\n\n const zoomTo = useCallback((s: number) => {\n const clamped = clamp(s, MIN_SCALE, MAX_SCALE);\n\n // Capture the viewport centre's position within its page so we can\n // restore it after the page wrappers resize to the new scale.\n const container = scrollRef.current;\n if (container) {\n const mid = container.scrollTop + container.clientHeight / 2;\n let anchored = false;\n for (const [pageNum, el] of pageWrapperRefs.current) {\n const top = el.offsetTop;\n const h = el.offsetHeight;\n if (mid >= top && mid < top + h) {\n zoomAnchorRef.current = {\n pageNum,\n pageOffsetRatio: h > 0 ? (mid - top) / h : 0,\n };\n anchored = true;\n break;\n }\n }\n if (!anchored) {\n zoomAnchorRef.current = { pageNum: 1, pageOffsetRatio: 0 };\n }\n }\n\n setScale(clamped);\n }, []);\n\n // After scale commits to the DOM (page wrappers have new heights), scroll\n // so the anchored point stays at the centre of the viewport.\n useLayoutEffect(() => {\n const anchor = zoomAnchorRef.current;\n if (!anchor) return;\n zoomAnchorRef.current = null;\n\n const container = scrollRef.current;\n const el = pageWrapperRefs.current.get(anchor.pageNum);\n if (!container || !el) return;\n\n const newMid = el.offsetTop + anchor.pageOffsetRatio * el.offsetHeight;\n container.scrollTop = newMid - container.clientHeight / 2;\n }, [scale]);\n\n function fitToWidth() {\n const container = scrollRef.current;\n if (!container || pageSizes.length === 0) return;\n const available = container.clientWidth - PAGE_GAP * 2;\n zoomTo(available / pageSizes[0].width);\n }\n\n function fitToHeight() {\n const container = scrollRef.current;\n if (!container || pageSizes.length === 0) return;\n const available = container.clientHeight - PAGE_GAP * 2;\n zoomTo(available / pageSizes[0].height);\n }\n\n function toggleFullscreen() {\n if (isFullscreen) document.exitFullscreen();\n else containerRef.current?.requestFullscreen();\n }\n\n function startEditingPage() {\n setPageInput(String(currentPage));\n setIsEditingPage(true);\n setTimeout(() => pageInputRef.current?.select(), 0);\n }\n\n function commitPageEdit() {\n const n = parseInt(pageInput, 10);\n if (!isNaN(n)) scrollToPage(n);\n setIsEditingPage(false);\n }\n\n function handlePageKey(e: KeyboardEvent<HTMLInputElement>) {\n if (e.key === \"Enter\") commitPageEdit();\n else if (e.key === \"Escape\") setIsEditingPage(false);\n }\n\n const canPrev = currentPage > 1;\n const canNext = currentPage < numPages;\n\n // Normalise the watermark prop into a config object (or undefined).\n const watermarkConfig: WatermarkConfig | undefined =\n watermark === undefined\n ? undefined\n : typeof watermark === \"string\"\n ? { text: watermark }\n : watermark;\n\n async function handleDownloadWithWatermark() {\n if (!src || isExporting) return;\n setIsExporting(true);\n try {\n const bytes = await generateWatermarkedPdf(src, watermarkConfig);\n const blob = new Blob([bytes.buffer.slice(0) as ArrayBuffer], {\n type: \"application/pdf\",\n });\n const url = URL.createObjectURL(blob);\n const a = document.createElement(\"a\");\n a.href = url;\n a.download =\n src instanceof File ? src.name || \"document.pdf\" : \"document.pdf\";\n a.click();\n setTimeout(() => URL.revokeObjectURL(url), 10_000);\n } finally {\n setIsExporting(false);\n }\n }\n\n async function handlePrintWithWatermark() {\n if (!src || isExporting) return;\n setIsExporting(true);\n try {\n const bytes = await generateWatermarkedPdf(src, watermarkConfig);\n const blob = new Blob([bytes.buffer.slice(0) as ArrayBuffer], {\n type: \"application/pdf\",\n });\n const url = URL.createObjectURL(blob);\n const iframe = document.createElement(\"iframe\");\n iframe.style.cssText =\n \"position:fixed;width:0;height:0;opacity:0;border:0\";\n iframe.src = url;\n document.body.appendChild(iframe);\n iframe.onload = () => {\n iframe.contentWindow?.focus();\n iframe.contentWindow?.print();\n setTimeout(() => {\n document.body.removeChild(iframe);\n URL.revokeObjectURL(url);\n }, 60_000);\n };\n } finally {\n setIsExporting(false);\n }\n }\n\n // ── Render ──────────────────────────────────────────────────────────────────\n\n return (\n <div\n ref={containerRef}\n className=\"flex h-full flex-col bg-white text-neutral-800 border border-gray-200\"\n >\n {/* ── Toolbar ──────────────────────────────────────────────────────── */}\n <div className=\"flex shrink-0 items-center gap-1 border-b border-neutral-200 bg-white px-3 py-2 shadow-sm\">\n {/* Sidebar toggle */}\n {sidebar && (\n <>\n <button\n type=\"button\"\n onClick={() => setSidebarOpen((o) => !o)}\n title={sidebarOpen ? \"Hide thumbnails\" : \"Show thumbnails\"}\n className={`flex h-8 w-8 items-center justify-center rounded-md transition hover:bg-neutral-100 ${sidebarOpen ? \"bg-neutral-100 text-blue-600\" : \"\"}`}\n >\n <Icons.PanelLeft size={16} />\n </button>\n <div className=\"mx-1 h-5 w-px bg-neutral-200\" />\n </>\n )}\n\n {/* Prev page */}\n <button\n type=\"button\"\n onClick={() => scrollToPage(currentPage - 1)}\n disabled={!canPrev}\n title=\"Previous page\"\n className=\"flex h-8 w-8 items-center justify-center rounded-md transition hover:bg-neutral-100 disabled:cursor-not-allowed disabled:opacity-30\"\n >\n <Icons.ChevronLeft size={18} />\n </button>\n\n {/* Page indicator / jump input */}\n {isEditingPage ? (\n <input\n ref={pageInputRef}\n type=\"number\"\n min={1}\n max={numPages}\n value={pageInput}\n onChange={(e) => setPageInput(e.target.value)}\n onBlur={commitPageEdit}\n onKeyDown={handlePageKey}\n className=\"w-14 rounded-md border border-neutral-300 bg-white px-2 py-0.5 text-center text-sm outline-none focus:border-blue-500\"\n />\n ) : (\n <button\n type=\"button\"\n onClick={numPages > 0 ? startEditingPage : undefined}\n disabled={numPages === 0}\n title=\"Click to jump to page\"\n className=\"flex items-center gap-1 rounded-md px-2 py-0.5 text-sm transition hover:bg-neutral-100 disabled:opacity-40\"\n >\n <span className=\"font-medium tabular-nums\">\n {numPages > 0 ? currentPage : \"—\"}\n </span>\n <span className=\"text-neutral-400\">/</span>\n <span className=\"tabular-nums text-neutral-500\">\n {numPages > 0 ? numPages : \"—\"}\n </span>\n </button>\n )}\n\n {/* Next page */}\n <button\n type=\"button\"\n onClick={() => scrollToPage(currentPage + 1)}\n disabled={!canNext}\n title=\"Next page\"\n className=\"flex h-8 w-8 items-center justify-center rounded-md transition hover:bg-neutral-100 disabled:cursor-not-allowed disabled:opacity-30\"\n >\n <Icons.ChevronRight size={18} />\n </button>\n\n <div className=\"mx-2 h-5 w-px bg-neutral-200\" />\n\n {/* Zoom out */}\n <button\n type=\"button\"\n onClick={() => zoomTo(scale - ZOOM_STEP)}\n disabled={scale <= MIN_SCALE}\n title=\"Zoom out\"\n className=\"flex h-8 w-8 items-center justify-center rounded-md transition hover:bg-neutral-100 disabled:cursor-not-allowed disabled:opacity-30\"\n >\n <Icons.ZoomOut size={16} />\n </button>\n\n {/* Zoom preset dropdown — always shows current value */}\n <select\n value={scale}\n onChange={(e) => zoomTo(parseFloat(e.target.value))}\n title=\"Zoom level\"\n className=\"w-[5.5rem] rounded-md border border-neutral-300 bg-white px-1.5 py-0.5 text-xs text-neutral-700 outline-none focus:border-blue-500\"\n >\n {!PRESETS.some((p) => Math.abs(p - scale) < 0.001) && (\n <option value={scale}>{Math.round(scale * 100)}%</option>\n )}\n {PRESETS.map((v) => (\n <option key={v} value={v}>\n {Math.round(v * 100)}%\n </option>\n ))}\n </select>\n\n {/* Zoom in */}\n <button\n type=\"button\"\n onClick={() => zoomTo(scale + ZOOM_STEP)}\n disabled={scale >= MAX_SCALE}\n title=\"Zoom in\"\n className=\"flex h-8 w-8 items-center justify-center rounded-md transition hover:bg-neutral-100 disabled:cursor-not-allowed disabled:opacity-30\"\n >\n <Icons.ZoomIn size={16} />\n </button>\n\n <div className=\"mx-1 h-5 w-px bg-neutral-200\" />\n\n {/* Fit to width */}\n <button\n type=\"button\"\n onClick={fitToWidth}\n disabled={pageSizes.length === 0}\n title=\"Fit to width\"\n className=\"flex h-8 w-8 items-center justify-center rounded-md transition hover:bg-neutral-100 disabled:cursor-not-allowed disabled:opacity-30\"\n >\n <Icons.ArrowLeftRight size={15} />\n </button>\n\n {/* Fit to height */}\n <button\n type=\"button\"\n onClick={fitToHeight}\n disabled={pageSizes.length === 0}\n title=\"Fit to height\"\n className=\"flex h-8 w-8 items-center justify-center rounded-md transition hover:bg-neutral-100 disabled:cursor-not-allowed disabled:opacity-30\"\n >\n <Icons.ArrowUpDown size={15} />\n </button>\n\n <div className=\"flex-1\" />\n\n {/* Download with watermark */}\n {download && (\n <button\n type=\"button\"\n onClick={handleDownloadWithWatermark}\n disabled={numPages === 0 || isExporting}\n title={watermarkConfig ? \"Download with watermark\" : \"Download\"}\n className=\"flex h-8 w-8 items-center justify-center rounded-md transition hover:bg-neutral-100 disabled:cursor-not-allowed disabled:opacity-30\"\n >\n {isExporting ? (\n <Icons.Loader2 size={15} className=\"animate-spin\" />\n ) : (\n <Icons.Download size={15} />\n )}\n </button>\n )}\n\n {/* Print with watermark */}\n {print && (\n <button\n type=\"button\"\n onClick={handlePrintWithWatermark}\n disabled={numPages === 0 || isExporting}\n title={watermarkConfig ? \"Print with watermark\" : \"Print\"}\n className=\"flex h-8 w-8 items-center justify-center rounded-md transition hover:bg-neutral-100 disabled:cursor-not-allowed disabled:opacity-30\"\n >\n <Icons.Printer size={15} />\n </button>\n )}\n\n <div className=\"mx-1 h-5 w-px bg-neutral-200\" />\n\n {/* Fullscreen */}\n <button\n type=\"button\"\n onClick={toggleFullscreen}\n title={isFullscreen ? \"Exit fullscreen\" : \"Enter fullscreen\"}\n className=\"flex h-8 w-8 items-center justify-center rounded-md transition hover:bg-neutral-100\"\n >\n {isFullscreen ? (\n <Icons.Minimize size={16} />\n ) : (\n <Icons.Maximize size={16} />\n )}\n </button>\n </div>\n\n {/* ── Body: sidebar + scroll area ──────────────────────────────────── */}\n <div className=\"flex flex-1 min-h-0\">\n {/* Sidebar thumbnails */}\n {sidebar && sidebarOpen && (\n <div\n ref={sidebarScrollRef}\n className=\"flex flex-col gap-1 w-[152px] shrink-0 overflow-y-auto border-r border-neutral-200 bg-neutral-50 py-2 px-2 overflow-x-hidden\"\n >\n {pageSizes.map((_, idx) => {\n const pageNum = idx + 1;\n const isActive = currentPage === pageNum;\n return (\n <div\n key={pageNum}\n ref={setThumbWrapper(pageNum)}\n data-thumb-page={pageNum}\n onClick={() => scrollToPage(pageNum)}\n title={`Page ${pageNum}`}\n className={`flex flex-col items-center gap-1.5 cursor-pointer rounded-lg p-1.5 transition-colors ${\n isActive ? \"bg-blue-50\" : \"hover:bg-neutral-100\"\n }`}\n >\n <div\n className={`relative rounded shadow-sm overflow-hidden ring-2 transition-colors ${\n isActive ? \"ring-blue-500\" : \"ring-transparent\"\n }`}\n >\n <canvas\n ref={setThumbCanvas(pageNum)}\n className=\"block\"\n onContextMenu={(e) => e.preventDefault()}\n />\n {watermarkConfig &&\n (() => {\n const size = pageSizes[idx];\n const thumbScale = THUMB_WIDTH / size.width;\n const tw = Math.floor(size.width * thumbScale);\n const th = Math.floor(size.height * thumbScale);\n return (\n <WatermarkOverlay\n config={{\n ...watermarkConfig,\n fontSize:\n (watermarkConfig.fontSize ?? 28) * thumbScale,\n gap: (watermarkConfig.gap ?? 80) * thumbScale,\n }}\n width={tw}\n height={th}\n />\n );\n })()}\n </div>\n <span\n className={`text-[11px] tabular-nums leading-none ${\n isActive\n ? \"text-blue-600 font-semibold\"\n : \"text-neutral-400\"\n }`}\n >\n {pageNum}\n </span>\n </div>\n );\n })}\n </div>\n )}\n\n {/* ── Scroll area ──────────────────────────────────────────────────── */}\n <div\n ref={scrollRef}\n className=\"flex-1 overflow-y-auto overflow-x-auto bg-white\"\n >\n {/* Loading */}\n {loading && (\n <div className=\"flex h-full items-center justify-center\">\n <div className=\"flex flex-col items-center gap-3 text-neutral-400\">\n <Icons.Loader2 size={32} className=\"animate-spin\" />\n <span className=\"text-sm\">Loading document…</span>\n </div>\n </div>\n )}\n\n {/* Error */}\n {!loading && error && (\n <div className=\"flex h-full items-center justify-center\">\n <div className=\"flex flex-col items-center gap-3 rounded-xl border border-red-200 bg-red-50 px-8 py-10 text-red-600\">\n <Icons.AlertTriangle size={32} />\n <p className=\"max-w-xs text-center text-sm\">{error}</p>\n </div>\n </div>\n )}\n\n {/* Empty */}\n {!loading && !error && numPages === 0 && (\n <div className=\"flex h-full items-center justify-center\">\n <div className=\"flex flex-col items-center gap-3 text-neutral-300\">\n <Icons.FileText size={48} />\n <span className=\"text-sm\">No document loaded</span>\n </div>\n </div>\n )}\n\n {/* Pages */}\n {!loading && !error && pageSizes.length > 0 && (\n <div\n className=\"flex flex-col items-center py-4\"\n style={{ gap: PAGE_GAP, minWidth: \"fit-content\" }}\n >\n {pageSizes.map((size, idx) => {\n const pageNum = idx + 1;\n return (\n <div\n key={pageNum}\n ref={setPageWrapper(pageNum)}\n data-page={pageNum}\n className=\"relative flex-shrink-0\"\n style={{\n width: Math.floor(size.width * scale),\n height: Math.floor(size.height * scale),\n }}\n >\n <canvas\n ref={setCanvas(pageNum)}\n className=\"block shadow-md ring-1 ring-black/10\"\n onContextMenu={(e) => e.preventDefault()}\n />\n {watermarkConfig && (\n <WatermarkOverlay\n config={{\n ...watermarkConfig,\n fontSize: (watermarkConfig.fontSize ?? 28) * scale,\n gap: (watermarkConfig.gap ?? 80) * scale,\n }}\n width={Math.floor(size.width * scale)}\n height={Math.floor(size.height * scale)}\n />\n )}\n </div>\n );\n })}\n </div>\n )}\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|