From 24d18d7c5c488322308ee7692f57ff7c9b2a8cb7 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 17 Jul 2026 20:12:15 +0000 Subject: [PATCH] perf: lazy load document PDF reader Co-authored-by: BigSimmo --- src/components/DocumentViewer.tsx | 30 ++++++++++++++++++- tests/document-viewer-pdf-reader-lazy.test.ts | 18 +++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 tests/document-viewer-pdf-reader-lazy.test.ts diff --git a/src/components/DocumentViewer.tsx b/src/components/DocumentViewer.tsx index 0fa1f770..75a627a9 100644 --- a/src/components/DocumentViewer.tsx +++ b/src/components/DocumentViewer.tsx @@ -1,6 +1,7 @@ "use client"; import Link from "next/link"; +import dynamic from "next/dynamic"; import { useRouter } from "next/navigation"; import { CircleAlert, @@ -64,7 +65,6 @@ import { } from "@/components/ui-primitives"; import { BadgeCluster } from "@/components/clinical-dashboard/clinical-badge"; import { SignedImage } from "@/components/clinical-dashboard/signed-image"; -import { NativePdfEmbed, PdfCanvasViewer } from "@/components/document-viewer/pdf-canvas-viewer"; import { NonPdfSourcePreview } from "@/components/document-viewer/non-pdf-source-preview"; import { clearCachedSignedUrl, getCachedSignedUrl, setCachedSignedUrl } from "@/lib/signed-url-cache"; import { readLocalProjectIdentity, unsafeLocalProjectMessage } from "@/lib/local-project-identity"; @@ -105,6 +105,34 @@ import { buildDocumentSummaryBadges } from "@/lib/document-summary-badges"; import { documentSummaryQuestion } from "@/lib/answer-contract"; import type { DocumentDetailPayload } from "@/lib/document-detail-contract"; +// pdf-canvas-viewer is only needed after a source document has loaded and the +// user is viewing a PDF. Keeping it out of the document route's initial client +// chunk avoids parsing its reader controls for image, text, and download-only +// documents. pdf.js itself remains loaded on demand by that component. +const PdfCanvasViewer = dynamic( + () => import("@/components/document-viewer/pdf-canvas-viewer").then((module) => module.PdfCanvasViewer), + { + ssr: false, + loading: () => , + }, +); +const NativePdfEmbed = dynamic( + () => import("@/components/document-viewer/pdf-canvas-viewer").then((module) => module.NativePdfEmbed), + { ssr: false, loading: () => }, +); + +function PdfPreviewLoading() { + return ( +
+ Loading PDF reader… +
+ ); +} + type PageRow = { id: string; page_number: number; diff --git a/tests/document-viewer-pdf-reader-lazy.test.ts b/tests/document-viewer-pdf-reader-lazy.test.ts new file mode 100644 index 00000000..1ec67d1e --- /dev/null +++ b/tests/document-viewer-pdf-reader-lazy.test.ts @@ -0,0 +1,18 @@ +import { readFileSync } from "node:fs"; +import { fileURLToPath } from "node:url"; +import { describe, expect, it } from "vitest"; + +const viewerSource = readFileSync( + fileURLToPath(new URL("../src/components/DocumentViewer.tsx", import.meta.url)), + "utf8", +); + +describe("DocumentViewer PDF reader loading", () => { + it("keeps the PDF reader out of the document route's initial client chunk", () => { + expect(viewerSource).not.toMatch( + /import\s*\{[^}]*\b(?:NativePdfEmbed|PdfCanvasViewer)\b[^}]*\}\s*from\s*["']@\/components\/document-viewer\/pdf-canvas-viewer["']/, + ); + expect(viewerSource).toContain('dynamic(\n () => import("@/components/document-viewer/pdf-canvas-viewer")'); + expect(viewerSource).toContain("loading: () => "); + }); +});