/** * `` — the complete interactive PDF viewer. * * The production drop-in. One required prop and you have the full * feature surface: * * ```tsx * * ``` * * `` is the canonical component — all viewer state, services * wiring, plugin slots, and rendering live here. {@link LensPDFDemo} * is a thin wrapper that adds the marketing chrome (URL bar, * drag-and-drop upload, file picker, empty state) on top of this * component and feeds it a `pdfUrl`. * * One mount, full feature surface. Backed by * `createBrowserViewerServices`, every viewer-only feature LensPDF * ships works on any PDF the browser can fetch: * * - PageCanvas + multi-page navigation + multi-DPI tile cache * - Color picker (RGB + CMYK + every detected spot ink + TAC) * - Densitometer (CMYK + every detected spot ink + TAC limit) * - Measure tool (mm / in / pt) * - TAC heatmap overlay (CMYK + spots) * - Per-ink CMYK + spot separations preview (inks default ON, * untick to hide that plate — same UX as Acrobat's Output * Preview) * - PDF layers (per-OCG isolated rendering, default all on) * - Annotation canvas + toolbar + thread (in-memory) * * Three mutually-exclusive primary canvases — Page (default), * Separation preview, Layer preview — match the lint-pdf reference * viewer's UX so the same muscle memory carries over. * * Server-only features (true ICC separations, preflight findings, * server-persisted annotations, PDF report exports) self-hide because * their dedicated services are intentionally `markUnwired`. Hosts * that have a backend pass `services` to override. * * Internal organisation: * * - Inline CSS-in-JS lives in `LensPDFDemo.styles.ts` (shared with * {@link LensPDFDemo}; this file focuses on the React tree). * - Smaller building blocks (`PageCanvas`, `SeparationCanvas`, * `LayerCanvas`, `AnnotationCanvas`, `AnnotationToolbar`, * `AnnotationThread`, `LayerPanel`, `BoxOverlay`, * `DielineOverlay`, `TACHeatmapOverlay`, `ColorPickerTool`, * `DensitometerTool`, `MeasureTool`) each ship as their own * component file and are composed here. * * @public */ import type { ReactNode } from "react"; import type { LensPDFDataConfig } from "../adapters"; import { type MinimalCodexClient } from "../browser"; import type { ThemeTokens, ViewerServices } from "../plugin/services"; import type { DecisionRecord, DecisionType, OverlayItem } from "../plugin/types"; import type { DielineResult } from "../types"; import { type StageAlign } from "./PdfSubstrate"; import { type LensPDFPresetKind } from "./presets"; import { type LensMenuAction, type LensPDFShellPlugin } from "./shellPlugins"; /** * Tool ids the viewer's sidebar can show. The `tools` prop accepts any * subset; default is every tool. Passing `[]` hides the entire tool * column, giving consumers a "viewer only" mount. * * @public */ export type LensPDFTool = "color-picker" | "densitometer" | "measure" | "annotate" | "tac-heatmap" | "separations" | "layers"; /** * Props for {@link LensPDF}. * * @public */ export interface LensPDFProps { /** * URL of the PDF to load. Any URL the browser can fetch — your own * CDN, a signed link, a `blob:` URL from a `File` your app uploaded. * Changing this swaps the document and resets to `initialPage`. */ pdfUrl: string; /** Theme tokens. Defaults to {@link darkThemeTokens}. */ tokens?: Partial; /** Brand label shown in the viewer top bar + footer. Default: "LensPDF". */ brand?: string; /** * Brand logo URL. Rendered to the left of the brand label in the * built-in top bar (see `showTopBar`). */ brandLogoUrl?: string; /** * Host-injected action buttons shown inside the tools menu * (hamburger drawer on mobile, persistent sidebar on desktop), * pinned above the plugin panels. Use this for "Download", * "Back to demo", deep-links etc. without authoring a shell * plugin. Each action renders as a token-styled anchor or * button. See {@link LensMenuAction}. */ menuActions?: ReadonlyArray; /** * When `false`, suppresses the built-in top bar. Hosts that already * render their own chrome around `` should pass `false`. * Default: `true`. */ showTopBar?: boolean; /** * Anchoring of the rendered page within the stage. ``"center"`` * (default) centers it; ``"left"`` pins the page to the stage's * top-left so it opens flush in that corner — handy for demos or * embedded previews that want a predictable origin. Mobile always * pins top-left regardless of this prop. * Default: ``"center"``. */ stageAlign?: StageAlign; /** * Override the loading screen shown while pdf.js fetches + parses * the document, or while a page is rasterising. When unset, * defaults to a branded page-shaped skeleton with a shimmer sweep. * * Pass a fully custom React node, or import `LensLoadingSkeleton` * and pass it with extra props (e.g. `logo`, `accentColor`, * `label`) to keep the default visual treatment with brand * customisations on top. */ loadingPlaceholder?: ReactNode; /** Optional className on the outermost div. */ className?: string; /** Tools to show in the sidebar. Default: every tool. */ tools?: ReadonlyArray; /** Initial zoom percentage. Default: 80. */ initialZoom?: number; /** TAC limit for the heatmap + densitometer. Default: 300. */ tacLimit?: number; /** * Override the pdf.js worker URL for the **analysis** pdf.js (the * separations / TAC / layers / densitometer / page-raster services * built by `createBrowserViewerServices`). Defaults to an unpkg URL * pinned to lens-pdf's bundled `pdfjs-dist`. * * This is a *different* pdf.js major than the page-render substrate * (react-pdf) — see {@link substrateWorkerSrc}. To run fully offline * / CDN-free you must self-host **both** workers and set both props; * the two URLs point at different `pdfjs-dist` versions and are not * interchangeable. */ workerSrc?: string; /** * Override the pdf.js worker URL for the **page-render substrate** * (react-pdf, which paints the base page). Defaults to an unpkg URL * pinned to the `pdfjs-dist` version react-pdf bundles * ({@link defaultPdfjsWorkerSrc}). * * Set this alongside {@link workerSrc} to self-host both workers and * remove the runtime unpkg dependency entirely (air-gapped hosts, * strict CSPs). The file MUST match react-pdf's bundled pdfjs major * — copy it from `pdfjs-dist/legacy/build/pdf.worker.min.mjs` * resolved through `react-pdf`, not through lens-pdf. */ substrateWorkerSrc?: string; /** * Pre-built services. When provided, Lens uses them where wired and * automatically falls back to in-browser RGB/pdf.js services for any * unwired capability. This keeps LintPDF/backends optional. */ services?: ViewerServices; /** * Cap (in pixels) on the in-browser analysis raster — the page render * the separations / TAC / densitometer tools derive every plate from. * Forwarded to `createBrowserViewerServices`'s `maxAnalysisPixels`. * * Leave unset for the sensible default: the full-quality desktop raster, * automatically tightened to ~1 MP on phones (`useIsMobile`) so the * separations view stays within iOS Safari's canvas-memory budget. Set * it to take full control of the memory/quality trade-off on every * device. Ignored when you pass your own `services`. */ maxAnalysisPixels?: number; /** Optional footer content below the viewer. */ footer?: ReactNode; /** When true, renders full-viewport with fixed positioning. */ fullscreen?: boolean; /** Initial page number (1-indexed). Default: 1. */ initialPage?: number; /** * When ``true``, the viewer mounts with the finding overlay layer * already enabled (boxes + F-number badges drawn on the page). * Default: ``false`` — the user reveals overlays via the "Finding * overlays" toggle or the Inspection tab. Useful for hosts that want * findings visible the moment the viewer paints (review tools, * embedded previews, demos). */ initialShowFindings?: boolean; /** * Lower / upper zoom bounds (transform scale, 1 = 100%). Defaults * to ``0.25`` / ``4`` — the same range the substrate's pan/pinch * limits enforce and zoom-to-fit clamps to. Bump ``maxScale`` for * hosts that need deeper zoom on wide-format art or high-DPI * imagery; the focus effect's fit clamp tracks automatically. */ minScale?: number; maxScale?: number; /** * Findings to flag on the page raster. Hosts convert their domain * records (engine findings, brand-spec violations, etc.) into * `OverlayItem`s. PageCanvas draws the bbox tinted by `tier`, * PageNavigator badges errors / warnings per page. */ items?: readonly OverlayItem[]; /** * Force the Inspection / Findings side-panel slot visible even when * ``items`` is empty. Renders an empty "no findings yet" state so * the slot stays in the layout. Defaults to ``false`` — hosts * without preflight data don't see a placeholder panel. * * Useful for: * - Demos that want the panel discoverable from the first frame. * - Hosts with an in-flight preflight call (stable layout while it * loads). * - Adapters where the user explicitly toggles preflight on/off in * the UI and you want to keep the slot mounted across that toggle. */ forceInspectionPanel?: boolean; /** * Spot-color palette keyed by spot name (case insensitive). Wins * over the Pantone Gold library + the PDF's ``altRgb`` fallback in * the separations panel swatch render. Hosts that have richer * sources of truth (codex's ``summary.spot_colors.colors[].swatch_hex``, * a callas / PitStop / Acrobat preflight report, an internal * swatch DB) pass values here. * * Example: ``{ "PANTONE 225 C": "#c6168d", "PANTONE 236 C": "#da1884" }``. */ spotPalette?: Record; /** Currently-selected finding (controlled). Drives the canvas * highlight + tooltip. */ selectedItem?: OverlayItem | null; /** Fires when the user clicks a finding's bbox or the page background * (in which case the argument is `null`). */ onItemSelect?: (item: OverlayItem | null) => void; /** * Dieline payload for the current page. When non-null, mounts * `` so each detected artwork region gets a * size-popover info chip at its centroid (mm + inches). */ dieline?: DielineResult | null; /** * When true, mounts `` so trim / bleed / crop boxes * defined in the PDF render with size popovers. Hosts that don't * carry box geometry can leave this off (default). */ showBoxOverlays?: boolean; /** * When true, the canvas is clipped to the page's TrimBox (falls * back to BleedBox, then CropBox). Hides the white bleed strip * outside the trim line. Default false. */ cropToTrim?: boolean; /** Fires after the active page changes (1-indexed). */ onPageChange?: (page: number) => void; /** Fires after the zoom level changes (percentage). */ onZoomChange?: (zoom: number) => void; /** Fires when the viewer raises a recoverable error. */ onError?: (message: string) => void; /** First-party plugin preset used as the base composition. */ preset?: LensPDFPresetKind; /** * Additional shell plugins (or replacements) for panels and toolbar * slots. Use `replaces` on your plugin to override a built-in one. */ plugins?: ReadonlyArray; /** * Zero-glue data config. Pass raw engine outputs here — lens maps * them internally. Findings from all sources are shown together in * the Inspection panel; dieline and spot palette from codex fill in * as defaults when the explicit `dieline` / `spotPalette` props are * absent. * * Supported engines: codex (`codexSummary` + `codexFindings`), * lint-pdf (`lintFindings`), callas (`callasFindings`), PitStop * (`pitstopFindings`). */ dataConfig?: LensPDFDataConfig; /** * Active decisions keyed by finding id. Populate from * ``GET /api/v1/jobs/{id}/decisions`` and re-pass after each * record / revoke. The sidebar shows approval badges and the * canvas dims approved / waived findings to 25% opacity. * * @public */ decisions?: Record; /** * Fires when the user clicks Approve / Waive / Reject / Suppress on a * finding in the sidebar. The host should call the lint-pdf decisions * API and refresh the ``decisions`` prop. * * @public */ onDecide?: (item: OverlayItem, type: DecisionType, notes?: string) => void; /** * Optional codex client. When provided, `` fires * `extractStream` in the background after each PDF loads. As codex * events arrive the viewer silently upgrades: * * - `colorWorld` → separations panel shows pikepdf-accurate ink list * (including spots inside compressed streams that the browser's * regex parser misses). * - `phase2_complete` → separations, TAC heatmap, and layers switch * to Ghostscript-rendered plates (higher fidelity than the * pdfjs RGB-approximated versions). * * Tools remain fully active on pdfjs throughout — there is no loading * state or disabled period. Codex is a silent quality upgrade. * * Accepts any object satisfying {@link MinimalCodexClient} — * including `HttpClient` from `@printwithsynergy/codex-client`. */ codex?: MinimalCodexClient; /** * Signals whether viewer data is provisional (server analysis * still in-flight) or authoritative. When `"provisional"`, a * dismissible notice is shown to the user. The notice auto-hides * when `dataStatus` flips to `"ready"`. * * If omitted, no notice is shown. */ dataStatus?: "provisional" | "ready"; /** * Content of the provisional-data notice. Defaults to * "Quick preview — full analysis loading…". Accepts any React * node so hosts can localise or brand the string. */ provisionalNotice?: ReactNode; /** * PDF load timeout forwarded to the substrate. Default: 30 000 ms. * Large-file or slow-network embedders can raise this to avoid the * "PDF didn't load within N seconds" error on the permalink path. */ loadTimeoutMs?: number; /** * Fires once pdf.js has parsed the document and basic metadata is * available from in-browser analysis. Use this to populate a * provisional facts panel before authoritative server data arrives. * * Hot-swap: replace with authoritative codex/lint data when it lands. */ onPdfMetadata?: (meta: { /** Total page count from pdf.js. */ numPages: number; /** Dimensions of the first page in PDF points. */ firstPageSize: { widthPts: number; heightPts: number; }; /** Inks detected by pdfjs colour-world analysis. */ inks: ReadonlyArray<{ name: string; type: string; }>; }) => void; } /** * The complete interactive LensPDF viewer — sidebar controls, theming, * optional fullscreen mode. All viewer-only features (color picker, * densitometer, measure, separations, TAC heatmap, layers, annotations) * are wired out of the box. Pass a `pdfUrl` and you have a viewer. * * @public */ export declare function LensPDF({ pdfUrl, tokens: tokenOverrides, brand, brandLogoUrl, menuActions, showTopBar, stageAlign, loadingPlaceholder, className, tools, initialZoom, tacLimit, workerSrc, substrateWorkerSrc, maxAnalysisPixels, services: serviceOverrides, footer, fullscreen: initialFullscreen, initialPage, initialShowFindings, minScale, maxScale, items, forceInspectionPanel, spotPalette, selectedItem, onItemSelect, dieline, showBoxOverlays, cropToTrim, onPageChange: onPageChangeProp, onZoomChange: onZoomChangeProp, onError: onErrorProp, preset, plugins: customPlugins, codex, dataConfig, decisions, onDecide, dataStatus, provisionalNotice, loadTimeoutMs, onPdfMetadata, }: LensPDFProps): import("react").JSX.Element; //# sourceMappingURL=LensPDF.d.ts.map