@monime/pdfio 0.1.9 → 0.1.10
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/Icons.d.ts +1 -0
- package/dist/Icons.d.ts.map +1 -1
- package/dist/PdfViewer.d.ts +5 -1
- 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 +358 -265
- 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};\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 ? StandardFonts.HelveticaBold : 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 (px < -margin || px > width + margin || py < -margin || py > height + margin) {\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];\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}\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({ src, watermark }: PDFViewerProps) {\n // ── Refs ────────────────────────────────────────────────────────────────────\n const containerRef = useRef<HTMLDivElement>(null);\n const scrollRef = 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\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\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 // ── 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 // ── 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], { type: \"application/pdf\" });\n const url = URL.createObjectURL(blob);\n const a = document.createElement(\"a\");\n a.href = url;\n a.download = 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], { type: \"application/pdf\" });\n const url = URL.createObjectURL(blob);\n const iframe = document.createElement(\"iframe\");\n iframe.style.cssText = \"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 {/* 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 <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 {/* Print with watermark */}\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 <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 {/* ── 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 );\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 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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"}
|