import * as react from 'react'; import react__default, { Component, ReactNode, ErrorInfo } from 'react'; import { PDFDocumentProxy, PDFPageProxy } from 'pdfjs-dist'; import * as pdfjsDist from 'pdfjs-dist'; export { pdfjsDist as pdfjsLib }; import * as zustand from 'zustand'; import * as react_jsx_runtime from 'react/jsx-runtime'; import { z } from 'zod'; import * as pdfjs_dist_types_src_display_api from 'pdfjs-dist/types/src/display/api'; import { ClassValue } from 'clsx'; import * as pdfjs_dist_types_src_display_metadata from 'pdfjs-dist/types/src/display/metadata'; import { StoreApi } from 'zustand/vanilla'; /** * Context provided by an external AI agent during interaction */ interface AgentContext { /** The last statement or response from the agent */ lastStatement?: string; /** Unique identifier for the conversation session */ conversationId?: string; /** Timestamp of the context */ timestamp: Date; /** Additional metadata from the agent */ metadata?: Record; } /** * Context passed when user triggers "Ask About This" feature */ interface AskAboutContext { /** Type of content being asked about */ type: 'text' | 'region'; /** Page number where the selection is made */ pageNumber: number; /** Selected text content (for text type) */ selectedText?: string; /** Selected region coordinates (for region type) */ region?: PDFRegion; /** Current agent context when asking */ agentContext?: AgentContext; } /** * Represents a rectangular region on a PDF page */ interface PDFRegion { /** X coordinate (from left) */ x: number; /** Y coordinate (from top) */ y: number; /** Width of the region */ width: number; /** Height of the region */ height: number; } /** * A focused region with visual styling, typically used by AI agent * to highlight content being discussed */ interface FocusedRegion extends PDFRegion { /** Unique identifier for this focus region */ id: string; /** Page number where the region is located */ pageNumber: number; /** Visual style for the focus indicator */ style?: 'pulse' | 'glow' | 'border'; /** Color of the focus indicator */ color?: string; /** Auto-clear timeout in milliseconds (0 = no auto-clear) */ autoClearTimeout?: number; } /** * A bookmark saved by the student */ interface Bookmark { /** Unique identifier */ id: string; /** Page number of the bookmark */ pageNumber: number; /** When the bookmark was created */ timestamp: Date; /** Agent's last statement when bookmarking (captured from context) */ agentContext?: string; /** User's personal note about the bookmark */ userNote?: string; /** Optional label for the bookmark */ label?: string; } /** * A quick note added by the student */ interface QuickNote { /** Unique identifier */ id: string; /** Page number where the note is placed */ pageNumber: number; /** X coordinate on the page */ x: number; /** Y coordinate on the page */ y: number; /** Note content */ content: string; /** When the note was created */ timestamp: Date; /** Agent's last statement when creating note */ agentLastStatement?: string; } /** * A key takeaway or summary, typically from the AI agent */ interface Takeaway { /** Unique identifier */ id: string; /** Page number associated with this takeaway */ pageNumber: number; /** Summary content */ summary: string; /** When the takeaway was added */ timestamp: Date; /** Source of the takeaway */ source: 'agent' | 'user'; /** Additional metadata */ metadata?: Record; } /** * Parameters for creating an agent-sourced highlight */ interface AgentHighlightParams { /** Page number for the highlight */ pageNumber: number; /** Rectangles defining the highlight area */ rects: HighlightRect[]; /** Text content being highlighted */ text: string; /** Highlight color */ color?: HighlightColor; /** Optional comment */ comment?: string; } /** * API exposed to external AI agents for interacting with the PDF viewer */ interface AgentAPI { /** * Focus a region on the page (visual indicator of what agent is discussing) * @returns The ID of the focused region */ focusRegion: (region: Omit) => string; /** * Clear a focused region by ID, or all if no ID provided */ clearFocusedRegion: (id?: string) => void; /** * Add a key takeaway for a page */ addTakeaway: (pageNumber: number, summary: string, metadata?: Record) => Takeaway; /** * Set the current agent context */ setAgentContext: (context: Partial) => void; /** * Add a highlight from the agent (marked with source: 'agent') */ addAgentHighlight: (params: AgentHighlightParams) => string; /** * Navigate to a specific page */ goToPage: (pageNumber: number) => void; /** * Get current page number */ getCurrentPage: () => number; /** * Get current agent context */ getAgentContext: () => AgentContext | null; } interface StudentState { /** User's bookmarks */ bookmarks: Bookmark[]; /** User's quick notes */ quickNotes: QuickNote[]; /** Key takeaways (from agent or user) */ takeaways: Takeaway[]; /** Pages the user has visited */ visitedPages: Set; /** Reading progress (0-1) */ progress: number; } interface StudentActions { addBookmark: (bookmark: Omit) => Bookmark; updateBookmark: (id: string, updates: Partial>) => void; removeBookmark: (id: string) => void; addQuickNote: (note: Omit) => QuickNote; updateQuickNote: (id: string, updates: Partial>) => void; removeQuickNote: (id: string) => void; addTakeaway: (takeaway: Omit) => Takeaway; removeTakeaway: (id: string) => void; markPageVisited: (pageNumber: number) => void; setProgress: (progress: number) => void; persistToStorage: (documentId: string) => void; loadFromStorage: (documentId: string) => void; reset: () => void; } interface AgentState { /** Current agent context */ currentContext: AgentContext | null; /** Currently focused regions */ focusedRegions: FocusedRegion[]; } interface AgentActions { /** Set the current agent context */ setAgentContext: (context: Partial) => void; /** Clear the agent context */ clearAgentContext: () => void; /** Add a focused region */ addFocusedRegion: (region: Omit) => string; /** Remove a focused region by ID */ removeFocusedRegion: (id: string) => void; /** Clear all focused regions */ clearAllFocusedRegions: () => void; /** Reset agent state */ reset: () => void; } interface StudentModeCallbacks { /** Callback when user triggers "Ask About This" */ onAskAbout?: (context: AskAboutContext) => void; /** Callback when a bookmark is added */ onBookmarkAdd?: (bookmark: Bookmark) => void; /** Callback when a quick note is added */ onQuickNoteAdd?: (note: QuickNote) => void; } interface StudentModeProps { /** Enable student learning mode features */ studentMode?: boolean; /** Callbacks for agent integration */ agentCallbacks?: StudentModeCallbacks; /** Initial agent context */ initialAgentContext?: AgentContext; /** Show quick note buttons on pages */ showQuickNoteButtons?: boolean; /** Show the minimap */ showMinimap?: boolean; /** Position of the minimap */ minimapPosition?: 'sidebar' | 'floating'; /** Enable "Ask About This" feature */ enableAskAbout?: boolean; /** Long press duration for ask about trigger (ms) */ askAboutLongPressDuration?: number; } type BlockType = 'heading' | 'paragraph' | 'list_item' | 'figure' | 'figure_region' | 'caption' | 'table' | 'mcq_option'; type DefaultAction = 'zoom_pan' | 'spotlight' | 'underline' | 'pulse'; /** Four numbers: [x1, y1, x2, y2] in PDF coordinates (origin top-left). */ type BBoxCoords = readonly [number, number, number, number]; interface Block { block_id: string; bbox: BBoxCoords; text: string | null; type: BlockType; parent_id: string | null; confidence: number; reading_order: number; default_action: DefaultAction; semantic_unit_id: string; } interface PageDimensionsDpi { width: number; height: number; dpi: number; } interface PageBBoxData { id: string; page_number: number; page_text: string; page_dimensions: PageDimensionsDpi; blocks: Block[]; created_at: string; } /** Lookup indexes used by the director + engine. */ interface BBoxIndex { byPage: Map; blockById: Map; crossPageFigures: Array<{ block_id: string; page: number; type: 'figure' | 'figure_region' | 'caption'; text: string; }>; } type EasingName = 'linear' | 'ease-in' | 'ease-out' | 'ease-in-out'; type SpotlightShape = 'rect' | 'rounded' | 'ellipse'; type UnderlineStyle = 'straight' | 'sketch' | 'double' | 'wavy'; type ArrowCurve = 'straight' | 'curved' | 'zigzag'; type PulseIntensity = 'subtle' | 'normal' | 'strong'; type BoxStyle = 'solid' | 'dashed'; type LabelPosition = 'top' | 'bottom' | 'left' | 'right'; type GhostPosition = 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left'; type ClearTarget = 'all' | 'spotlights' | 'overlays' | string[]; interface ActionCamera { type: 'camera'; target_block?: string; target_bbox?: BBoxCoords; scale: number; padding: number; easing: EasingName; } interface ActionSpotlight { type: 'spotlight'; target_block: string; dim_opacity: number; feather_px: number; shape: SpotlightShape; } interface ActionUnderline { type: 'underline'; target_block: string; color: string; style: UnderlineStyle; draw_duration_ms: number; } interface ActionHighlight { type: 'highlight'; target_block: string; color: string; draw_duration_ms: number; } interface ActionPulse { type: 'pulse'; target_block: string; count: number; intensity: PulseIntensity; } interface ActionCallout { type: 'callout'; from_block: string; to_block: string; label?: string; curve: ArrowCurve; } interface ActionGhostReference { type: 'ghost_reference'; target_page: number; target_block: string; position: GhostPosition; } interface ActionBox { type: 'box'; target_block: string; color: string; style: BoxStyle; } interface ActionLabel { type: 'label'; target_block: string; text: string; position: LabelPosition; } interface ActionClear { type: 'clear'; targets: ClearTarget; } type StoryboardAction = ActionCamera | ActionSpotlight | ActionUnderline | ActionHighlight | ActionPulse | ActionCallout | ActionGhostReference | ActionBox | ActionLabel | ActionClear; interface StoryboardStep { at_ms: number; duration_ms: number; action: StoryboardAction; } interface Storyboard { version: 1; reasoning: string; steps: StoryboardStep[]; } /** Active overlay state tracked by the engine / store. */ interface ActiveOverlay { id: string; kind: StoryboardAction['type']; action: StoryboardAction; createdAt: number; expiresAt: number; } interface CameraState { scale: number; x: number; y: number; easing: EasingName; } interface PDFViewerProps { /** URL or ArrayBuffer of the PDF file */ src: string | ArrayBuffer | Uint8Array; /** Initial page number (1-indexed) - used in uncontrolled mode */ initialPage?: number; /** Controlled page - viewer syncs to this value when provided */ page?: number; /** Initial scale/zoom level. Use 'auto' or 'page-width' to fit to container width. */ initialScale?: number | 'page-fit' | 'page-width' | 'auto'; /** Theme mode */ theme?: 'light' | 'dark' | 'sepia'; /** Custom class name */ className?: string; /** Whether to show the toolbar (default: true) */ showToolbar?: boolean; /** Whether to show the sidebar (default: true) */ showSidebar?: boolean; /** Whether to show the annotation toolbar (default: false) */ showAnnotationToolbar?: boolean; /** Whether to show floating zoom controls (default: true) */ showFloatingZoom?: boolean; /** Default sidebar panel */ defaultSidebarPanel?: SidebarPanel; /** View mode for the document */ viewMode?: ViewMode; /** Callback when document is loaded */ onDocumentLoad?: (document: PDFDocumentLoadedEvent) => void; /** Callback when page changes */ onPageChange?: (page: number) => void; /** Callback when scale/zoom changes */ onScaleChange?: (scale: number) => void; /** Callback when text is selected */ onTextSelect?: (selection: TextSelection) => void; /** Callback on error */ onError?: (error: Error) => void; /** Worker source URL for pdf.js */ workerSrc?: string; /** Callback when a page starts rendering */ onPageRenderStart?: (pageNumber: number) => void; /** Callback when a page finishes rendering */ onPageRenderComplete?: (pageNumber: number) => void; /** Callback when a highlight is added */ onHighlightAdded?: (highlight: Highlight) => void; /** Callback when a highlight is removed */ onHighlightRemoved?: (highlightId: string) => void; /** Callback when an annotation is added */ onAnnotationAdded?: (annotation: Annotation) => void; /** Callback when zoom/scale changes (alias for onScaleChange) */ onZoomChange?: (scale: number) => void; /** Custom loading component */ loadingComponent?: React.ReactNode; /** Custom error component - can be a node or a function receiving error and retry callback */ errorComponent?: React.ReactNode | ((error: Error, retry: () => void) => React.ReactNode); } interface PDFDocumentLoadedEvent { numPages: number; document: PDFDocumentProxy; } interface TextSelection { text: string; pageNumber: number; rects: DOMRect[]; } interface PDFPageState { pageNumber: number; page: PDFPageProxy | null; width: number; height: number; scale: number; rotation: number; isRendering: boolean; isRendered: boolean; error: Error | null; } interface PageDimensions { width: number; height: number; scale: number; } type ViewMode = 'single' | 'dual' | 'continuous' | 'book' | 'tutor'; type ScrollMode = 'single' | 'continuous'; type SidebarPanel = 'thumbnails' | 'outline' | 'search' | 'annotations' | null; type Theme = 'light' | 'dark' | 'sepia'; /** Loading phase for the PDF document */ type LoadingPhase = 'initializing' | 'fetching' | 'parsing' | 'rendering'; /** Document loading state for progressive loading */ type DocumentLoadingState = 'idle' | 'initializing' | 'loading' | 'ready' | 'error'; /** Loading progress information */ interface LoadingProgress { /** Current loading phase */ phase: LoadingPhase; /** Progress percentage (0-100), undefined for indeterminate */ percent?: number; /** Bytes loaded */ bytesLoaded?: number; /** Total bytes */ totalBytes?: number; } /** Streaming progress for progressive document loading */ interface StreamingProgress { /** Bytes loaded so far */ loaded: number; /** Total bytes (0 if unknown) */ total: number; } /** Request to scroll to a specific page */ interface ScrollToPageRequest { page: number; requestId: string; behavior: 'smooth' | 'instant'; } interface ViewerState { document: PDFDocumentProxy | null; numPages: number; isLoading: boolean; loadingProgress: LoadingProgress | null; error: Error | null; documentLoadingState: DocumentLoadingState; firstPageReady: boolean; streamingProgress: StreamingProgress | null; currentPage: number; scale: number; rotation: number; scrollToPageRequest: ScrollToPageRequest | null; viewMode: ViewMode; scrollMode: ScrollMode; theme: Theme; sidebarOpen: boolean; sidebarPanel: SidebarPanel; isFullscreen: boolean; isPresentationMode: boolean; } interface ViewerActions { setDocument: (doc: PDFDocumentProxy | null) => void; setLoading: (loading: boolean, progress?: LoadingProgress) => void; setLoadingProgress: (progress: LoadingProgress | null) => void; setError: (error: Error | null) => void; setDocumentLoadingState: (state: DocumentLoadingState) => void; setFirstPageReady: (ready: boolean) => void; setStreamingProgress: (progress: StreamingProgress | null) => void; setCurrentPage: (page: number) => void; goToPage: (page: number) => void; nextPage: () => void; previousPage: () => void; requestScrollToPage: (page: number, behavior?: 'smooth' | 'instant') => Promise; completeScrollRequest: (requestId: string) => void; setScale: (scale: number) => void; zoomIn: () => void; zoomOut: () => void; fitToWidth: () => void; fitToPage: () => void; setRotation: (rotation: number) => void; rotateClockwise: () => void; rotateCounterClockwise: () => void; setViewMode: (mode: ViewMode) => void; setScrollMode: (mode: ScrollMode) => void; setTheme: (theme: Theme) => void; toggleSidebar: () => void; setSidebarPanel: (panel: SidebarPanel) => void; setFullscreen: (fullscreen: boolean) => void; reset: () => void; } interface Highlight { id: string; pageNumber: number; rects: HighlightRect[]; color: HighlightColor; text: string; comment?: string; createdAt: Date; updatedAt: Date; /** Source of the highlight: user-created, agent-created, or search result */ source?: 'user' | 'agent' | 'search'; } interface HighlightRect { x: number; y: number; width: number; height: number; } type HighlightColor = 'yellow' | 'green' | 'blue' | 'pink' | 'orange'; type AnnotationType = 'note' | 'drawing' | 'shape' | 'stamp'; interface BaseAnnotation { id: string; type: AnnotationType; pageNumber: number; createdAt: Date; updatedAt: Date; } interface NoteAnnotation extends BaseAnnotation { type: 'note'; x: number; y: number; content: string; color: string; } interface DrawingAnnotation extends BaseAnnotation { type: 'drawing'; paths: DrawingPath[]; color: string; strokeWidth: number; } interface DrawingPath { points: { x: number; y: number; }[]; } interface ShapeAnnotation extends BaseAnnotation { type: 'shape'; shapeType: 'rect' | 'circle' | 'arrow' | 'line'; x: number; y: number; width: number; height: number; color: string; strokeWidth: number; } type Annotation = NoteAnnotation | DrawingAnnotation | ShapeAnnotation; interface SearchResult { pageNumber: number; matchIndex: number; text: string; rects: HighlightRect[]; } interface SearchState { query: string; results: SearchResult[]; currentResultIndex: number; isSearching: boolean; caseSensitive: boolean; wholeWord: boolean; } interface OutlineItem { title: string; pageNumber: number; children: OutlineItem[]; expanded?: boolean; } interface Plugin { name: string; version: string; initialize?: (api: PluginAPI) => void | Promise; destroy?: () => void | Promise; } interface PluginAPI { viewer: ViewerState & ViewerActions; registerToolbarItem: (item: ToolbarItem) => void; registerSidebarPanel: (panel: SidebarPanelConfig) => void; registerContextMenuItem: (item: ContextMenuItem) => void; } interface ToolbarItem { id: string; position: 'left' | 'center' | 'right'; render: () => React.ReactNode; order?: number; } interface SidebarPanelConfig { id: string; title: string; icon: React.ReactNode; render: () => React.ReactNode; } interface ContextMenuItem { id: string; label: string; icon?: React.ReactNode; onClick: () => void; condition?: () => boolean; } interface PDFViewerEventMap { pagechange: { pageNumber: number; }; scalechange: { scale: number; }; documentload: PDFDocumentLoadedEvent; textselect: TextSelection; error: Error; } interface HighlightTextOptions { /** Highlight color (default: 'yellow') */ color?: HighlightColor; /** Only highlight on specific page (default: all pages) */ page?: number; /** Case sensitive search (default: false) */ caseSensitive?: boolean; /** Scroll to first match (default: true) */ scrollTo?: boolean; } interface DrawRectOptions { /** Page number (1-indexed) */ page: number; /** X coordinate (percentage of page width, 0-100) */ x: number; /** Y coordinate (percentage of page height, 0-100) */ y: number; /** Width (percentage of page width) */ width: number; /** Height (percentage of page height) */ height: number; /** Border color (default: 'blue') */ color?: string; /** Border width in pixels (default: 2) */ strokeWidth?: number; /** Fill color (optional, transparent if not set) */ fillColor?: string; } interface DrawCircleOptions { /** Page number (1-indexed) */ page: number; /** Center X coordinate (percentage of page width, 0-100) */ x: number; /** Center Y coordinate (percentage of page height, 0-100) */ y: number; /** Radius (percentage of page width) */ radius: number; /** Border color (default: 'blue') */ color?: string; /** Border width in pixels (default: 2) */ strokeWidth?: number; /** Fill color (optional, transparent if not set) */ fillColor?: string; } interface AddNoteOptions { /** Page number (1-indexed) */ page: number; /** X coordinate (percentage of page width, 0-100) */ x: number; /** Y coordinate (percentage of page height, 0-100) */ y: number; /** Note content */ content: string; /** Note color (default: 'yellow') */ color?: string; } interface SearchOptions { /** Case sensitive search (default: false) */ caseSensitive?: boolean; /** Match whole words only (default: false) */ wholeWord?: boolean; /** Highlight all matches (default: true) */ highlightAll?: boolean; } interface GoToPageOptions { /** Scroll behavior (default: 'smooth') */ behavior?: 'smooth' | 'instant'; } interface SearchAndHighlightOptions { /** Highlight color (default: 'yellow') */ color?: HighlightColor; /** Page range to search - either { start, end } or array of page numbers */ pageRange?: { start: number; end: number; } | number[]; /** Case sensitive search (default: false) */ caseSensitive?: boolean; /** Match whole words only (default: false) */ wholeWord?: boolean; /** Scroll to first match (default: true) */ scrollToFirst?: boolean; /** Clear previous search highlights (default: true) */ clearPrevious?: boolean; } interface SearchAndHighlightResult { /** Total number of matches found */ matchCount: number; /** IDs of created highlights */ highlightIds: string[]; /** Detailed match information */ matches: Array<{ pageNumber: number; text: string; highlightId: string; rects: HighlightRect[]; }>; } interface AgentToolResult { success: boolean; data?: T; error?: { code: string; message: string; }; } interface AgentTools { /** Navigate to a specific page */ navigateToPage: (page: number) => Promise>; /** Highlight text with structured response */ highlightText: (text: string, options?: HighlightTextOptions) => Promise>; /** Get text content of a specific page */ getPageContent: (page: number) => Promise>; /** Clear all visual elements (highlights and annotations) */ clearAllVisuals: () => Promise>; } interface PageCoordinates { x: number; y: number; } interface PageDimensionsInfo { width: number; height: number; rotation: number; } interface CoordinateHelpers { /** Get dimensions of a specific page */ getPageDimensions: (page: number) => PageDimensionsInfo | null; /** Convert percent coordinates (0-100) to pixels */ percentToPixels: (xPercent: number, yPercent: number, page: number) => PageCoordinates | null; /** Convert pixel coordinates to percent (0-100) */ pixelsToPercent: (x: number, y: number, page: number) => PageCoordinates | null; } /** * Imperative handle for PDFViewerClient. * Use this to programmatically control the PDF viewer. * * @example * ```tsx * const viewerRef = useRef(null); * * // Highlight text * viewerRef.current?.highlightText("important keyword"); * * // Draw a rectangle * viewerRef.current?.drawRect({ page: 1, x: 10, y: 20, width: 30, height: 10 }); * * // Navigate * viewerRef.current?.goToPage(5); * ``` */ interface PDFViewerHandle { /** * Find and highlight text in the PDF. * @param text - Text to find and highlight * @param options - Highlight options * @returns Array of highlight IDs created */ highlightText: (text: string, options?: HighlightTextOptions) => Promise; /** * Remove a specific highlight by ID. */ removeHighlight: (id: string) => void; /** * Clear all highlights. */ clearHighlights: () => void; /** * Draw a rectangle on the PDF. * @param options - Rectangle options (coordinates are percentages 0-100) * @returns Annotation ID */ drawRect: (options: DrawRectOptions) => string; /** * Draw a circle on the PDF. * @param options - Circle options (coordinates are percentages 0-100) * @returns Annotation ID */ drawCircle: (options: DrawCircleOptions) => string; /** * Add a note annotation. * @param options - Note options * @returns Annotation ID */ addNote: (options: AddNoteOptions) => string; /** * Remove a specific annotation by ID. */ removeAnnotation: (id: string) => void; /** * Clear all annotations. */ clearAnnotations: () => void; /** * Go to a specific page. Returns a Promise that resolves when scroll completes. * @param page - Page number (1-indexed) * @param options - Navigation options * @returns Promise that resolves when the page is visible */ goToPage: (page: number, options?: GoToPageOptions) => Promise; /** * Go to the next page. */ nextPage: () => void; /** * Go to the previous page. */ previousPage: () => void; /** * Get the current page number. */ getCurrentPage: () => number; /** * Get total number of pages. */ getNumPages: () => number; /** * Set the zoom level. * @param scale - Zoom scale (1.0 = 100%) */ setZoom: (scale: number) => void; /** * Get the current zoom level. */ getZoom: () => number; /** * Zoom in. */ zoomIn: () => void; /** * Zoom out. */ zoomOut: () => void; /** * Search for text in the PDF. * @param query - Search query * @param options - Search options * @returns Search results */ search: (query: string, options?: SearchOptions) => Promise; /** * Go to the next search result. */ nextSearchResult: () => void; /** * Go to the previous search result. */ previousSearchResult: () => void; /** * Clear search results. */ clearSearch: () => void; /** * Search for text and highlight all matches in one operation. * @param query - Text to search for * @param options - Search and highlight options * @returns Structured result with match count and highlight IDs */ searchAndHighlight: (query: string, options?: SearchAndHighlightOptions) => Promise; /** * Agent-friendly tools with structured responses. * Each method returns { success, data?, error? } for easy agent integration. */ agentTools: AgentTools; /** * Coordinate conversion utilities for working with PDF page coordinates. */ coordinates: CoordinateHelpers; /** * Get the underlying PDF document (pdfjs-dist PDFDocumentProxy). */ getDocument: () => PDFDocumentProxy | null; /** * Check if the document is loaded. */ isLoaded: () => boolean; } /** * Main PDF Viewer component. * * This component is SSR-safe and will only render on the client side. * It uses React.lazy and Suspense to defer loading of the PDF viewer * which depends on browser APIs. * * @example * ```tsx * import { PDFViewer } from 'pdfjs-reader-core'; * * function App() { * return ( * console.log('Loaded', doc.numPages, 'pages')} * /> * ); * } * ``` */ declare const PDFViewer: react.NamedExoticComponent; /** * PDF Viewer component with imperative API. * * @example * ```tsx * const viewerRef = useRef(null); * * { * // Highlight text when document loads * viewerRef.current?.highlightText("important", { color: "yellow" }); * }} * /> * * // Call methods anytime * viewerRef.current?.goToPage(5); * viewerRef.current?.drawRect({ page: 1, x: 10, y: 20, width: 30, height: 10 }); * ``` */ declare const PDFViewerClient: react.MemoExoticComponent>>; interface DocumentContainerProps { className?: string; /** Enable touch gestures for mobile */ enableTouchGestures?: boolean; } declare const DocumentContainer: react.NamedExoticComponent; interface VirtualizedDocumentContainerProps { /** Number of pages to render above/below viewport */ overscan?: number; /** Gap between pages in pixels */ pageGap?: number; /** Enable touch gestures */ enableTouchGestures?: boolean; className?: string; } /** * VirtualizedDocumentContainer efficiently renders only visible pages. * * Mobile optimizations: * - Only pages in/near viewport are rendered (virtualization) * - Smooth scrolling with -webkit-overflow-scrolling * - Touch gestures for pinch-zoom and swipe navigation * - Passive event listeners for scroll performance * - Smart page caching to avoid re-fetching */ declare const VirtualizedDocumentContainer: react.NamedExoticComponent; interface ContinuousScrollContainerProps extends VirtualizedDocumentContainerProps { /** Scroll to page smoothly */ smoothScroll?: boolean; } /** * ContinuousScrollContainer is a convenience wrapper around VirtualizedDocumentContainer * that provides continuous scrolling behavior with all pages visible in a single * scrollable view. * * Features: * - All pages rendered in a continuous scroll view * - Virtualized rendering for performance (only visible pages + buffer are rendered) * - Automatic page tracking based on scroll position * - Scroll-to-page functionality * - Memory management (unloads distant pages) */ declare const ContinuousScrollContainer: react.NamedExoticComponent; interface DualPageContainerProps { /** Show the first page alone (like a book cover) */ showCover?: boolean; /** Book spread mode: odd pages on right side */ bookSpread?: boolean; /** Gap between the two pages */ pageGap?: number; /** Enable touch gestures */ enableTouchGestures?: boolean; className?: string; } declare const DualPageContainer: react.NamedExoticComponent; interface BookModeContainerProps { className?: string; /** Flip animation duration in ms (default: 800) */ flippingTime?: number; /** Draw page shadows during flip (default: true) */ drawShadow?: boolean; /** Max shadow opacity 0-1 (default: 0.7) */ maxShadowOpacity?: number; } declare const BookModeContainer: react__default.NamedExoticComponent; interface PDFPageProps { pageNumber: number; page: PDFPageProxy | null; scale: number; rotation: number; className?: string; showTextLayer?: boolean; showHighlightLayer?: boolean; showAnnotationLayer?: boolean; onPageLoad?: (page: PDFPageProxy) => void; onRenderComplete?: () => void; onRenderError?: (error: Error) => void; onAnnotationClick?: (annotation: Annotation) => void; onPageClick?: (pageNumber: number, point: { x: number; y: number; }) => void; } declare const PDFPage: react.NamedExoticComponent; interface CanvasLayerProps { page: PDFPageProxy; scale: number; rotation: number; className?: string; onRenderStart?: () => void; onRenderComplete?: () => void; onRenderError?: (error: Error) => void; /** Priority for rendering (lower = higher priority) - used for render ordering */ priority?: number; } /** * CanvasLayer renders a PDF page at full quality. * * Optimizations for mobile: * - Uses requestAnimationFrame for render timing * - Cancels pending renders on unmount/update * - Uses 'display' intent for faster rendering * - Disables alpha channel for better performance */ declare const CanvasLayer: react.NamedExoticComponent; interface TextLayerProps { page: PDFPageProxy; scale: number; rotation: number; className?: string; } declare const TextLayer: react.NamedExoticComponent; interface HighlightLayerProps { highlights: Highlight[]; scale: number; selectedId?: string | null; onHighlightClick?: (highlight: Highlight) => void; className?: string; /** Filter by source type */ sourceFilter?: 'all' | 'user' | 'agent'; } declare const HighlightLayer: react.NamedExoticComponent; interface AnnotationLayerProps { pageNumber: number; annotations: Annotation[]; scale: number; selectedId?: string | null; isDrawing?: boolean; currentDrawingPath?: DrawingPath | null; drawingColor?: string; drawingStrokeWidth?: number; activeAnnotationTool?: 'note' | 'draw' | 'shape' | null; onAnnotationClick?: (annotation: Annotation) => void; onNoteClick?: (annotation: Annotation, event: { x: number; y: number; }) => void; onDrawStart?: (point: { x: number; y: number; }) => void; onDrawMove?: (point: { x: number; y: number; }) => void; onDrawEnd?: () => void; onPageClick?: (point: { x: number; y: number; }) => void; className?: string; } declare const AnnotationLayer: react.NamedExoticComponent; interface FocusRegionLayerProps { focusedRegions: FocusedRegion[]; scale: number; pageNumber: number; className?: string; } declare const FocusRegionLayer: react.NamedExoticComponent; interface ToolbarProps { className?: string; showNavigation?: boolean; showZoom?: boolean; showRotation?: boolean; showTheme?: boolean; showSidebar?: boolean; showFullscreen?: boolean; } declare const Toolbar: react.NamedExoticComponent; interface MobileToolbarProps { currentPage: number; totalPages: number; onPreviousPage: () => void; onNextPage: () => void; onGoToPage: (page: number) => void; scale: number; onZoomIn: () => void; onZoomOut: () => void; onToggleSidebar: () => void; sidebarOpen: boolean; theme: 'light' | 'dark' | 'sepia'; onThemeChange: (theme: 'light' | 'dark' | 'sepia') => void; position?: 'top' | 'bottom'; className?: string; } declare const MobileToolbar: react.NamedExoticComponent; interface SidebarProps { className?: string; width?: number; } declare const Sidebar: react.NamedExoticComponent; interface MobileSidebarProps { isOpen: boolean; onClose: () => void; activePanel: SidebarPanel; onPanelChange: (panel: SidebarPanel) => void; children: React.ReactNode; className?: string; } declare const MobileSidebar: react.NamedExoticComponent; interface ThumbnailPanelProps { className?: string; thumbnailScale?: number; } declare const ThumbnailPanel: react.NamedExoticComponent; interface SearchPanelProps { className?: string; } declare const SearchPanel: react.NamedExoticComponent; interface OutlinePanelProps { className?: string; } declare const OutlinePanel: react.NamedExoticComponent; interface HighlightsPanelProps { className?: string; /** Callback when a highlight item is clicked */ onHighlightClick?: (highlight: Highlight) => void; } declare const HighlightsPanel: react.NamedExoticComponent; interface BookmarksPanelProps { className?: string; /** Callback when a bookmark item is clicked */ onBookmarkClick?: (bookmark: Bookmark) => void; } declare const BookmarksPanel: react.NamedExoticComponent; interface TakeawaysPanelProps { className?: string; /** Callback when a takeaway item is clicked */ onTakeawayClick?: (takeaway: Takeaway) => void; /** Filter by source */ sourceFilter?: 'all' | 'agent' | 'user'; } declare const TakeawaysPanel: react.NamedExoticComponent; interface SelectionToolbarProps { /** Current text selection */ selection: TextSelection | null; /** Callback when a color is clicked to create highlight */ onCreateHighlight: (color: HighlightColor) => void; /** Callback when copy button is clicked */ onCopy?: () => void; /** Active/default highlight color */ activeColor?: HighlightColor; /** Additional class name */ className?: string; } declare const SelectionToolbar: react.NamedExoticComponent; interface HighlightPopoverProps { /** The highlight to show the popover for */ highlight: Highlight | null; /** The scale of the PDF page */ scale: number; /** The page element containing the highlight */ pageElement: HTMLElement | null; /** Callback when color is changed */ onColorChange: (id: string, color: HighlightColor) => void; /** Callback when comment is updated */ onCommentChange: (id: string, comment: string) => void; /** Callback when delete is clicked */ onDelete: (id: string) => void; /** Callback when copy is clicked */ onCopy?: (text: string) => void; /** Callback when popover is closed */ onClose: () => void; /** Additional class name */ className?: string; } declare const HighlightPopover: react.NamedExoticComponent; type AnnotationTool = 'note' | 'draw' | 'shape' | null; type ShapeType = 'rect' | 'circle' | 'arrow' | 'line'; interface AnnotationState { highlights: Highlight[]; annotations: Annotation[]; selectedHighlightId: string | null; selectedAnnotationId: string | null; activeHighlightColor: HighlightColor; isHighlightMode: boolean; isAnnotationMode: boolean; activeAnnotationTool: AnnotationTool; activeShapeType: ShapeType; drawingColor: string; drawingStrokeWidth: number; currentDrawingPath: DrawingPath | null; currentDrawingPage: number | null; } interface AnnotationActions { addHighlight: (highlight: Omit) => Highlight; updateHighlight: (id: string, updates: Partial) => void; removeHighlight: (id: string) => void; selectHighlight: (id: string | null) => void; setActiveHighlightColor: (color: HighlightColor) => void; getHighlightsByPage: (pageNumber: number) => Highlight[]; addAnnotation: (annotation: Omit) => Annotation; updateAnnotation: (id: string, updates: Partial) => void; removeAnnotation: (id: string) => void; selectAnnotation: (id: string | null) => void; getAnnotationsByPage: (pageNumber: number) => Annotation[]; setHighlightMode: (enabled: boolean) => void; setAnnotationMode: (enabled: boolean) => void; setActiveAnnotationTool: (tool: AnnotationTool) => void; setActiveShapeType: (shapeType: ShapeType) => void; setDrawingColor: (color: string) => void; setDrawingStrokeWidth: (width: number) => void; startDrawing: (pageNumber: number, point: { x: number; y: number; }) => void; addDrawingPoint: (point: { x: number; y: number; }) => void; finishDrawing: () => Annotation | null; cancelDrawing: () => void; exportHighlights: () => string; importHighlights: (json: string) => void; exportAnnotations: () => string; importAnnotations: (json: string) => void; clearAll: () => void; } type AnnotationStore = AnnotationState & AnnotationActions; declare function createAnnotationStore(initialOverrides?: Partial): zustand.StoreApi; type AnnotationStoreApi = ReturnType; interface AnnotationToolbarProps { /** Override the active tool from store */ activeTool?: AnnotationTool; /** Override the active shape type from store */ activeShapeType?: ShapeType; /** Override the drawing color from store */ drawingColor?: string; /** Override the stroke width from store */ strokeWidth?: number; /** Custom tool change handler */ onToolChange?: (tool: AnnotationTool) => void; /** Custom shape type change handler */ onShapeTypeChange?: (shapeType: ShapeType) => void; /** Custom color change handler */ onColorChange?: (color: string) => void; /** Custom stroke width change handler */ onStrokeWidthChange?: (width: number) => void; /** Position of the toolbar */ position?: 'top' | 'bottom' | 'floating'; /** Additional class name */ className?: string; } declare const AnnotationToolbar: react.NamedExoticComponent; interface StickyNoteProps { note: NoteAnnotation; scale: number; isSelected?: boolean; isEditing?: boolean; onSelect?: () => void; onUpdate?: (updates: Partial) => void; onDelete?: () => void; onStartEdit?: () => void; onEndEdit?: () => void; onDragStart?: (e: React.MouseEvent | React.TouchEvent) => void; className?: string; } declare const StickyNote: react.NamedExoticComponent; interface DrawingCanvasProps { width: number; height: number; scale: number; color?: string; strokeWidth?: number; isActive?: boolean; onDrawingComplete?: (path: DrawingPath) => void; className?: string; } declare const DrawingCanvas: react.NamedExoticComponent; interface ShapeRendererProps { shape: ShapeAnnotation; scale: number; isSelected?: boolean; isEditing?: boolean; onSelect?: () => void; onUpdate?: (updates: Partial) => void; onDelete?: () => void; className?: string; } declare const ShapeRenderer: react.NamedExoticComponent; interface ShapePreviewProps { shapeType: 'rect' | 'circle' | 'arrow' | 'line'; startPoint: { x: number; y: number; }; endPoint: { x: number; y: number; }; scale: number; color: string; strokeWidth: number; } declare const ShapePreview: react.NamedExoticComponent; interface QuickNoteButtonProps { /** Page number this button is for */ pageNumber: number; /** Scale of the page */ scale: number; /** Position of the button (relative to page) */ position?: 'top-right' | 'bottom-right'; /** Callback when button is clicked */ onClick: (pageNumber: number, x: number, y: number) => void; /** Custom className */ className?: string; /** Whether the button is visible */ visible?: boolean; } /** * Floating button on each page to quickly add a note. */ declare const QuickNoteButton: react.NamedExoticComponent; interface QuickNotePopoverProps { /** Whether the popover is visible */ visible: boolean; /** Position of the popover */ position: { x: number; y: number; }; /** Initial content */ initialContent?: string; /** Agent's last statement to display */ agentLastStatement?: string; /** Callback when note is saved */ onSave: (content: string) => void; /** Callback when cancelled */ onCancel: () => void; /** Custom className */ className?: string; } /** * Popover for entering quick note content. */ declare const QuickNotePopover: react.NamedExoticComponent; interface AskAboutOverlayProps { /** Whether the overlay is visible */ visible: boolean; /** Progress of the long press (0-1) */ progress: number; /** Position of the overlay */ position: { x: number; y: number; } | null; /** Size of the progress indicator */ size?: number; /** Custom className */ className?: string; } /** * Visual feedback overlay shown during long-press for "Ask About This" feature. * Displays a circular progress indicator. */ declare const AskAboutOverlay: react.NamedExoticComponent; interface AskAboutTriggerProps { /** Position to display the trigger */ position: { x: number; y: number; }; /** Callback when ask about is confirmed */ onConfirm: () => void; /** Callback when cancelled */ onCancel: () => void; /** Whether to show the trigger */ visible: boolean; /** Auto-hide delay in ms (0 = no auto-hide) */ autoHideDelay?: number; /** Custom className */ className?: string; } /** * Confirmation UI shown after long-press completes. * Provides buttons to confirm or cancel the "Ask About This" action. */ declare const AskAboutTrigger: react.NamedExoticComponent; interface MinimapProps { /** Display variant */ variant?: 'sidebar' | 'floating'; /** Position for floating variant */ floatingPosition?: 'left' | 'right'; /** Maximum height in pixels */ maxHeight?: number; /** Whether to show page numbers */ showPageNumbers?: boolean; /** Callback when a page is clicked */ onPageClick?: (pageNumber: number) => void; /** Custom className */ className?: string; } /** * Visual minimap showing reading progress and navigation. */ declare const Minimap: react.NamedExoticComponent; interface FloatingZoomControlsProps { /** Position of the controls */ position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'; /** Additional class name */ className?: string; /** Show fit to width button */ showFitToWidth?: boolean; /** Show fit to page button */ showFitToPage?: boolean; /** Show zoom percentage */ showZoomLevel?: boolean; } /** * Floating zoom controls for easy zoom adjustment. * Shows +/- buttons and optionally fit-to-width/fit-to-page buttons. */ declare const FloatingZoomControls: react.NamedExoticComponent; interface PDFThumbnailNavProps { /** Scale for thumbnails (default: 0.15) */ thumbnailScale?: number; /** Orientation of the thumbnail strip */ orientation?: 'horizontal' | 'vertical'; /** Maximum number of thumbnails visible at once */ maxVisible?: number; /** Custom class name */ className?: string; /** Callback when a thumbnail is clicked */ onThumbnailClick?: (page: number) => void; /** Gap between thumbnails in pixels (default: 8) */ gap?: number; /** Show page numbers below thumbnails (default: true) */ showPageNumbers?: boolean; } /** * PDFThumbnailNav provides a navigable strip of PDF page thumbnails. * Syncs with the current PDF viewer via store subscription. * * Features: * - Virtualized rendering (only visible thumbnails are rendered) * - Auto-scrolls to keep current page visible * - Click to navigate to page * - Horizontal or vertical orientation */ declare const PDFThumbnailNav: react.NamedExoticComponent; interface PDFErrorBoundaryProps { /** Child components to render */ children: ReactNode; /** Custom fallback UI */ fallback?: ReactNode | ((error: Error, reset: () => void) => ReactNode); /** Callback when an error is caught */ onError?: (error: Error, errorInfo: ErrorInfo) => void; /** Whether to show the default error UI */ showDefaultUI?: boolean; className?: string; } interface PDFErrorBoundaryState { hasError: boolean; error: Error | null; } declare class PDFErrorBoundary extends Component { constructor(props: PDFErrorBoundaryProps); static getDerivedStateFromError(error: Error): PDFErrorBoundaryState; componentDidCatch(error: Error, errorInfo: ErrorInfo): void; handleReset: () => void; render(): ReactNode; } interface WithErrorBoundaryProps extends Omit { component: ReactNode; } declare function withErrorBoundary({ component, ...props }: WithErrorBoundaryProps): ReactNode; interface PDFLoadingScreenProps { /** 0-100 progress percentage, undefined for indeterminate */ progress?: number; /** Bytes loaded */ bytesLoaded?: number; /** Total bytes */ totalBytes?: number; /** Current loading phase */ phase?: 'initializing' | 'fetching' | 'parsing' | 'rendering'; /** Optional: document title for context */ documentName?: string; /** Additional class name */ className?: string; /** Show compact inline mode for streaming progress */ compact?: boolean; /** Show streaming progress (when first page is ready but background loading continues) */ showStreamingProgress?: boolean; } /** * Beautiful loading screen for PDF documents. * Shows animated document icon, progress bar, and phase-specific messages. */ declare const PDFLoadingScreen: react.NamedExoticComponent; interface ChunkHistoryEntry { text: string; pageNumber: number; timestamp: number; } type EngineStatus = 'idle' | 'transitioning' | 'executing'; type LlmStatus = 'idle' | 'in-flight' | 'failed'; interface DebugEvent { id: string; timestamp: number; kind: 'chunk' | 'llm-request' | 'llm-response' | 'llm-error' | 'storyboard-execute' | 'fallback-fired' | 'note'; /** Short headline shown in the log */ summary: string; /** Full payload (chunk text, raw LLM response, parsed storyboard, error, etc.) */ payload?: unknown; } interface NarrationState { currentChunk: string | null; currentPage: number; chunkHistory: ChunkHistoryEntry[]; camera: CameraState; activeOverlays: ActiveOverlay[]; engineStatus: EngineStatus; llmStatus: LlmStatus; lastStoryboard: Storyboard | null; lastError: string | null; isPaused: boolean; debugEvents: DebugEvent[]; } interface NarrationActions { setCurrentChunk: (chunk: string | null) => void; setCurrentPage: (page: number) => void; pushChunkHistory: (entry: ChunkHistoryEntry) => void; setCamera: (camera: Partial) => void; addOverlay: (overlay: ActiveOverlay) => void; removeOverlay: (id: string) => void; clearOverlays: (predicate?: (o: ActiveOverlay) => boolean) => void; setEngineStatus: (s: EngineStatus) => void; setLlmStatus: (s: LlmStatus, error?: string | null) => void; setLastStoryboard: (sb: Storyboard | null) => void; setPaused: (paused: boolean) => void; appendDebugEvent: (event: Omit) => void; clearDebugEvents: () => void; reset: () => void; } type NarrationStore = NarrationState & NarrationActions; declare function createNarrationStore(overrides?: Partial): zustand.StoreApi; type NarrationStoreApi = ReturnType; declare function makeOverlayId(action: StoryboardAction): string; declare const SYSTEM_PROMPT = "You are the cinematic director of an AI tutor's PDF visualization. The tutor speaks one \"chunk\" at a time; for each chunk you anchor the visuals onto the EXACT blocks on the page the tutor is talking about, so the reader sees the page react like a produced teaching video. Think of yourself as a motion designer layering effects on top of a document \u2014 zoom is only one tool in the kit, and often not the right one.\n\n# Your primary task\nYou are given a list of blocks for the current page under \"Page blocks\", each with a `block_id`, `text`, `type`, `bbox`, and `default_action`. Your #1 job is to decide WHICH block(s) the current chunk is referring to, and then pick the right visual action(s) to anchor there.\n\nAnchoring rules:\n- EVERY action that references a block MUST set `target_block` (or `from_block`/`to_block` for callouts) to an EXISTING `block_id` from \"Page blocks\" (or \"Cross-page figures index\" for cross-page refs). Never invent an id. Never emit a step whose target can't be found in the provided lists.\n- Match the chunk to blocks by semantic overlap with the block's `text`: quoted phrases, named entities, keywords, figure references (\"Fig 3.2\", \"the suture\"), list enumerations.\n- If no block clearly matches, emit a single `camera` step that fits the page (no overlays) and explain in `reasoning`. Do NOT spray overlays onto random blocks.\n- If multiple blocks match, pick the most specific one, or use a `callout` from one to the other.\n\n# Output shape\nOutput ONLY this JSON, nothing else:\n{\n \"version\": 1,\n \"steps\": [ { \"at_ms\": , \"duration_ms\": , \"action\": }, ... ]\n}\n\n# Action shapes \u2014 ALL fields shown are REQUIRED per action type\n- camera: { \"type\":\"camera\", \"target_block\":\"\", \"scale\":1.1, \"padding\":80, \"easing\":\"ease-out\" }\n- spotlight: { \"type\":\"spotlight\", \"target_block\":\"\", \"dim_opacity\":0.65, \"feather_px\":40, \"shape\":\"rounded\" }\n- underline: { \"type\":\"underline\", \"target_block\":\"\", \"color\":\"#FBBF24\", \"style\":\"sketch\", \"draw_duration_ms\":600 }\n- highlight: { \"type\":\"highlight\", \"target_block\":\"\", \"color\":\"rgba(250,204,21,0.35)\", \"draw_duration_ms\":500 }\n- pulse: { \"type\":\"pulse\", \"target_block\":\"\", \"count\":2, \"intensity\":\"normal\" }\n- callout: { \"type\":\"callout\", \"from_block\":\"\", \"to_block\":\"\", \"label\":\"\", \"curve\":\"curved\" }\n- ghost_reference: { \"type\":\"ghost_reference\", \"target_page\":, \"target_block\":\"\", \"position\":\"top-right\" }\n- box: { \"type\":\"box\", \"target_block\":\"\", \"color\":\"#3B82F6\", \"style\":\"solid\" }\n- label: { \"type\":\"label\", \"target_block\":\"\", \"text\":\"\", \"position\":\"top\" }\n- clear: { \"type\":\"clear\", \"targets\":\"overlays\" }\n\n# When to use each action (match the effect to the narration's intent, not just the block type)\n- camera \u2014 when focus SHIFTS to a new region. If the primary block is already on-screen and roughly centered, you may skip camera entirely and start with an overlay. When you do use camera, prefer scale 1.1\u20131.4 for gentle re-centering; reserve 1.5+ for dense figures you need to inspect closely.\n- spotlight \u2014 when narration ISOLATES one idea, term, or sentence. Great for definitions, principles, and \"the key insight is\u2026\" moments.\n- underline \u2014 when narration QUOTES a phrase or reads a line word-by-word. Use style \"sketch\" for handwritten feel, \"straight\" for formal, \"wavy\" for emphasis. Pairs well with spotlight.\n- highlight \u2014 when narration FLAGS a keyword inline without full focus. Cheap, fast, great for list items, definitions-in-context, callback references.\n- pulse \u2014 when narration says \"notice this\" / \"see here\" / \"look at the diagram\". Use with figures, icons, and anchor blocks that should catch the eye without blocking surrounding content.\n- callout \u2014 when narration CONNECTS two things on the page: a caption to its figure, a label to a region, one list item to another. Arrow implies directional meaning.\n- ghost_reference \u2014 when narration REFERS to a block on a DIFFERENT page (\"as we saw on page 2\u2026\"). Never use for same-page references.\n- box \u2014 when narration FRAMES a structural region: a table, a sidebar, a group of related items, a diagram subpart. Use dashed style for \"in-progress\" / \"under discussion\" regions.\n- label \u2014 when narration attaches a NAMED TAG to a block: \"this is the definition\", \"this is an example\", \"step 3\". Keep text \u226440 chars for readability.\n- clear \u2014 rarely needed; the engine auto-expires overlays. Use only when you explicitly want to wipe prior state before a new beat.\n\nRespect each block's `default_action` as a soft hint, but override it freely when narration intent calls for a different effect. A paragraph labeled `default_action: spotlight` can absolutely take a highlight or underline if that's what the narration asks for.\n\n# Intent Taxonomy \u2014 canonical \"recipes\" you should use as your default vocabulary\nWhen narration fits one of these patterns, emit the corresponding storyboard shape (fill in real block_ids from the page). You may also COMPOSE freely beyond these \u2014 they are a floor, not a ceiling.\n\n## define \u2014 the narration introduces or defines a term\nShape: spotlight the term + underline it + drop a label tag. No camera move if the block is already on-screen.\n{\n \"version\": 1,\n \"steps\": [\n { \"at_ms\":0, \"duration_ms\":700, \"action\": { \"type\":\"spotlight\", \"target_block\":\"p1_para0\", \"dim_opacity\":0.6, \"feather_px\":40, \"shape\":\"rounded\" } },\n { \"at_ms\":200, \"duration_ms\":800, \"action\": { \"type\":\"underline\", \"target_block\":\"p1_para0\", \"color\":\"#FBBF24\", \"style\":\"sketch\", \"draw_duration_ms\":700 } },\n { \"at_ms\":900, \"duration_ms\":1200, \"action\": { \"type\":\"label\", \"target_block\":\"p1_para0\", \"text\":\"definition\", \"position\":\"top\" } }\n ]\n}\n\n## point_out \u2014 the narration directs the viewer's eye to a figure, diagram, or specific region\nShape: gentle camera move + callout arrow from caption to figure + pulse the figure.\n{\n \"version\": 1,\n \"steps\": [\n { \"at_ms\":0, \"duration_ms\":600, \"action\": { \"type\":\"camera\", \"target_block\":\"p1_fig0\", \"scale\":1.3, \"padding\":80, \"easing\":\"ease-out\" } },\n { \"at_ms\":400, \"duration_ms\":900, \"action\": { \"type\":\"callout\", \"from_block\":\"p1_cap1\", \"to_block\":\"p1_fig0\", \"label\":\"see here\", \"curve\":\"curved\" } },\n { \"at_ms\":900, \"duration_ms\":1200, \"action\": { \"type\":\"pulse\", \"target_block\":\"p1_fig0\", \"count\":2, \"intensity\":\"normal\" } }\n ]\n}\n\n## compare \u2014 the narration contrasts two things on the page\nShape: box A + box B + callout between them with a relational label.\n{\n \"version\": 1,\n \"steps\": [\n { \"at_ms\":0, \"duration_ms\":600, \"action\": { \"type\":\"box\", \"target_block\":\"p1_list5\", \"color\":\"#3B82F6\", \"style\":\"solid\" } },\n { \"at_ms\":300, \"duration_ms\":600, \"action\": { \"type\":\"box\", \"target_block\":\"p1_list12\", \"color\":\"#F472B6\", \"style\":\"solid\" } },\n { \"at_ms\":800, \"duration_ms\":1000, \"action\": { \"type\":\"callout\", \"from_block\":\"p1_list5\", \"to_block\":\"p1_list12\", \"label\":\"vs\", \"curve\":\"curved\" } }\n ]\n}\n\n## emphasize \u2014 the narration stresses a keyword, warning, or takeaway\nShape: highlight + pulse. Fast, punchy, no camera.\n{\n \"version\": 1,\n \"steps\": [\n { \"at_ms\":0, \"duration_ms\":500, \"action\": { \"type\":\"highlight\", \"target_block\":\"p1_list0\", \"color\":\"rgba(250,204,21,0.35)\", \"draw_duration_ms\":450 } },\n { \"at_ms\":350, \"duration_ms\":800, \"action\": { \"type\":\"pulse\", \"target_block\":\"p1_list0\", \"count\":2, \"intensity\":\"strong\" } }\n ]\n}\n\n# Choreography rules\n- HARD REQUIREMENT: every storyboard MUST contain at least ONE non-camera step. A lone camera step is NEVER a valid output \u2014 the viewer needs an overlay to know WHY the camera moved. If you cannot find a good overlay target, emit a `highlight` or `pulse` on your primary block as the second step.\n- Favor VARIETY that matches narration texture \u2014 a definition earns different visuals than a comparison. Don't send every chunk through the same zoom+box motion.\n- Include a camera step only when focus genuinely shifts to a new region. If the primary target is already on-screen and roughly centred, SKIP the camera entirely and start directly with an overlay.\n- Camera scale: default to **1.1** (gentle re-centre). Use **1.2\u20131.3** for normal reading distance. Use **1.4\u20131.6** ONLY for dense figures or small inline details. NEVER use a scale below 0.5 or above 4.0 \u2014 the engine rejects those. When in doubt, use 1.1.\n- Prefer overlays that OVERLAP the camera move. A camera that takes 700ms to finish while overlays fire at 200ms feels cinematic; overlays waiting until after the camera settles feel sluggish. Stagger `at_ms`: camera at 0, first overlay at 150\u2013300ms, second overlay at 600\u2013900ms.\n- 2\u20134 steps is typical; single-step overlays (no camera) are PREFERRED when the target is already visible.\n- When narration compares two things, USE the compare recipe (box + box + callout), not a single camera step. When narration says \"key takeaway\", USE emphasize (highlight + pulse).\n- Never target a block_id not present in the provided lists. If no block matches, emit a single camera step at scale 1.0 + a subtle pulse on the nearest heading; explain in `reasoning`.\n- Output ONLY valid JSON. No markdown, no code fences, no commentary, no trailing whitespace inside property values.\n\n# Forbidden outputs \u2014 these will be rejected:\n- A storyboard with only a camera step.\n- A camera step with scale < 0.5 or > 4.0.\n- target_block values not listed in \"Page blocks\" or \"Cross-page figures index\".\n- Tab characters, newlines, or explanatory text inside JSON string values."; interface BuildUserPromptInput { chunk: string; pageNumber: number; page: PageBBoxData; index: BBoxIndex; history: ChunkHistoryEntry[]; camera: CameraState; activeOverlays: ActiveOverlay[]; maxSteps?: number; } /** Truncate text to ~max chars, word-aware. */ declare function truncate(text: string | null, max?: number): string; declare function buildUserPrompt(input: BuildUserPromptInput): string; interface LlmConfig { endpointUrl: string; model: string; authToken?: string; extraBody?: Record; maxTokens?: number; temperature?: number; /** If true, include response_format: json_schema (disable if the backend doesn't support it). */ useJsonSchema?: boolean; /** If true, request streaming (default false: simpler, more reliable for non-streaming backends). */ stream?: boolean; } interface DirectorInput extends BuildUserPromptInput { signal?: AbortSignal; timeoutMs?: number; } interface DirectorResult { storyboard: Storyboard | null; raw: string; error?: string; } /** * Call the LLM, stream the response, extract a JSON storyboard, validate with zod. * Returns { storyboard: null } with an error string on any failure path. */ declare function directStoryboard(config: LlmConfig, input: DirectorInput): Promise; interface EmbeddingProvider { /** Return embeddings (normalized or raw — similarity function handles either). */ embed: (texts: string[]) => Promise; } declare function cosineSimilarity(a: Float32Array, b: Float32Array): number; interface BlockMatch { block: Block; score: number; } declare function matchChunkToBlock(chunk: string, page: PageBBoxData, provider: EmbeddingProvider): Promise; /** * Build a fallback storyboard from a matched block. Keys on `block.type` * (7 types) rather than `block.default_action` (4 values) so every block * class produces a visually distinct storyboard — not every failure rounds * down to camera + box. An optional `page` lets caption blocks route to a * callout→figure on the same page. * * Never throws — emits a clear-only storyboard when no match is supplied. */ declare function storyboardFromMatch(match: BlockMatch | null, page?: PageBBoxData): Storyboard; /** * Input passed to a consumer-supplied `storyboardProvider`. Gives the * provider everything the built-in director already has access to, so * the consumer can decide how much context to forward to their own * endpoint (typically: just `chunk` + `pageNumber`, since bbox is * usually cached server-side). */ interface StoryboardProviderInput { chunk: string; pageNumber: number; /** The current page's bbox data — included in case the provider wants * to forward it, though usually the provider's backend already has * this cached. */ page: PageBBoxData; /** Last few chunks the tutor has spoken, for conversational context. */ history: ReadonlyArray<{ text: string; pageNumber: number; timestamp: number; }>; /** AbortSignal — if the consumer's fetch supports it, wire it up so * a newer chunk cancels a stale in-flight request. */ signal: AbortSignal; } interface TutorModeContainerProps { pageNumber: number; bboxData: PageBBoxData[]; narrationStore: NarrationStoreApi; scale: number; rotation?: number; /** Reactive chunk from the tutor (updates as she speaks) */ currentChunk?: string | null; /** LLM endpoint configuration used by the built-in director. */ llm?: LlmConfig; /** * Consumer-owned director. When provided, this is called per chunk * INSTEAD OF `directStoryboard(llm, …)`. Return a storyboard matching * `StoryboardSchema` (or `null` to skip the chunk). The library still * validates the return value, runs salvage (range clamp, overlay- * presence), and emits debug events, so DebugLog telemetry is * identical to the built-in path. * * Use this when your backend owns the system prompt + bbox context * (e.g. a fine-tuned director endpoint) and you want to iterate on * prompt/model choices without a library upgrade. * * Priority: if BOTH `storyboardProvider` and `llm` are set, the * provider wins and `llm` is ignored. * * Added in v0.5.0. */ storyboardProvider?: (input: StoryboardProviderInput) => Promise; /** Milliseconds of no new chunks before the camera returns to fit-page */ idleTimeoutMs?: number; /** LLM call timeout in ms. Default 30000 (Qwen3-32B and similar can take 5-15s). */ llmTimeoutMs?: number; /** Optional embedding provider for fallback matching when the LLM fails */ embeddingProvider?: EmbeddingProvider; /** Show subtitle bar with the current chunk text (default: true) */ showSubtitles?: boolean; /** * Show the "Reset view" button (default: true). Clicking it clears all * overlays and returns the camera to fit-page. If `onExitTutorMode` is * also provided, it runs after the reset — useful when the host app * wants the same button to also leave tutor mode entirely. */ showExitButton?: boolean; /** * Optional callback fired AFTER the engine's resetVisuals. Provide this * only if the host wants the reset button to also leave tutor mode / * navigate away. Omit it for a pure "reset the visuals" behaviour. */ onExitTutorMode?: () => void; /** * Minimum hold time (ms) for every overlay, regardless of the * `duration_ms` the LLM specifies. Short LLM-emitted durations (600-1200ms) * flash past too quickly to read; bump this for narration-paired UX. * Default: 3500ms. */ minOverlayDurationMs?: number; /** * Background colour of the container surround (visible around the PDF when * the viewport is larger than the page fit). Default: `#ffffff`. Pass a * dark value for dark-themed hosts. */ backgroundColor?: string; /** * Optional content to render while the PDF document/page is still loading. * Receives the loading stage so the host can show a spinner, a skeleton, * or a custom brand. If omitted, a minimal default spinner is rendered on * the `backgroundColor` surround. */ loadingComponent?: react__default.ReactNode; /** * Fired when the underlying viewer's page changes from any source — the * agent API (`agentTools.goToPage / nextPage / previousPage`), the sidebar * (bookmarks, thumbnails, search), or a programmatic * `useViewerStore().goToPage` call. Use this to keep your own * `pageNumber` state (the controlled prop you pass in) in lockstep with * the viewer, so agent-driven navigation actually moves the rendered * page and downstream concerns (progress save, recap, etc.) see the * right value. * * ```tsx * const [currentPage, setCurrentPage] = useState(1); * * ``` * * Added in v0.4.2. */ onPageChange?: (page: number) => void; className?: string; } /** Build a cross-page/block index from the raw bbox list. */ declare function buildBBoxIndex(bboxData: PageBBoxData[]): BBoxIndex; declare function TutorModeContainer({ pageNumber, bboxData, narrationStore, scale, rotation, currentChunk, llm, idleTimeoutMs, llmTimeoutMs, embeddingProvider, showSubtitles, showExitButton, onExitTutorMode, minOverlayDurationMs, backgroundColor, loadingComponent, onPageChange, storyboardProvider, className, }: TutorModeContainerProps): react_jsx_runtime.JSX.Element; interface CinemaLayerProps { page: PageBBoxData; index: BBoxIndex; overlays: ActiveOverlay[]; scale: number; } declare function CinemaLayer({ page, index, overlays, scale, }: CinemaLayerProps): react_jsx_runtime.JSX.Element; interface CameraViewProps { camera: CameraState; children: react__default.ReactNode; /** total duration for the tween, in ms */ durationMs?: number; className?: string; } /** * Wraps page content in a Framer Motion container that animates scale+translate. * The origin is the center of the container. */ declare function CameraView({ camera, children, durationMs, className, }: CameraViewProps): react_jsx_runtime.JSX.Element; interface SpotlightMaskProps { page: PageDimensionsDpi; bbox: BBoxCoords; action: ActionSpotlight; durationMs?: number; } /** * Design: **Theatrical spotlight.** A warm-ink dim (not pure black) * closes over the whole page, then a feathered cutout reveals the * target block. A thin terracotta accent ring follows the cutout shape * — the same colour used by every other overlay so the spotlit region * is clearly part of the same annotation system. * * The dim colour is warm INK (`#2a2420`) rather than `#000` so the * dimmed area looks like the PDF pushed into shadow rather than * blacked out. On light textbook pages this reads as "atmosphere" * instead of "censorship". * * The accent ring animates with a slight stagger after the dim, so the * reveal has a subtle two-beat cadence: darken → point. */ declare function SpotlightMask({ page, bbox, action, durationMs, }: SpotlightMaskProps): react_jsx_runtime.JSX.Element; interface AnimatedUnderlineProps { bbox: BBoxCoords; action: ActionUnderline; } declare function AnimatedUnderline({ bbox, action }: AnimatedUnderlineProps): react_jsx_runtime.JSX.Element; interface AnimatedHighlightProps { bbox: BBoxCoords; action: ActionHighlight; } declare function AnimatedHighlight({ bbox, action }: AnimatedHighlightProps): react_jsx_runtime.JSX.Element; interface PulseOverlayProps { bbox: BBoxCoords; action: ActionPulse; } /** * Design: **Camera viewfinder brackets.** Four L-shapes slide in from * each corner of the target block, like framing crosshairs on a camera. * Behind them, a soft radial glow pulses in sync with the bracket * emphasis — a "look here" cue that points without blocking content. * * Intensity controls bracket length + stroke weight + glow strength. * `count` controls how many emphasis pulses happen before the overlay * settles. * * Brackets slide in from OUTSIDE the block then snap to the corners, * which reads as "framing" rather than the usual bordered box. */ declare function PulseOverlay({ bbox, action }: PulseOverlayProps): react_jsx_runtime.JSX.Element; interface CalloutArrowProps { fromBbox: BBoxCoords; toBbox: BBoxCoords; action: ActionCallout; } /** * Design: **Editorial connector.** Hand-drawn feel via a refined stroke * + a small origin dot at the "from" end (like a teacher's pen planted * on the page before they draw the arrow). The arrowhead is a slender * open caret rather than a heavy filled triangle — reads as annotation, * not direction-signage. * * An optional label pill rides the arrow's trailing end using the same * editorial pin language as StickyLabel (cream paper, terracotta rule, * serif small-caps). Everything uses ACCENT for colour consistency. */ declare function CalloutArrow({ fromBbox, toBbox, action }: CalloutArrowProps): react_jsx_runtime.JSX.Element; interface GhostReferenceProps { page: PageDimensionsDpi; sourceBbox: BBoxCoords; sourceBlockText: string | null; sourcePageNumber: number; action: ActionGhostReference; } declare function GhostReference({ page, sourceBbox, sourceBlockText, action, }: GhostReferenceProps): react_jsx_runtime.JSX.Element; interface BoxOverlayProps { bbox: BBoxCoords; action: ActionBox; } /** * Design: **Framed region.** A structural region marker — thinner and * more refined than the previous generic 3px coloured border. A subtle * accent-tinted wash fills the interior so the framed region reads as * "selected" even without a thick border. Dashed style uses a custom * dash pattern that matches the editorial vocabulary. * * Honours `action.color` when the LLM specifies something non-default, * otherwise falls through to the terracotta accent so boxes fit the * system. */ declare function BoxOverlay({ bbox, action }: BoxOverlayProps): react_jsx_runtime.JSX.Element; interface StickyLabelProps { /** * Pre-computed anchor point in viewport pixels — the label will be * placed at this point and positioned relative to it via * `action.position`. See `LabelOverlay` for the coordinate math that * maps a block's bbox + camera state to this value. */ screenAnchor: { x: number; y: number; }; action: ActionLabel; } /** * Editorial annotation pin. The label body sits a short distance from * the target block, connected by a hairline stem and a small marker * disc at the anchor point. Body uses a classical serif at small-caps * sizing — feels like a scholar's tag, not a browser toast. * * Positioning math: caller pre-computes the `screenAnchor` (usually the * midpoint of the block edge the label is attached to), and this * component lays out the pin on the appropriate side via CSS translate * offsets. */ declare function StickyLabel({ screenAnchor, action }: StickyLabelProps): react_jsx_runtime.JSX.Element; interface SubtitleBarProps { text: string | null; } declare function SubtitleBar({ text }: SubtitleBarProps): react_jsx_runtime.JSX.Element; interface ViewportSize { width: number; height: number; } interface EngineDeps { narrationStore: NarrationStoreApi; bboxIndex: BBoxIndex; /** Callback to read current viewport size (pixels). */ getViewport: () => ViewportSize; /** * Minimum time (ms) an overlay stays on screen regardless of what * `step.duration_ms` the LLM emitted. The schema caps duration at 5000ms * and recipes typically suggest 600-1200ms — too short to register * visually in a narration context. Default: 3500ms. */ minOverlayDurationMs?: number; /** * When true, only ONE annotation overlay is on-screen at any time: every * new overlay-emitting step first clears all prior overlays and their * auto-removal timers. * * Defaults to `isIOSMobile()` — on iOS Safari/WebKit, stacking 2-3 overlays * (e.g. highlight + box + underline + callout) on the same anchor is the * single biggest remaining crash vector we've measured. The GPU * compositor rejects the layer combination long before the canvas cap * kicks in. Dropping to one overlay at a time sacrifices overlay chaining * on iOS for a hard stability guarantee. * * Pass `false` explicitly to force multi-overlay mode even on iOS (e.g. * for tests or a deliberate opt-out in the consumer). */ singleActiveOverlay?: boolean; } declare class StoryboardEngine { private deps; /** * Timers that schedule the START of a step (via `setTimeout(runStep, at_ms)`). * These are storyboard-scoped: when a new storyboard arrives, anything still * pending should be abandoned. */ private pendingStepTimers; /** * Timers that auto-REMOVE an already-placed overlay after its visible * duration. Keyed by overlay id so we can cancel one specifically. These are * NOT cancelled when a new storyboard starts — otherwise the still-visible * overlay from the previous beat would get stranded in the store forever * (the "stuck spotlight" bug). */ private overlayRemovalTimers; private currentStoryboardId; /** * Resolved value of `deps.singleActiveOverlay`. Cached at construction so * each step dispatch is a single branch rather than a per-call * userAgent sniff, and so the policy stays stable for the engine's * lifetime (a viewport flip that recreates the engine will re-evaluate). */ private readonly singleActiveOverlay; constructor(deps: EngineDeps); /** * Execute a new storyboard. Cancels in-flight steps from the previous storyboard * and smoothly transitions the camera/overlays from the current state. */ execute(storyboard: Storyboard): void; /** * Abort pending STEP dispatches from the current storyboard. Overlay * removal timers are left alone so already-visible overlays still auto- * expire on their own schedule. To force-clear every overlay, call * `resetVisuals()` instead. */ cancelPending(): void; /** Cancel every removal timer (used by resetVisuals and destroy). */ private cancelAllRemovalTimers; /** * Full destructor — cancels BOTH pending step timers AND overlay * removal timers. Use this in component unmount / cleanup. * * The per-storyboard `cancelPending()` deliberately leaves overlay * removal timers alone so a mid-flight overlay doesn't get stranded * when a new storyboard arrives (see `overlayRemovalTimers` doc). * That invariant is correct inside a session, but at teardown we * must release every timer — otherwise their setTimeout closures * keep `deps` (narrationStore, the full bboxIndex) alive beyond the * lifetime of this engine. Over many component recreations on iOS * Safari (viewport state churns during address-bar scroll * animation), that cumulative retention contributes to tab reloads. */ destroy(): void; /** Test-only — exposes internal queue sizes for regression tests. */ _queueSizesForTest(): { pending: number; removals: number; }; /** Reset visuals: clear overlays, cancel every removal timer, fit camera. */ resetVisuals(): void; /** Execute one step — dispatch to narrationStore. Returns true if applied. */ private runStep; private applyCamera; } declare const StoryboardActionSchema: z.ZodUnion<[z.ZodEffects; target_block: z.ZodOptional; target_bbox: z.ZodOptional>; scale: z.ZodDefault; padding: z.ZodDefault; easing: z.ZodDefault>; }, "strip", z.ZodTypeAny, { type: "camera"; scale: number; easing: "linear" | "ease-in" | "ease-out" | "ease-in-out"; padding: number; target_block?: string | undefined; target_bbox?: [number, number, number, number] | undefined; }, { type: "camera"; scale?: number | undefined; easing?: "linear" | "ease-in" | "ease-out" | "ease-in-out" | undefined; padding?: number | undefined; target_block?: string | undefined; target_bbox?: [number, number, number, number] | undefined; }>, { type: "camera"; scale: number; easing: "linear" | "ease-in" | "ease-out" | "ease-in-out"; padding: number; target_block?: string | undefined; target_bbox?: [number, number, number, number] | undefined; }, { type: "camera"; scale?: number | undefined; easing?: "linear" | "ease-in" | "ease-out" | "ease-in-out" | undefined; padding?: number | undefined; target_block?: string | undefined; target_bbox?: [number, number, number, number] | undefined; }>, z.ZodObject<{ type: z.ZodLiteral<"spotlight">; target_block: z.ZodString; dim_opacity: z.ZodDefault; feather_px: z.ZodDefault; shape: z.ZodDefault>; }, "strip", z.ZodTypeAny, { type: "spotlight"; shape: "rect" | "rounded" | "ellipse"; target_block: string; dim_opacity: number; feather_px: number; }, { type: "spotlight"; target_block: string; shape?: "rect" | "rounded" | "ellipse" | undefined; dim_opacity?: number | undefined; feather_px?: number | undefined; }>, z.ZodObject<{ type: z.ZodLiteral<"underline">; target_block: z.ZodString; color: z.ZodDefault; style: z.ZodDefault>; draw_duration_ms: z.ZodDefault; }, "strip", z.ZodTypeAny, { style: "straight" | "sketch" | "double" | "wavy"; color: string; type: "underline"; target_block: string; draw_duration_ms: number; }, { type: "underline"; target_block: string; style?: "straight" | "sketch" | "double" | "wavy" | undefined; color?: string | undefined; draw_duration_ms?: number | undefined; }>, z.ZodObject<{ type: z.ZodLiteral<"highlight">; target_block: z.ZodString; color: z.ZodDefault; draw_duration_ms: z.ZodDefault; }, "strip", z.ZodTypeAny, { color: string; type: "highlight"; target_block: string; draw_duration_ms: number; }, { type: "highlight"; target_block: string; color?: string | undefined; draw_duration_ms?: number | undefined; }>, z.ZodObject<{ type: z.ZodLiteral<"pulse">; target_block: z.ZodString; count: z.ZodDefault; intensity: z.ZodDefault>; }, "strip", z.ZodTypeAny, { type: "pulse"; intensity: "subtle" | "normal" | "strong"; target_block: string; count: number; }, { type: "pulse"; target_block: string; intensity?: "subtle" | "normal" | "strong" | undefined; count?: number | undefined; }>, z.ZodObject<{ type: z.ZodLiteral<"callout">; from_block: z.ZodString; to_block: z.ZodString; label: z.ZodOptional; curve: z.ZodDefault>; }, "strip", z.ZodTypeAny, { type: "callout"; curve: "straight" | "curved" | "zigzag"; from_block: string; to_block: string; label?: string | undefined; }, { type: "callout"; from_block: string; to_block: string; label?: string | undefined; curve?: "straight" | "curved" | "zigzag" | undefined; }>, z.ZodObject<{ type: z.ZodLiteral<"ghost_reference">; target_page: z.ZodNumber; target_block: z.ZodString; position: z.ZodDefault>; }, "strip", z.ZodTypeAny, { type: "ghost_reference"; position: "top-right" | "top-left" | "bottom-right" | "bottom-left"; target_block: string; target_page: number; }, { type: "ghost_reference"; target_block: string; target_page: number; position?: "top-right" | "top-left" | "bottom-right" | "bottom-left" | undefined; }>, z.ZodObject<{ type: z.ZodLiteral<"box">; target_block: z.ZodString; color: z.ZodDefault; style: z.ZodDefault>; }, "strip", z.ZodTypeAny, { style: "solid" | "dashed"; color: string; type: "box"; target_block: string; }, { type: "box"; target_block: string; style?: "solid" | "dashed" | undefined; color?: string | undefined; }>, z.ZodObject<{ type: z.ZodLiteral<"label">; target_block: z.ZodString; text: z.ZodString; position: z.ZodDefault>; }, "strip", z.ZodTypeAny, { text: string; type: "label"; position: "top" | "bottom" | "left" | "right"; target_block: string; }, { text: string; type: "label"; target_block: string; position?: "top" | "bottom" | "left" | "right" | undefined; }>, z.ZodObject<{ type: z.ZodLiteral<"clear">; targets: z.ZodDefault, z.ZodArray]>>; }, "strip", z.ZodTypeAny, { type: "clear"; targets: "all" | "spotlights" | "overlays" | string[]; }, { type: "clear"; targets?: "all" | "spotlights" | "overlays" | string[] | undefined; }>]>; declare const StoryboardSchema: z.ZodObject<{ version: z.ZodLiteral<1>; reasoning: z.ZodDefault>; steps: z.ZodArray; duration_ms: z.ZodDefault; action: z.ZodUnion<[z.ZodEffects; target_block: z.ZodOptional; target_bbox: z.ZodOptional>; scale: z.ZodDefault; padding: z.ZodDefault; easing: z.ZodDefault>; }, "strip", z.ZodTypeAny, { type: "camera"; scale: number; easing: "linear" | "ease-in" | "ease-out" | "ease-in-out"; padding: number; target_block?: string | undefined; target_bbox?: [number, number, number, number] | undefined; }, { type: "camera"; scale?: number | undefined; easing?: "linear" | "ease-in" | "ease-out" | "ease-in-out" | undefined; padding?: number | undefined; target_block?: string | undefined; target_bbox?: [number, number, number, number] | undefined; }>, { type: "camera"; scale: number; easing: "linear" | "ease-in" | "ease-out" | "ease-in-out"; padding: number; target_block?: string | undefined; target_bbox?: [number, number, number, number] | undefined; }, { type: "camera"; scale?: number | undefined; easing?: "linear" | "ease-in" | "ease-out" | "ease-in-out" | undefined; padding?: number | undefined; target_block?: string | undefined; target_bbox?: [number, number, number, number] | undefined; }>, z.ZodObject<{ type: z.ZodLiteral<"spotlight">; target_block: z.ZodString; dim_opacity: z.ZodDefault; feather_px: z.ZodDefault; shape: z.ZodDefault>; }, "strip", z.ZodTypeAny, { type: "spotlight"; shape: "rect" | "rounded" | "ellipse"; target_block: string; dim_opacity: number; feather_px: number; }, { type: "spotlight"; target_block: string; shape?: "rect" | "rounded" | "ellipse" | undefined; dim_opacity?: number | undefined; feather_px?: number | undefined; }>, z.ZodObject<{ type: z.ZodLiteral<"underline">; target_block: z.ZodString; color: z.ZodDefault; style: z.ZodDefault>; draw_duration_ms: z.ZodDefault; }, "strip", z.ZodTypeAny, { style: "straight" | "sketch" | "double" | "wavy"; color: string; type: "underline"; target_block: string; draw_duration_ms: number; }, { type: "underline"; target_block: string; style?: "straight" | "sketch" | "double" | "wavy" | undefined; color?: string | undefined; draw_duration_ms?: number | undefined; }>, z.ZodObject<{ type: z.ZodLiteral<"highlight">; target_block: z.ZodString; color: z.ZodDefault; draw_duration_ms: z.ZodDefault; }, "strip", z.ZodTypeAny, { color: string; type: "highlight"; target_block: string; draw_duration_ms: number; }, { type: "highlight"; target_block: string; color?: string | undefined; draw_duration_ms?: number | undefined; }>, z.ZodObject<{ type: z.ZodLiteral<"pulse">; target_block: z.ZodString; count: z.ZodDefault; intensity: z.ZodDefault>; }, "strip", z.ZodTypeAny, { type: "pulse"; intensity: "subtle" | "normal" | "strong"; target_block: string; count: number; }, { type: "pulse"; target_block: string; intensity?: "subtle" | "normal" | "strong" | undefined; count?: number | undefined; }>, z.ZodObject<{ type: z.ZodLiteral<"callout">; from_block: z.ZodString; to_block: z.ZodString; label: z.ZodOptional; curve: z.ZodDefault>; }, "strip", z.ZodTypeAny, { type: "callout"; curve: "straight" | "curved" | "zigzag"; from_block: string; to_block: string; label?: string | undefined; }, { type: "callout"; from_block: string; to_block: string; label?: string | undefined; curve?: "straight" | "curved" | "zigzag" | undefined; }>, z.ZodObject<{ type: z.ZodLiteral<"ghost_reference">; target_page: z.ZodNumber; target_block: z.ZodString; position: z.ZodDefault>; }, "strip", z.ZodTypeAny, { type: "ghost_reference"; position: "top-right" | "top-left" | "bottom-right" | "bottom-left"; target_block: string; target_page: number; }, { type: "ghost_reference"; target_block: string; target_page: number; position?: "top-right" | "top-left" | "bottom-right" | "bottom-left" | undefined; }>, z.ZodObject<{ type: z.ZodLiteral<"box">; target_block: z.ZodString; color: z.ZodDefault; style: z.ZodDefault>; }, "strip", z.ZodTypeAny, { style: "solid" | "dashed"; color: string; type: "box"; target_block: string; }, { type: "box"; target_block: string; style?: "solid" | "dashed" | undefined; color?: string | undefined; }>, z.ZodObject<{ type: z.ZodLiteral<"label">; target_block: z.ZodString; text: z.ZodString; position: z.ZodDefault>; }, "strip", z.ZodTypeAny, { text: string; type: "label"; position: "top" | "bottom" | "left" | "right"; target_block: string; }, { text: string; type: "label"; target_block: string; position?: "top" | "bottom" | "left" | "right" | undefined; }>, z.ZodObject<{ type: z.ZodLiteral<"clear">; targets: z.ZodDefault, z.ZodArray]>>; }, "strip", z.ZodTypeAny, { type: "clear"; targets: "all" | "spotlights" | "overlays" | string[]; }, { type: "clear"; targets?: "all" | "spotlights" | "overlays" | string[] | undefined; }>]>; }, "strip", z.ZodTypeAny, { action: { type: "camera"; scale: number; easing: "linear" | "ease-in" | "ease-out" | "ease-in-out"; padding: number; target_block?: string | undefined; target_bbox?: [number, number, number, number] | undefined; } | { type: "spotlight"; shape: "rect" | "rounded" | "ellipse"; target_block: string; dim_opacity: number; feather_px: number; } | { style: "straight" | "sketch" | "double" | "wavy"; color: string; type: "underline"; target_block: string; draw_duration_ms: number; } | { color: string; type: "highlight"; target_block: string; draw_duration_ms: number; } | { type: "pulse"; intensity: "subtle" | "normal" | "strong"; target_block: string; count: number; } | { type: "callout"; curve: "straight" | "curved" | "zigzag"; from_block: string; to_block: string; label?: string | undefined; } | { type: "ghost_reference"; position: "top-right" | "top-left" | "bottom-right" | "bottom-left"; target_block: string; target_page: number; } | { style: "solid" | "dashed"; color: string; type: "box"; target_block: string; } | { text: string; type: "label"; position: "top" | "bottom" | "left" | "right"; target_block: string; } | { type: "clear"; targets: "all" | "spotlights" | "overlays" | string[]; }; at_ms: number; duration_ms: number; }, { action: { type: "camera"; scale?: number | undefined; easing?: "linear" | "ease-in" | "ease-out" | "ease-in-out" | undefined; padding?: number | undefined; target_block?: string | undefined; target_bbox?: [number, number, number, number] | undefined; } | { type: "spotlight"; target_block: string; shape?: "rect" | "rounded" | "ellipse" | undefined; dim_opacity?: number | undefined; feather_px?: number | undefined; } | { type: "underline"; target_block: string; style?: "straight" | "sketch" | "double" | "wavy" | undefined; color?: string | undefined; draw_duration_ms?: number | undefined; } | { type: "highlight"; target_block: string; color?: string | undefined; draw_duration_ms?: number | undefined; } | { type: "pulse"; target_block: string; intensity?: "subtle" | "normal" | "strong" | undefined; count?: number | undefined; } | { type: "callout"; from_block: string; to_block: string; label?: string | undefined; curve?: "straight" | "curved" | "zigzag" | undefined; } | { type: "ghost_reference"; target_block: string; target_page: number; position?: "top-right" | "top-left" | "bottom-right" | "bottom-left" | undefined; } | { type: "box"; target_block: string; style?: "solid" | "dashed" | undefined; color?: string | undefined; } | { text: string; type: "label"; target_block: string; position?: "top" | "bottom" | "left" | "right" | undefined; } | { type: "clear"; targets?: "all" | "spotlights" | "overlays" | string[] | undefined; }; at_ms?: number | undefined; duration_ms?: number | undefined; }>, "many">; }, "strip", z.ZodTypeAny, { version: 1; reasoning: string; steps: { action: { type: "camera"; scale: number; easing: "linear" | "ease-in" | "ease-out" | "ease-in-out"; padding: number; target_block?: string | undefined; target_bbox?: [number, number, number, number] | undefined; } | { type: "spotlight"; shape: "rect" | "rounded" | "ellipse"; target_block: string; dim_opacity: number; feather_px: number; } | { style: "straight" | "sketch" | "double" | "wavy"; color: string; type: "underline"; target_block: string; draw_duration_ms: number; } | { color: string; type: "highlight"; target_block: string; draw_duration_ms: number; } | { type: "pulse"; intensity: "subtle" | "normal" | "strong"; target_block: string; count: number; } | { type: "callout"; curve: "straight" | "curved" | "zigzag"; from_block: string; to_block: string; label?: string | undefined; } | { type: "ghost_reference"; position: "top-right" | "top-left" | "bottom-right" | "bottom-left"; target_block: string; target_page: number; } | { style: "solid" | "dashed"; color: string; type: "box"; target_block: string; } | { text: string; type: "label"; position: "top" | "bottom" | "left" | "right"; target_block: string; } | { type: "clear"; targets: "all" | "spotlights" | "overlays" | string[]; }; at_ms: number; duration_ms: number; }[]; }, { version: 1; steps: { action: { type: "camera"; scale?: number | undefined; easing?: "linear" | "ease-in" | "ease-out" | "ease-in-out" | undefined; padding?: number | undefined; target_block?: string | undefined; target_bbox?: [number, number, number, number] | undefined; } | { type: "spotlight"; target_block: string; shape?: "rect" | "rounded" | "ellipse" | undefined; dim_opacity?: number | undefined; feather_px?: number | undefined; } | { type: "underline"; target_block: string; style?: "straight" | "sketch" | "double" | "wavy" | undefined; color?: string | undefined; draw_duration_ms?: number | undefined; } | { type: "highlight"; target_block: string; color?: string | undefined; draw_duration_ms?: number | undefined; } | { type: "pulse"; target_block: string; intensity?: "subtle" | "normal" | "strong" | undefined; count?: number | undefined; } | { type: "callout"; from_block: string; to_block: string; label?: string | undefined; curve?: "straight" | "curved" | "zigzag" | undefined; } | { type: "ghost_reference"; target_block: string; target_page: number; position?: "top-right" | "top-left" | "bottom-right" | "bottom-left" | undefined; } | { type: "box"; target_block: string; style?: "solid" | "dashed" | undefined; color?: string | undefined; } | { text: string; type: "label"; target_block: string; position?: "top" | "bottom" | "left" | "right" | undefined; } | { type: "clear"; targets?: "all" | "spotlights" | "overlays" | string[] | undefined; }; at_ms?: number | undefined; duration_ms?: number | undefined; }[]; reasoning?: string | undefined; }>; type StoryboardParsed = z.infer; /** Converts the zod schema to JSON Schema for use with OpenAI structured outputs. * * OpenAI's structured outputs validator requires: * - Every property has a `type` (no bare `const`/`enum` without `type`) * - All properties listed in `required` * - `additionalProperties: false` on every object * * We model the action union as a single object with all possible fields optional * at the schema level (validated strictly later by zod). This keeps the schema * compatible across providers; field-level required-ness is enforced post-parse. */ interface StoryboardJsonSchemaOptions { /** Block IDs valid for the CURRENT page. Constrains target_block / from_block / to_block. */ validBlockIds?: string[]; /** Block IDs valid for cross-page references (e.g., figures on other pages). */ validCrossPageBlockIds?: string[]; } declare function storyboardJsonSchema(opts?: StoryboardJsonSchemaOptions): Record; /** * Lazily load a local MiniLM model (only on first call). The dynamic import * keeps `@xenova/transformers` out of the main bundle unless used. */ declare function getLocalMiniLM(): Promise; type ViewerStore = ViewerState & ViewerActions; declare function createViewerStore(initialOverrides?: Partial): zustand.StoreApi; type ViewerStoreApi = ReturnType; interface SearchActions { setQuery: (query: string) => void; search: (document: PDFDocumentProxy) => Promise; clearSearch: () => void; nextResult: () => void; previousResult: () => void; goToResult: (index: number) => void; setCaseSensitive: (value: boolean) => void; setWholeWord: (value: boolean) => void; toggleCaseSensitive: () => void; toggleWholeWord: () => void; getCurrentResult: () => SearchResult | null; } type SearchStore = SearchState & SearchActions; declare function createSearchStore(initialOverrides?: Partial): zustand.StoreApi; type SearchStoreApi = ReturnType; type AgentStore = AgentState & AgentActions; declare function createAgentStore(initialOverrides?: Partial): zustand.StoreApi; type AgentStoreApi = ReturnType; type StudentStore = StudentState & StudentActions; declare function createStudentStore(initialOverrides?: Partial): zustand.StoreApi; type StudentStoreApi = ReturnType; interface PDFViewerContextValue { viewerStore: ViewerStoreApi; annotationStore: AnnotationStoreApi; searchStore: SearchStoreApi; agentStore: AgentStoreApi; studentStore: StudentStoreApi; } interface PDFViewerProviderProps { children: ReactNode; initialState?: { viewer?: Partial; annotation?: Partial; search?: Partial; agent?: Partial; student?: Partial; }; theme?: Theme; defaultSidebarPanel?: SidebarPanel; /** Enable student learning mode features */ studentMode?: boolean; } declare const PDFViewerContext: react.Context; declare function PDFViewerProvider({ children, initialState, theme, defaultSidebarPanel, studentMode: _studentMode, }: PDFViewerProviderProps): react_jsx_runtime.JSX.Element; /** * Hook to access the viewer store. * Optionally pass a selector to subscribe to specific state. */ declare function useViewerStore(selector: (state: ViewerStore) => T): T; /** * Hook to access the annotation store. * Optionally pass a selector to subscribe to specific state. */ declare function useAnnotationStore(selector: (state: AnnotationStore) => T): T; /** * Hook to access the search store. * Optionally pass a selector to subscribe to specific state. */ declare function useSearchStore(selector: (state: SearchStore) => T): T; /** * Hook to access the agent store. * Optionally pass a selector to subscribe to specific state. */ declare function useAgentStore(selector: (state: AgentStore) => T): T; /** * Hook to access the student store. * Optionally pass a selector to subscribe to specific state. */ declare function useStudentStore(selector: (state: StudentStore) => T): T; /** * Hook to access all stores directly (for actions). */ declare function usePDFViewerStores(): PDFViewerContextValue; type AddHighlightParams = Omit; /** * Main unified hook for accessing PDF viewer state and actions. * This is the primary API for controlling the viewer. */ declare function usePDFViewer(): { document: pdfjs_dist_types_src_display_api.PDFDocumentProxy | null; numPages: number; isLoading: boolean; error: Error | null; currentPage: number; goToPage: (page: number) => void; nextPage: () => void; previousPage: () => void; scale: number; setScale: (newScale: number) => void; zoomIn: () => void; zoomOut: () => void; fitToWidth: () => void; fitToPage: () => void; rotation: number; rotateClockwise: () => void; rotateCounterClockwise: () => void; theme: Theme; setTheme: (newTheme: Theme) => void; viewMode: ViewMode; setViewMode: (mode: ViewMode) => void; sidebarOpen: boolean; toggleSidebar: () => void; sidebarPanel: SidebarPanel; setSidebarPanel: (panel: SidebarPanel) => void; isFullscreen: boolean; setFullscreen: (fullscreen: boolean) => void; highlights: Highlight[]; selectedHighlightId: string | null; activeHighlightColor: HighlightColor; isHighlightMode: boolean; addHighlight: (highlight: AddHighlightParams) => Highlight; removeHighlight: (id: string) => void; setActiveHighlightColor: (color: HighlightColor) => void; setHighlightMode: (enabled: boolean) => void; annotations: Annotation[]; searchQuery: string; searchResults: SearchResult[]; currentSearchResult: number; isSearching: boolean; search: (query: string) => Promise; clearSearch: () => void; nextSearchResult: () => void; previousSearchResult: () => void; agentContext: AgentContext | null; setAgentContext: (context: Partial) => void; clearAgentContext: () => void; focusedRegions: FocusedRegion[]; focusRegion: (region: Omit) => string; clearFocusedRegion: (id: string) => void; clearAllFocusedRegions: () => void; getAgentAPI: () => AgentAPI; bookmarks: Bookmark[]; addBookmark: (data: Omit) => Bookmark; updateBookmark: (id: string, updates: Partial>) => void; removeBookmark: (id: string) => void; quickNotes: QuickNote[]; addQuickNote: (data: Omit) => QuickNote; updateQuickNote: (id: string, updates: Partial>) => void; removeQuickNote: (id: string) => void; takeaways: Takeaway[]; addTakeaway: (data: Omit) => Takeaway; removeTakeaway: (id: string) => void; visitedPages: Set; progress: number; markPageVisited: (pageNumber: number) => void; }; interface UsePageNavigationOptions { enableKeyboardNavigation?: boolean; } /** * Hook for page navigation functionality. */ declare function usePageNavigation(options?: UsePageNavigationOptions): { currentPage: number; numPages: number; goToPage: (page: number) => void; nextPage: () => void; previousPage: () => void; goToFirstPage: () => void; goToLastPage: () => void; canGoNext: boolean; canGoPrevious: boolean; }; interface UseZoomOptions { enableWheelZoom?: boolean; minScale?: number; maxScale?: number; } /** * Hook for zoom functionality. */ declare function useZoom(options?: UseZoomOptions): { scale: number; setScale: (newScale: number) => void; zoomIn: () => void; zoomOut: () => void; fitToWidth: () => void; fitToPage: () => void; resetZoom: () => void; setContainerRef: (el: HTMLElement | null) => void; scalePercentage: number; }; interface UseTextSelectionOptions { onSelect?: (selection: TextSelection) => void; onCopy?: (text: string) => void; } /** * Hook for handling text selection in the PDF viewer. */ declare function useTextSelection(options?: UseTextSelectionOptions): { selection: TextSelection | null; isSelecting: boolean; clearSelection: () => void; copySelection: () => void; hasSelection: boolean; }; interface UseHighlightsOptions { /** Callback when a highlight is created */ onHighlightCreate?: (highlight: Highlight) => void; /** Callback when a highlight is updated */ onHighlightUpdate?: (highlight: Highlight) => void; /** Callback when a highlight is deleted */ onHighlightDelete?: (id: string) => void; } interface UseHighlightsReturn { /** Create a highlight from the current text selection */ createHighlightFromSelection: (selection: TextSelection, pageElement: HTMLElement, scale: number, color?: HighlightColor) => Highlight | null; /** * Add a highlight programmatically with coordinates. * Coordinates should be in PDF points (not scaled). * * @example * ```tsx * addHighlight({ * pageNumber: 1, * rects: [{ x: 100, y: 200, width: 150, height: 20 }], * text: "Highlighted text", * color: "yellow" * }); * ``` */ addHighlight: (highlight: { pageNumber: number; rects: HighlightRect[]; text: string; color?: HighlightColor; comment?: string; }) => Highlight; /** Update an existing highlight */ updateHighlight: (id: string, updates: Partial>) => void; /** Delete a highlight */ deleteHighlight: (id: string) => void; /** Get highlights for a specific page */ highlightsForPage: (pageNumber: number) => Highlight[]; /** Get all highlights */ allHighlights: Highlight[]; /** Get the currently selected highlight */ selectedHighlight: Highlight | null; /** Select a highlight by ID */ selectHighlight: (id: string | null) => void; /** Get the active highlight color */ activeColor: HighlightColor; /** Set the active highlight color */ setActiveColor: (color: HighlightColor) => void; } /** * Hook for creating and managing highlights from text selections. */ declare function useHighlights(options?: UseHighlightsOptions): UseHighlightsReturn; interface UseAnnotationsOptions { onAnnotationCreate?: (annotation: Annotation) => void; onAnnotationUpdate?: (annotation: Annotation) => void; onAnnotationDelete?: (id: string) => void; } interface UseAnnotationsReturn { annotations: Annotation[]; selectedAnnotation: Annotation | undefined; selectedAnnotationId: string | null; activeTool: AnnotationTool; activeShapeType: ShapeType; drawingColor: string; drawingStrokeWidth: number; isDrawing: boolean; currentDrawingPath: { points: { x: number; y: number; }[]; } | null; currentDrawingPage: number | null; setActiveTool: (tool: AnnotationTool) => void; setActiveShapeType: (shapeType: ShapeType) => void; setDrawingColor: (color: string) => void; setDrawingStrokeWidth: (width: number) => void; createNote: (pageNumber: number, x: number, y: number, content?: string, color?: string) => NoteAnnotation; updateNote: (id: string, updates: Partial) => void; startDrawing: (pageNumber: number, point: { x: number; y: number; }) => void; continueDrawing: (point: { x: number; y: number; }) => void; finishDrawing: () => Annotation | null; cancelDrawing: () => void; /** * Create a shape annotation programmatically. * Coordinates should be in PDF points (not scaled). * * @example * ```tsx * // Create a red rectangle box annotation * createShape({ * pageNumber: 1, * shapeType: 'rect', * x: 100, * y: 200, * width: 150, * height: 80, * color: '#ef4444', * strokeWidth: 2 * }); * ``` */ createShape: (options: { pageNumber: number; shapeType: ShapeType; x: number; y: number; width: number; height: number; color?: string; strokeWidth?: number; }) => ShapeAnnotation; updateShape: (id: string, updates: Partial) => void; selectAnnotation: (id: string | null) => void; deleteAnnotation: (id: string) => void; getAnnotationsByPage: (pageNumber: number) => Annotation[]; exportAnnotations: () => string; importAnnotations: (json: string) => void; } declare function useAnnotations(options?: UseAnnotationsOptions): UseAnnotationsReturn; interface UseTouchGesturesOptions { /** Callback when pinch-to-zoom is detected */ onPinchZoom?: (scale: number, center: { x: number; y: number; }) => void; /** Callback when swipe left is detected */ onSwipeLeft?: () => void; /** Callback when swipe right is detected */ onSwipeRight?: () => void; /** Callback when long press is detected */ onLongPress?: (position: { x: number; y: number; }) => void; /** Callback when double tap is detected */ onDoubleTap?: (position: { x: number; y: number; }) => void; /** Minimum swipe distance in pixels */ swipeThreshold?: number; /** Long press duration in milliseconds */ longPressDuration?: number; /** Double tap max interval in milliseconds */ doubleTapInterval?: number; /** Whether gestures are enabled */ enabled?: boolean; } declare function useTouchGestures(options?: UseTouchGesturesOptions): { ref: (element: T | null) => void; elementRef: react.MutableRefObject; }; declare function useIsTouchDevice(): boolean; declare function useIsMobile(): boolean; interface PluginManagerOptions { /** Callback when a plugin is registered */ onPluginRegistered?: (plugin: Plugin) => void; /** Callback when a plugin is unregistered */ onPluginUnregistered?: (name: string) => void; /** Callback when toolbar items change */ onToolbarItemsChanged?: (items: ToolbarItem[]) => void; /** Callback when sidebar panels change */ onSidebarPanelsChanged?: (panels: SidebarPanelConfig[]) => void; /** Callback when context menu items change */ onContextMenuItemsChanged?: (items: ContextMenuItem[]) => void; } declare class PluginManager { private plugins; private toolbarItems; private sidebarPanels; private contextMenuItems; private viewerStateGetter; private options; constructor(options?: PluginManagerOptions); /** * Set the getter function for viewer state */ setViewerStateGetter(getter: () => ViewerState & ViewerActions): void; /** * Create the plugin API for a specific plugin */ private createPluginAPI; /** * Register a plugin */ register(plugin: Plugin): Promise; /** * Unregister a plugin */ unregister(name: string): Promise; /** * Get a registered plugin by name */ getPlugin(name: string): Plugin | undefined; /** * Get all registered plugins */ getAllPlugins(): Plugin[]; /** * Check if a plugin is registered */ hasPlugin(name: string): boolean; /** * Get all toolbar items from all plugins */ getToolbarItems(): ToolbarItem[]; /** * Get toolbar items by position */ getToolbarItemsByPosition(position: 'left' | 'center' | 'right'): ToolbarItem[]; /** * Get all sidebar panels from all plugins */ getSidebarPanels(): SidebarPanelConfig[]; /** * Get all context menu items from all plugins */ getContextMenuItems(): ContextMenuItem[]; /** * Destroy all plugins and clean up */ destroy(): Promise; private notifyToolbarItemsChanged; private notifySidebarPanelsChanged; private notifyContextMenuItemsChanged; } declare function getPluginManager(): PluginManager; declare function createPluginManager(options?: PluginManagerOptions): PluginManager; interface UsePluginsOptions extends PluginManagerOptions { /** Initial plugins to register */ initialPlugins?: Plugin[]; } interface UsePluginsReturn { /** The plugin manager instance */ pluginManager: PluginManager; /** All registered plugins */ plugins: Plugin[]; /** Toolbar items from plugins */ toolbarItems: ToolbarItem[]; /** Sidebar panels from plugins */ sidebarPanels: SidebarPanelConfig[]; /** Context menu items from plugins */ contextMenuItems: ContextMenuItem[]; /** Register a plugin */ registerPlugin: (plugin: Plugin) => Promise; /** Unregister a plugin by name */ unregisterPlugin: (name: string) => Promise; /** Check if a plugin is registered */ hasPlugin: (name: string) => boolean; } declare function usePlugins(options?: UsePluginsOptions): UsePluginsReturn; interface UseAgentContextOptions { /** Default focus region style */ defaultFocusStyle?: 'pulse' | 'glow' | 'border'; /** Default focus region color */ defaultFocusColor?: string; /** Default auto-clear timeout for focus regions (ms) */ defaultAutoClearTimeout?: number; } interface UseAgentContextReturn { /** Current agent context */ agentContext: AgentContext | null; /** Set or update agent context */ setAgentContext: (context: Partial) => void; /** Clear agent context */ clearAgentContext: () => void; /** Currently focused regions */ focusedRegions: FocusedRegion[]; /** Add a focus region and return its ID */ focusRegion: (region: Omit) => string; /** Remove a focus region by ID */ clearFocusedRegion: (id: string) => void; /** Clear all focus regions */ clearAllFocusedRegions: () => void; /** Add a takeaway for a page */ addTakeaway: (pageNumber: number, summary: string, metadata?: Record) => Takeaway; /** Add a highlight from the agent */ addAgentHighlight: (params: AgentHighlightParams) => string; /** Get the agent API for external integration */ getAgentAPI: () => AgentAPI; } /** * Hook for agent context management and AI agent integration. * Provides methods for agents to interact with the PDF viewer. */ declare function useAgentContext(options?: UseAgentContextOptions): UseAgentContextReturn; interface UseAskAboutOptions { /** Whether the feature is enabled */ enabled?: boolean; /** Long press duration to trigger ask about (ms) */ longPressDuration?: number; /** Callback when ask about is triggered */ onAskAbout?: (context: AskAboutContext) => void; /** Minimum selection length to enable ask about for text */ minSelectionLength?: number; } interface UseAskAboutReturn { /** Whether ask about is currently in progress (long press active) */ isLongPressing: boolean; /** Progress of the long press (0-1) */ longPressProgress: number; /** Position of the long press */ longPressPosition: { x: number; y: number; } | null; /** Trigger ask about for selected text */ askAboutSelection: (text: string, pageNumber: number) => void; /** Trigger ask about for a region (diagram, image) */ askAboutRegion: (region: PDFRegion, pageNumber: number) => void; /** Start long press tracking (for custom implementations) */ startLongPress: (x: number, y: number) => void; /** Cancel long press */ cancelLongPress: () => void; /** Complete long press and trigger callback */ completeLongPress: (type: 'text' | 'region', pageNumber: number, text?: string, region?: PDFRegion) => void; /** Handlers for attaching to elements */ handlers: { onMouseDown: (e: React.MouseEvent) => void; onMouseUp: () => void; onMouseLeave: () => void; onTouchStart: (e: React.TouchEvent) => void; onTouchEnd: () => void; onTouchCancel: () => void; }; } /** * Hook for implementing the "Ask About This" feature. * Provides long-press detection and callbacks for asking about text or regions. */ declare function useAskAbout(options?: UseAskAboutOptions): UseAskAboutReturn; interface UseBookmarksOptions { /** Callback when bookmark is added */ onBookmarkAdd?: (bookmark: Bookmark) => void; /** Callback when bookmark is removed */ onBookmarkRemove?: (id: string) => void; /** Automatically capture agent context when bookmarking */ captureAgentContext?: boolean; } interface UseBookmarksReturn { /** All bookmarks */ bookmarks: Bookmark[]; /** Bookmarks for the current page */ currentPageBookmarks: Bookmark[]; /** Check if current page is bookmarked */ isCurrentPageBookmarked: boolean; /** Add a bookmark */ addBookmark: (data?: Partial>) => Bookmark; /** Update a bookmark */ updateBookmark: (id: string, updates: Partial>) => void; /** Remove a bookmark */ removeBookmark: (id: string) => void; /** Toggle bookmark on current page */ toggleBookmark: () => void; /** Navigate to a bookmarked page */ goToBookmark: (bookmark: Bookmark) => void; /** Get bookmarks for a specific page */ getBookmarksForPage: (pageNumber: number) => Bookmark[]; /** Get bookmarks grouped by page */ bookmarksByPage: Map; } /** * Hook for managing bookmarks with agent context capture. */ declare function useBookmarks(options?: UseBookmarksOptions): UseBookmarksReturn; interface UseQuickNotesOptions { /** Callback when note is added */ onNoteAdd?: (note: QuickNote) => void; /** Callback when note is removed */ onNoteRemove?: (id: string) => void; /** Automatically capture agent's last statement when creating notes */ captureAgentContext?: boolean; } interface UseQuickNotesReturn { /** All quick notes */ quickNotes: QuickNote[]; /** Quick notes for the current page */ currentPageNotes: QuickNote[]; /** Add a quick note */ addQuickNote: (data: { content: string; x: number; y: number; pageNumber?: number; }) => QuickNote; /** Update a quick note */ updateQuickNote: (id: string, updates: Partial>) => void; /** Remove a quick note */ removeQuickNote: (id: string) => void; /** Get notes for a specific page */ getNotesForPage: (pageNumber: number) => QuickNote[]; /** Notes grouped by page */ notesByPage: Map; } /** * Hook for managing quick notes with agent context capture. */ declare function useQuickNotes(options?: UseQuickNotesOptions): UseQuickNotesReturn; interface UseStudentProgressOptions { /** Automatically track page visits */ autoTrack?: boolean; /** Debounce time for page tracking (ms) */ trackDebounce?: number; } interface UseStudentProgressReturn { /** Set of visited page numbers */ visitedPages: Set; /** Array of visited page numbers (for easier iteration) */ visitedPagesArray: number[]; /** Current reading progress (0-1) */ progress: number; /** Percentage of pages visited */ visitedPercentage: number; /** Number of pages visited */ visitedCount: number; /** Total number of pages */ totalPages: number; /** Current page number */ currentPage: number; /** Mark a page as visited */ markPageVisited: (pageNumber: number) => void; /** Check if a page has been visited */ isPageVisited: (pageNumber: number) => boolean; /** Get page visit status for minimap rendering */ getPageStatus: (pageNumber: number) => 'current' | 'visited' | 'unvisited'; } /** * Hook for tracking student reading progress. */ declare function useStudentProgress(options?: UseStudentProgressOptions): UseStudentProgressReturn; /** * Utility for merging class names with support for conditionals. * Uses clsx under the hood. */ declare function cn(...inputs: ClassValue[]): string; interface PDFJSInitOptions { /** Custom worker source URL */ workerSrc?: string; /** Whether to use the fake worker (for testing) */ useFakeWorker?: boolean; } /** * Initialize pdf.js with the worker. * This should be called before loading any PDFs. */ declare function initializePDFJS(options?: PDFJSInitOptions): Promise; /** * Check if pdf.js is initialized */ declare function isPDFJSInitialized(): boolean; interface LoadDocumentOptions { /** URL or data of the PDF */ src: string | ArrayBuffer | Uint8Array; /** Custom worker source */ workerSrc?: string; /** Password for encrypted PDFs */ password?: string; /** Callback for loading progress */ onProgress?: (progress: { loaded: number; total: number; }) => void; /** Enable range requests for faster loading (default: true) */ enableRangeRequests?: boolean; /** Enable streaming for progressive display (default: true) */ enableStreaming?: boolean; /** Cache the document data (default: true) */ cacheDocument?: boolean; /** AbortSignal for cancellation */ signal?: AbortSignal; } interface LoadDocumentWithCallbacksOptions extends LoadDocumentOptions { /** Callback when document structure is ready (XRef parsed) */ onDocumentReady?: (document: PDFDocumentProxy, numPages: number) => void; /** Callback when the first page is loaded and ready to render */ onFirstPageReady?: (page: PDFPageProxy) => void; } interface LoadDocumentWithCallbacksResult { /** Promise that resolves when loading is complete */ promise: Promise; /** Function to cancel loading */ cancel: () => void; } interface LoadDocumentResult { document: PDFDocumentProxy; numPages: number; } /** * Load a PDF document from a URL or data buffer. * * Optimizations: * - Range requests allow loading only the parts needed (faster initial load) * - Streaming allows pages to render as data arrives * - Document caching prevents duplicate fetches * - Worker handles parsing off the main thread */ declare function loadDocument(options: LoadDocumentOptions): Promise; /** * Get a specific page from a PDF document. */ declare function getPage(document: PDFDocumentProxy, pageNumber: number): Promise; /** * Get text content from a PDF page. */ declare function getPageTextContent(page: PDFPageProxy): Promise; /** * Get the outline (table of contents) from a PDF document. */ declare function getOutline(document: PDFDocumentProxy): Promise<{ title: string; bold: boolean; italic: boolean; color: Uint8ClampedArray; dest: string | Array | null; url: string | null; unsafeUrl: string | undefined; newWindow: boolean | undefined; count: number | undefined; items: Array; }[]>; /** * Get metadata from a PDF document. */ declare function getMetadata(document: PDFDocumentProxy): Promise<{ info: Object; metadata: pdfjs_dist_types_src_display_metadata.Metadata; }>; /** * Load a PDF document with streaming callbacks for progressive loading. * * This function provides callbacks that fire at different stages of loading: * - onDocumentReady: Fires when the document structure (XRef) is parsed * - onFirstPageReady: Fires when the first page is loaded and ready to render * * This enables showing the PDF viewer UI immediately and rendering the first * page as soon as it's available, while the rest of the document loads in * the background. */ declare function loadDocumentWithCallbacks(options: LoadDocumentWithCallbacksOptions): LoadDocumentWithCallbacksResult; /** * Save highlights to localStorage for a specific document. * * @param documentId - Unique identifier for the document (e.g., URL hash, filename) * @param highlights - Array of highlights to save * @returns true if save succeeded, false otherwise */ declare function saveHighlights(documentId: string, highlights: Highlight[]): boolean; /** * Load highlights from localStorage for a specific document. * * @param documentId - Unique identifier for the document * @returns Array of highlights, empty array if none found or error */ declare function loadHighlights(documentId: string): Highlight[]; /** * Remove all highlights for a specific document from localStorage. * * @param documentId - Unique identifier for the document * @returns true if removal succeeded, false otherwise */ declare function clearHighlights(documentId: string): boolean; /** * Get all document IDs that have stored highlights. * * @returns Array of document IDs */ declare function getAllDocumentIds(): string[]; /** * Export highlights as a JSON string. * Includes metadata for import/export portability. * * @param highlights - Array of highlights to export * @returns JSON string representation */ declare function exportHighlightsAsJSON(highlights: Highlight[]): string; /** * Import highlights from a JSON string. * * @param json - JSON string to import * @returns Array of highlights, or null if invalid */ declare function importHighlightsFromJSON(json: string): Highlight[] | null; /** * Export highlights as Markdown format. * Groups by page and includes text, comments, and metadata. * * @param highlights - Array of highlights to export * @param documentTitle - Optional document title for the header * @returns Markdown string representation */ declare function exportHighlightsAsMarkdown(highlights: Highlight[], documentTitle?: string): string; /** * Generate a document ID from a URL or file path. * Creates a consistent hash for the same document. * * @param source - URL or file path * @returns Document ID string */ declare function generateDocumentId(source: string | ArrayBuffer | Uint8Array): string; interface PDFViewerControllerOptions { /** Initial page number */ initialPage?: number; /** Initial scale/zoom level */ initialScale?: number; /** Theme mode */ theme?: Theme; /** Whether to show the toolbar */ showToolbar?: boolean; /** Whether to show the sidebar */ showSidebar?: boolean; /** Custom onDocumentLoad callback */ onDocumentLoad?: (doc: PDFDocumentProxy) => void; /** Custom onPageChange callback */ onPageChange?: (page: number) => void; /** Custom onScaleChange callback */ onScaleChange?: (scale: number) => void; /** Custom onError callback */ onError?: (error: Error) => void; /** PDF source - URL, ArrayBuffer, or Uint8Array */ src?: string | ArrayBuffer | Uint8Array; } interface PDFViewerController { loadDocument(src: string | ArrayBuffer | Uint8Array): Promise; getDocument(): PDFDocumentProxy | null; goToPage(page: number): void; nextPage(): void; previousPage(): void; getCurrentPage(): number; getTotalPages(): number; setZoom(scale: number): void; zoomIn(): void; zoomOut(): void; fitToWidth(): void; fitToPage(): void; getZoom(): number; setRotation(rotation: number): void; rotateClockwise(): void; rotateCounterClockwise(): void; getRotation(): number; setTheme(theme: Theme): void; getTheme(): Theme; setViewMode(mode: ViewMode): void; getViewMode(): ViewMode; toggleSidebar(): void; setSidebarPanel(panel: SidebarPanel): void; isSidebarOpen(): boolean; getHighlights(): Highlight[]; addHighlight(highlight: Omit): Highlight; removeHighlight(id: string): void; exportHighlights(): string; importHighlights(json: string): void; getAnnotations(): Annotation[]; addAnnotation(annotation: Omit): Annotation; removeAnnotation(id: string): void; exportAnnotations(): string; importAnnotations(json: string): void; on(event: K, handler: PDFViewerEventHandlers[K]): () => void; destroy(): void; } interface PDFViewerEventHandlers { pagechange: (page: number) => void; scalechange: (scale: number) => void; documentload: (doc: PDFDocumentProxy) => void; error: (error: Error) => void; } /** * Create a PDF viewer instance for non-React usage */ declare function createPDFViewer(container: HTMLElement, options?: PDFViewerControllerOptions): PDFViewerController; /** * Initialize a PDF viewer with minimal configuration */ declare function quickViewer(container: HTMLElement | string, src: string | ArrayBuffer | Uint8Array, options?: Partial): Promise; interface ExportData { highlights?: Highlight[]; bookmarks?: Bookmark[]; quickNotes?: QuickNote[]; takeaways?: Takeaway[]; documentTitle?: string; } /** * Export all annotations as a comprehensive Markdown document. * Organizes content by page with highlights, notes, bookmarks, and takeaways. */ declare function exportAnnotationsAsMarkdown(data: ExportData): string; /** * Export all annotations as JSON. */ declare function exportAnnotationsAsJSON(data: ExportData): string; /** * Download content as a file. */ declare function downloadFile(content: string, filename: string, mimeType: string): void; /** * Export annotations as Markdown and trigger download. */ declare function downloadAnnotationsAsMarkdown(data: ExportData, filename?: string): void; /** * Export annotations as JSON and trigger download. */ declare function downloadAnnotationsAsJSON(data: ExportData, filename?: string): void; interface StoredBookmark extends Omit { timestamp: string; } interface StoredQuickNote extends Omit { timestamp: string; } interface StoredTakeaway extends Omit { timestamp: string; } interface StoredStudentData { bookmarks: StoredBookmark[]; quickNotes: StoredQuickNote[]; takeaways: StoredTakeaway[]; visitedPages: number[]; progress: number; lastAccessed: string; } interface StudentData { bookmarks: Bookmark[]; quickNotes: QuickNote[]; takeaways: Takeaway[]; visitedPages: Set; progress: number; lastAccessed: Date; } /** * Save student data to localStorage. */ declare function saveStudentData(documentId: string, data: StudentData): boolean; /** * Load student data from localStorage. */ declare function loadStudentData(documentId: string): StudentData | null; /** * Clear student data for a specific document. */ declare function clearStudentData(documentId: string): boolean; /** * Get all document IDs that have stored student data. */ declare function getAllStudentDataDocumentIds(): string[]; /** * Get storage usage statistics. */ declare function getStorageStats(): { usedBytes: number; itemCount: number; }; interface AgentAPIStores { agentStore: StoreApi; studentStore: StoreApi; viewerStore: StoreApi; annotationStore: StoreApi; } /** * Create a standalone Agent API for external AI agent integration. * This allows external agents to interact with the PDF viewer without * needing direct access to React hooks. * * @example * ```typescript * import { createAgentAPI } from 'pdfjs-reader-core'; * * // Get stores from the context (this would typically be done in a React component) * const api = createAgentAPI(stores); * * // Now the external agent can use the API * api.setAgentContext({ lastStatement: "The mitochondria is the powerhouse of the cell" }); * api.focusRegion({ pageNumber: 1, x: 100, y: 200, width: 300, height: 50, style: 'pulse' }); * api.addTakeaway(1, "Key concept: Cell energy production"); * ``` */ declare function createAgentAPI(stores: AgentAPIStores): AgentAPI; /** * Convenience type for the return value of createAgentAPI */ type AgentAPIInstance = ReturnType; interface TextMatch { /** The matched text */ text: string; /** Position rects on the page */ rects: HighlightRect[]; /** Page number where the match was found */ pageNumber: number; /** Character index where the match starts in the page text */ startIndex: number; } interface FindTextOptions { /** Case sensitive search (default: false) */ caseSensitive?: boolean; /** Match whole words only (default: false) */ wholeWord?: boolean; } interface CharPosition { char: string; rect: HighlightRect; } /** Internal text item representation */ interface TextItem { text: string; transform: number[]; width: number; height: number; } /** * Extract text content with text items from a PDF page. * Uses proper text item tracking for accurate positioning. */ declare function extractPageText(document: PDFDocumentProxy, pageNumber: number): Promise<{ fullText: string; textItems: TextItem[]; viewport: { width: number; height: number; }; }>; /** * Find all occurrences of text on a specific page. */ declare function findTextOnPage(document: PDFDocumentProxy, pageNumber: number, query: string, options?: FindTextOptions): Promise; /** * Find all occurrences of text across multiple pages. */ declare function findTextInDocument(document: PDFDocumentProxy, query: string, options?: FindTextOptions & { pageRange?: number[]; }): Promise; /** * Merge adjacent rects into larger rects. */ declare function mergeAdjacentRects(rects: HighlightRect[]): HighlightRect[]; /** * Get the full text content of a page as a string. */ declare function getPageText(document: PDFDocumentProxy, pageNumber: number): Promise; /** * Count occurrences of text on a page without extracting positions. */ declare function countTextOnPage(document: PDFDocumentProxy, pageNumber: number, query: string, options?: FindTextOptions): Promise; /** * Convert PDF coordinates to viewport (screen) coordinates. * PDF coordinates have origin at bottom-left, viewport at top-left. * * @param x - X coordinate in PDF space * @param y - Y coordinate in PDF space * @param scale - Current zoom scale * @param pageHeight - Height of the page in PDF units * @returns Viewport coordinates { x, y } */ declare function pdfToViewport(x: number, y: number, scale: number, pageHeight: number): { x: number; y: number; }; /** * Convert viewport (screen) coordinates to PDF coordinates. * Viewport coordinates have origin at top-left, PDF at bottom-left. * * @param x - X coordinate in viewport space * @param y - Y coordinate in viewport space * @param scale - Current zoom scale * @param pageHeight - Height of the page in PDF units * @returns PDF coordinates { x, y } */ declare function viewportToPDF(x: number, y: number, scale: number, pageHeight: number): { x: number; y: number; }; /** * Convert percentage-based coordinates to PDF coordinates. * Useful for positioning elements based on relative position. * * @param xPercent - X coordinate as percentage (0-100) * @param yPercent - Y coordinate as percentage (0-100) * @param pageWidth - Width of the page in PDF units * @param pageHeight - Height of the page in PDF units * @returns PDF coordinates { x, y } */ declare function percentToPDF(xPercent: number, yPercent: number, pageWidth: number, pageHeight: number): { x: number; y: number; }; /** * Convert PDF coordinates to percentage-based coordinates. * * @param x - X coordinate in PDF space * @param y - Y coordinate in PDF space * @param pageWidth - Width of the page in PDF units * @param pageHeight - Height of the page in PDF units * @returns Percentage coordinates { x, y } (0-100) */ declare function pdfToPercent(x: number, y: number, pageWidth: number, pageHeight: number): { x: number; y: number; }; /** * Convert percentage coordinates to viewport (screen) pixels. * * @param xPercent - X coordinate as percentage (0-100) * @param yPercent - Y coordinate as percentage (0-100) * @param pageWidth - Width of the page in PDF units * @param pageHeight - Height of the page in PDF units * @param scale - Current zoom scale * @returns Viewport coordinates { x, y } */ declare function percentToViewport(xPercent: number, yPercent: number, pageWidth: number, pageHeight: number, scale: number): { x: number; y: number; }; /** * Convert viewport (screen) pixels to percentage coordinates. * * @param x - X coordinate in viewport space * @param y - Y coordinate in viewport space * @param pageWidth - Width of the page in PDF units * @param pageHeight - Height of the page in PDF units * @param scale - Current zoom scale * @returns Percentage coordinates { x, y } (0-100) */ declare function viewportToPercent(x: number, y: number, pageWidth: number, pageHeight: number, scale: number): { x: number; y: number; }; /** * Apply rotation to coordinates. * Rotates point around the center of the page. * * @param x - X coordinate * @param y - Y coordinate * @param rotation - Rotation in degrees (0, 90, 180, 270) * @param pageWidth - Width of the page * @param pageHeight - Height of the page * @returns Rotated coordinates { x, y } */ declare function applyRotation(x: number, y: number, rotation: number, pageWidth: number, pageHeight: number): { x: number; y: number; }; /** * Remove rotation from coordinates. * Inverse of applyRotation. * * @param x - X coordinate * @param y - Y coordinate * @param rotation - Rotation in degrees (0, 90, 180, 270) * @param pageWidth - Width of the page (original, before rotation) * @param pageHeight - Height of the page (original, before rotation) * @returns Unrotated coordinates { x, y } */ declare function removeRotation(x: number, y: number, rotation: number, pageWidth: number, pageHeight: number): { x: number; y: number; }; /** * Calculate the bounding box dimensions after rotation. * * @param width - Original width * @param height - Original height * @param rotation - Rotation in degrees * @returns Rotated dimensions { width, height } */ declare function getRotatedDimensions(width: number, height: number, rotation: number): { width: number; height: number; }; /** * Convert a rectangle from one coordinate space to another. * * @param rect - Rectangle with x, y, width, height * @param fromScale - Source scale * @param toScale - Target scale * @returns Scaled rectangle */ declare function scaleRect(rect: { x: number; y: number; width: number; height: number; }, fromScale: number, toScale: number): { x: number; y: number; width: number; height: number; }; /** * Check if a point is inside a rectangle. * * @param point - Point coordinates { x, y } * @param rect - Rectangle { x, y, width, height } * @returns True if point is inside rectangle */ declare function isPointInRect(point: { x: number; y: number; }, rect: { x: number; y: number; width: number; height: number; }): boolean; /** * Check if two rectangles intersect. * * @param rectA - First rectangle * @param rectB - Second rectangle * @returns True if rectangles intersect */ declare function doRectsIntersect(rectA: { x: number; y: number; width: number; height: number; }, rectB: { x: number; y: number; width: number; height: number; }): boolean; /** * Get the intersection of two rectangles. * * @param rectA - First rectangle * @param rectB - Second rectangle * @returns Intersection rectangle or null if no intersection */ declare function getRectIntersection(rectA: { x: number; y: number; width: number; height: number; }, rectB: { x: number; y: number; width: number; height: number; }): { x: number; y: number; width: number; height: number; } | null; /** * Page turn sound effect using Web Audio API. * Synthesizes a realistic paper-turning sound without requiring external audio files. */ /** * Play a synthesized page-turn sound effect. * Uses filtered noise to simulate the sound of a paper page turning. */ declare function playPageTurnSound(volume?: number): void; export { type ActionBox, type ActionCallout, type ActionCamera, type ActionClear, type ActionGhostReference, type ActionHighlight, type ActionLabel, type ActionPulse, type ActionSpotlight, type ActionUnderline, type ActiveOverlay, type AddNoteOptions, type AgentAPI, type AgentAPIInstance, type AgentAPIStores, type AgentActions, type AgentContext, type AgentHighlightParams, type AgentState, type AgentStore, type AgentStoreApi, type AgentToolResult, type AgentTools, AnimatedHighlight, type AnimatedHighlightProps, AnimatedUnderline, type AnimatedUnderlineProps, type Annotation, type AnnotationActions, AnnotationLayer, type AnnotationLayerProps, type AnnotationState, type AnnotationStore, type AnnotationStoreApi, type AnnotationTool, AnnotationToolbar, type AnnotationToolbarProps, type AnnotationType, type ArrowCurve, type AskAboutContext, AskAboutOverlay, type AskAboutOverlayProps, AskAboutTrigger, type AskAboutTriggerProps, type BBoxCoords, type BBoxIndex, type Block, type BlockMatch, type BlockType, BookModeContainer, type BookModeContainerProps, type Bookmark, BookmarksPanel, type BookmarksPanelProps, BoxOverlay, type BoxOverlayProps, type BoxStyle, type BuildUserPromptInput, CalloutArrow, type CalloutArrowProps, type CameraState, CameraView, type CameraViewProps, CanvasLayer, type CanvasLayerProps, type CharPosition, type ChunkHistoryEntry, CinemaLayer, type CinemaLayerProps, type ClearTarget, type ContextMenuItem, ContinuousScrollContainer, type ContinuousScrollContainerProps, type CoordinateHelpers, type DebugEvent, type DefaultAction, type DirectorInput, type DirectorResult, DocumentContainer, type DocumentContainerProps, type DocumentLoadingState, type DrawCircleOptions, type DrawRectOptions, type DrawingAnnotation, DrawingCanvas, type DrawingCanvasProps, type DrawingPath, DualPageContainer, type DualPageContainerProps, type EasingName, type EmbeddingProvider, type EngineStatus, type ExportData, type FindTextOptions, FloatingZoomControls, type FloatingZoomControlsProps, FocusRegionLayer, type FocusRegionLayerProps, type FocusedRegion, type GhostPosition, GhostReference, type GhostReferenceProps, type GoToPageOptions, type Highlight, type HighlightColor, HighlightLayer, type HighlightLayerProps, HighlightPopover, type HighlightPopoverProps, type HighlightRect, type HighlightTextOptions, HighlightsPanel, type HighlightsPanelProps, type LabelPosition, type LlmConfig, type LlmStatus, type LoadDocumentOptions, type LoadDocumentResult, type LoadDocumentWithCallbacksOptions, type LoadDocumentWithCallbacksResult, type LoadingPhase, type LoadingProgress, Minimap, type MinimapProps, MobileSidebar, type MobileSidebarProps, MobileToolbar, type MobileToolbarProps, type NarrationActions, type NarrationState, type NarrationStore, type NarrationStoreApi, type NoteAnnotation, type OutlineItem, OutlinePanel, type OutlinePanelProps, type PDFDocumentLoadedEvent, PDFErrorBoundary, type PDFErrorBoundaryProps, PDFLoadingScreen, type PDFLoadingScreenProps, PDFPage, type PDFPageProps, type PDFPageState, type PDFRegion, PDFThumbnailNav, type PDFThumbnailNavProps, PDFViewer, PDFViewerClient, PDFViewerContext, type PDFViewerContextValue, type PDFViewerController, type PDFViewerControllerOptions, type PDFViewerEventMap, type PDFViewerHandle, type PDFViewerProps, PDFViewerProvider, type PDFViewerProviderProps, type PageBBoxData, type PageCoordinates, type PageDimensions, type PageDimensionsDpi, type PageDimensionsInfo, type Plugin, type PluginAPI, PluginManager, type PluginManagerOptions, type PulseIntensity, PulseOverlay, type PulseOverlayProps, type QuickNote, QuickNoteButton, type QuickNoteButtonProps, QuickNotePopover, type QuickNotePopoverProps, SYSTEM_PROMPT, type ScrollMode, type ScrollToPageRequest, type SearchActions, type SearchAndHighlightOptions, type SearchAndHighlightResult, type SearchOptions, SearchPanel, type SearchPanelProps, type SearchResult, type SearchState, type SearchStore, type SearchStoreApi, SelectionToolbar, type SelectionToolbarProps, type ShapeAnnotation, ShapePreview, type ShapePreviewProps, ShapeRenderer, type ShapeRendererProps, type ShapeType, Sidebar, type SidebarPanel, type SidebarPanelConfig, type SidebarProps, SpotlightMask, type SpotlightMaskProps, type SpotlightShape, StickyLabel, type StickyLabelProps, StickyNote, type StickyNoteProps, type StoredStudentData, type Storyboard, type StoryboardAction, StoryboardActionSchema, StoryboardEngine, type StoryboardParsed, type StoryboardProviderInput, StoryboardSchema, type StoryboardStep, type StreamingProgress, type StudentActions, type StudentData, type StudentModeCallbacks, type StudentModeProps, type StudentState, type StudentStore, type StudentStoreApi, SubtitleBar, type SubtitleBarProps, type Takeaway, TakeawaysPanel, type TakeawaysPanelProps, TextLayer, type TextLayerProps, type TextMatch, type TextSelection, type Theme, ThumbnailPanel, type ThumbnailPanelProps, Toolbar, type ToolbarItem, type ToolbarProps, TutorModeContainer, type TutorModeContainerProps, type UnderlineStyle, type UseAgentContextOptions, type UseAgentContextReturn, type UseAnnotationsOptions, type UseAnnotationsReturn, type UseAskAboutOptions, type UseAskAboutReturn, type UseBookmarksOptions, type UseBookmarksReturn, type UseHighlightsOptions, type UseHighlightsReturn, type UsePageNavigationOptions, type UsePluginsOptions, type UsePluginsReturn, type UseQuickNotesOptions, type UseQuickNotesReturn, type UseStudentProgressOptions, type UseStudentProgressReturn, type UseTextSelectionOptions, type UseTouchGesturesOptions, type UseZoomOptions, type ViewMode, type ViewerActions, type ViewerState, type ViewerStore, type ViewerStoreApi, VirtualizedDocumentContainer, type VirtualizedDocumentContainerProps, type WithErrorBoundaryProps, applyRotation, buildBBoxIndex, buildUserPrompt, clearHighlights, clearStudentData, cn, cosineSimilarity, countTextOnPage, createAgentAPI, createAgentStore, createAnnotationStore, createNarrationStore, createPDFViewer, createPluginManager, createSearchStore, createStudentStore, createViewerStore, directStoryboard, doRectsIntersect, downloadAnnotationsAsJSON, downloadAnnotationsAsMarkdown, downloadFile, exportAnnotationsAsJSON, exportAnnotationsAsMarkdown, exportHighlightsAsJSON, exportHighlightsAsMarkdown, extractPageText, findTextInDocument, findTextOnPage, generateDocumentId, getAllDocumentIds, getAllStudentDataDocumentIds, getLocalMiniLM, getMetadata, getOutline, getPage, getPageText, getPageTextContent, getPluginManager, getRectIntersection, getRotatedDimensions, getStorageStats, importHighlightsFromJSON, initializePDFJS, isPDFJSInitialized, isPointInRect, loadDocument, loadDocumentWithCallbacks, loadHighlights, loadStudentData, makeOverlayId, matchChunkToBlock, mergeAdjacentRects, pdfToPercent, pdfToViewport, percentToPDF, percentToViewport, playPageTurnSound, quickViewer, removeRotation, saveHighlights, saveStudentData, scaleRect, storyboardFromMatch, storyboardJsonSchema, truncate, useAgentContext, useAgentStore, useAnnotationStore, useAnnotations, useAskAbout, useBookmarks, useHighlights, useIsMobile, useIsTouchDevice, usePDFViewer, usePDFViewerStores, usePageNavigation, usePlugins, useQuickNotes, useSearchStore, useStudentProgress, useStudentStore, useTextSelection, useTouchGestures, useViewerStore, useZoom, viewportToPDF, viewportToPercent, withErrorBoundary };