teamsync-pdf-viewer 1.2.1 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,21 @@
2
2
 
3
3
  All notable changes to `teamsync-pdf-viewer` are documented here.
4
4
 
5
+ ## [1.3.1] — 2026-09-04
6
+
7
+ ### Fixed
8
+ - **Settings & Keyboard Shortcuts Customization**: Fixed settings search bar filtering across all shortcuts and general settings. Resolved shortcut recording and key normalization for US keyboard layouts (such as `Meta+Shift+=` and `Meta+Shift+-`), enabled `Escape` cancellation, and added a "Reset to Defaults" option.
9
+ - **Cross-Component Shortcut Synchronization**: Replaced local hook state with a shared reactive pub/sub store backed by `window.localStorage`, ensuring shortcut customizations sync immediately across all active viewer instances.
10
+ - **Viewer-Side Shortcut Execution**: Enabled full shortcut action handling in the viewer, adding support for `SEARCH` (`Meta+F`), `FILE_PICKER` (`Meta+O`), `COPY` (`Meta+C`), and `PASTE` (`Meta+V`) alongside existing rotation and undo/redo shortcuts. Added an "Open PDF..." action to the header menu and window-level shortcut handling.
11
+ - **Sidebar Search Bar**: Enabled immediate search on `Enter` keypress, immediate query and highlight clearing, auto-focus when opening the search tab, and corrected box-sizing to eliminate container overflow.
12
+
13
+ ## [1.3.0] — 2026-09-04
14
+
15
+ ### Added
16
+ - **Programmatic & Reactive Page Navigation**: Added `instance.goToPage(pageNumber, options?)` and `instance.setCurrentPage(pageNumber)` (also available via `instance.UI.setCurrentPageNumber` and `instance.Core.documentViewer.goToPage`). Added reactive navigation when `page` or `initialPage` props update on `<TeamSyncViewer>` or `<DocumentViewer>` while the viewer is already open, enabling citation jumping without reopening documents.
17
+ - **Exported Text Search Utilities**: Extracted standalone asynchronous search function `searchPdfText(pdfDoc, query, redactions?, onProgress?)` and exported `usePdfSearch`, `searchPdfText`, `SearchResult`, and `SearchBounds` from the library root. Added `instance.searchText(query)` to execute document searches programmatically.
18
+ - **Transient Citation Highlights Layer**: Added dedicated transient highlights system (`TransientHighlight`, `instance.setTransientHighlights`, `instance.getTransientHighlights`, `instance.addTransientHighlight`, `instance.clearTransientHighlights`, and `instance.highlightSnippet`). Highlights render on an isolated overlay layer with pulse animations and tooltips without polluting `AnnotationManager`, `getAnnotations()`, or XFDF exports.
19
+
5
20
  ## [1.2.1] — 2026-09-01
6
21
 
7
22
  ### Fixed
@@ -6,7 +6,7 @@ interface DiffSummarySidebarProps {
6
6
  diffItems: DiffItem[];
7
7
  textDiffs: TextDiffSegment[];
8
8
  currentDiffIndex: number;
9
- onSelectDiff: (index: number) => void;
9
+ onSelectDiff: (index: number, item?: DiffItem) => void;
10
10
  onJumpToPage: (pageIndex: number) => void;
11
11
  }
12
12
  export default function DiffSummarySidebar({ isOpen, diffItems, textDiffs, currentDiffIndex, onSelectDiff, onJumpToPage }: DiffSummarySidebarProps): React.JSX.Element | null;
@@ -1,7 +1,7 @@
1
1
  import { default as React } from 'react';
2
2
  import { Annotation } from '../annotations/types';
3
3
  import { AnnotationManager } from '../annotations/AnnotationManager';
4
- import { Redaction, WatermarkOptions, SDKPermissions, PdfAssetPaths } from '../core/types';
4
+ import { Redaction, WatermarkOptions, SDKPermissions, PdfAssetPaths, TransientHighlight } from '../core/types';
5
5
  import * as pdfjsLib from 'pdfjs-dist';
6
6
  export interface DocumentLoadErrorInfo {
7
7
  url: string;
@@ -44,7 +44,9 @@ interface DocumentViewerProps {
44
44
  watermarkText?: string;
45
45
  enableAnnotations?: boolean;
46
46
  initialPage?: number;
47
+ page?: number;
48
+ transientHighlights?: TransientHighlight[];
47
49
  permissions?: SDKPermissions;
48
50
  }
49
- export default function DocumentViewer({ leftSidebarOpen, rightSidebarOpen, activeTab, annotationManager, initialDoc, loadNonce, withCredentials, assets, sidebars, redactions, regexRedactions, scale, setScale, sidebarTab, setSidebarTab, onAnnotationsChange, onRedactionsChange, onDocumentLoaded, onLoadError, onFirstPageRendered, onPageChange, pageTransition, pageLayout, rotation, setRotation, watermark, watermarkText, enableAnnotations: _enableAnnotations, initialPage, permissions }: DocumentViewerProps): React.JSX.Element;
51
+ export default function DocumentViewer({ leftSidebarOpen, rightSidebarOpen, activeTab, annotationManager, initialDoc, loadNonce, withCredentials, assets, sidebars, redactions, regexRedactions, scale, setScale, sidebarTab, setSidebarTab, onAnnotationsChange, onRedactionsChange, onDocumentLoaded, onLoadError, onFirstPageRendered, onPageChange, pageTransition, pageLayout, rotation, setRotation, watermark, watermarkText, enableAnnotations: _enableAnnotations, initialPage, page, transientHighlights, permissions }: DocumentViewerProps): React.JSX.Element;
50
52
  export {};
@@ -13,6 +13,7 @@ interface HeaderProps {
13
13
  onZoomIn: () => void;
14
14
  onZoomOut: () => void;
15
15
  onZoomSet: (scale: number) => void;
16
+ onOpenFile?: () => void;
16
17
  onDownload: () => void;
17
18
  onFullScreen: () => void;
18
19
  onSaveAs: () => void;
@@ -32,5 +33,5 @@ interface HeaderProps {
32
33
  plugins?: ViewerPlugin[];
33
34
  permissions?: SDKPermissions;
34
35
  }
35
- export default function Header({ activeTab, setActiveTab, leftSidebarOpen, setLeftSidebarOpen, rightSidebarOpen, setRightSidebarOpen: _setRightSidebarOpen, sidebarTab, scale, onZoomIn, onZoomOut, onZoomSet, onDownload, onFullScreen, onSaveAs, onPrint, onOpenSettings, onOpenAbout, onOpenSidebarTab, pageTransition, setPageTransition, pageLayout, setPageLayout, rotation, setRotation, signatureCount, enableAnnotations, enableSign, plugins, permissions }: HeaderProps): React.JSX.Element;
36
+ export default function Header({ activeTab, setActiveTab, leftSidebarOpen, setLeftSidebarOpen, rightSidebarOpen, setRightSidebarOpen: _setRightSidebarOpen, sidebarTab, scale, onZoomIn, onZoomOut, onZoomSet, onOpenFile, onDownload, onFullScreen, onSaveAs, onPrint, onOpenSettings, onOpenAbout, onOpenSidebarTab, pageTransition, setPageTransition, pageLayout, setPageLayout, rotation, setRotation, signatureCount, enableAnnotations, enableSign, plugins, permissions }: HeaderProps): React.JSX.Element;
36
37
  export {};
@@ -1,6 +1,6 @@
1
1
  import { default as React } from 'react';
2
2
  import { SearchResult } from '../hooks/usePdfSearch';
3
- import { Redaction, WatermarkOptions } from '../core/types';
3
+ import { Redaction, WatermarkOptions, TransientHighlight } from '../core/types';
4
4
  import { Annotation } from '../annotations/types';
5
5
  import * as pdfjsLib from 'pdfjs-dist';
6
6
  export type { Annotation };
@@ -22,6 +22,7 @@ interface PageRendererProps {
22
22
  annotations: Annotation[];
23
23
  selectedAnnotationId: string | null;
24
24
  activeSearchResult: SearchResult | null;
25
+ transientHighlights?: TransientHighlight[];
25
26
  onMouseDown: (e: React.MouseEvent<Element>, pageNum: number) => void;
26
27
  onMouseMove: (e: React.MouseEvent<Element>, pageNum: number) => void;
27
28
  onMouseUp: (e: React.MouseEvent<Element>, pageNum: number) => void;
@@ -35,6 +36,6 @@ interface PageRendererProps {
35
36
  /** Called after the page canvas has been painted (used for first-page-rendered telemetry). */
36
37
  onRendered?: (pageNum: number) => void;
37
38
  }
38
- declare function PageRendererComponent({ pageNum, pdfDoc, scale, rotation, containerWidth: _containerWidth, containerHeight: _containerHeight, scrollTop: _scrollTop, scrollLeft: _scrollLeft, pageTop, pageLeft, basePageWidth, basePageHeight, activeTab, activeTool, annotations, selectedAnnotationId, activeSearchResult, onMouseDown, onMouseMove, onMouseUp, onAnnotationClick, onAnnotationMouseEnter, onClearSelection, watermark, watermarkText, redactions, onDiscardRedaction, onRendered }: PageRendererProps): React.JSX.Element;
39
+ declare function PageRendererComponent({ pageNum, pdfDoc, scale, rotation, containerWidth: _containerWidth, containerHeight: _containerHeight, scrollTop: _scrollTop, scrollLeft: _scrollLeft, pageTop, pageLeft, basePageWidth, basePageHeight, activeTab, activeTool, annotations, selectedAnnotationId, activeSearchResult, transientHighlights, onMouseDown, onMouseMove, onMouseUp, onAnnotationClick, onAnnotationMouseEnter, onClearSelection, watermark, watermarkText, redactions, onDiscardRedaction, onRendered }: PageRendererProps): React.JSX.Element;
39
40
  declare const PageRenderer: React.MemoExoticComponent<typeof PageRendererComponent>;
40
41
  export default PageRenderer;
@@ -1,7 +1,7 @@
1
1
  import { default as React } from 'react';
2
2
  import { DocumentLoadErrorInfo } from './DocumentViewer';
3
3
  import { WebViewerInstance } from '../core/ViewerInstance';
4
- import { WebViewerOptions } from '../core/types';
4
+ import { WebViewerOptions, TransientHighlight } from '../core/types';
5
5
  import { Annotation } from '../annotations/types';
6
6
  export interface TeamSyncViewerProps extends Omit<WebViewerOptions, 'path'> {
7
7
  /** URL of the PDF to display. Alias of `initialDoc`; `fileUrl` wins when both are set. */
@@ -28,6 +28,8 @@ export interface TeamSyncViewerProps extends Omit<WebViewerOptions, 'path'> {
28
28
  }) => void;
29
29
  onPageChange?: (pageNumber: number, numPages: number) => void;
30
30
  onAnnotationsChange?: (annotations: Annotation[]) => void;
31
+ /** Fires whenever transient highlights change. */
32
+ onTransientHighlightsChange?: (highlights: TransientHighlight[]) => void;
31
33
  className?: string;
32
34
  style?: React.CSSProperties;
33
35
  }
@@ -2,7 +2,8 @@ import { ViewerBus } from './eventBus';
2
2
  import { ExportOptions } from './export';
3
3
  import { Annotation } from '../annotations/types';
4
4
  import { AnnotationManager } from '../annotations/AnnotationManager';
5
- import { Redaction, WatermarkOptions, ViewerEventMap, ViewerEventType } from './types';
5
+ import { Redaction, WatermarkOptions, ViewerEventMap, ViewerEventType, TransientHighlight } from './types';
6
+ import { SearchResult } from '../hooks/usePdfSearch';
6
7
  /**
7
8
  * © 2026 AngelBot Ai Pvt Ltd. All rights reserved.
8
9
  * WebViewerInstance — the imperative handle returned by `createWebViewer()` and exposed through
@@ -21,6 +22,11 @@ export interface ViewerBinding {
21
22
  getCurrentPage(): number;
22
23
  getPageCount(): number;
23
24
  loadDocument(url: string): void;
25
+ goToPage(pageNumber: number, options?: {
26
+ smooth?: boolean;
27
+ }): void;
28
+ getTransientHighlights(): TransientHighlight[];
29
+ setTransientHighlights(highlights: TransientHighlight[]): void;
24
30
  }
25
31
  export declare class WebViewerInstance {
26
32
  readonly bus: ViewerBus;
@@ -56,6 +62,39 @@ export declare class WebViewerInstance {
56
62
  getPdfDocument(): pdfjsLib.PDFDocumentProxy | null;
57
63
  getCurrentPage(): number;
58
64
  getPageCount(): number;
65
+ /** Navigate to a specific page number (1-based). */
66
+ goToPage(pageNumber: number, options?: {
67
+ smooth?: boolean;
68
+ }): void;
69
+ /** Set the current page number (1-based). Alias of goToPage. */
70
+ setCurrentPage(pageNumber: number): void;
71
+ /**
72
+ * Search the document for text matches, returning snippets and page bounding boxes.
73
+ */
74
+ searchText(query: string): Promise<SearchResult[]>;
75
+ /** Get the current list of transient highlights. */
76
+ getTransientHighlights(): TransientHighlight[];
77
+ /**
78
+ * Set transient visual highlights across one or more pages.
79
+ * These render directly on the PDF canvas but are NEVER saved into AnnotationManager
80
+ * or exported to XFDF.
81
+ */
82
+ setTransientHighlights(highlights: TransientHighlight[]): void;
83
+ /** Add a single transient highlight to the current list. */
84
+ addTransientHighlight(highlight: TransientHighlight): void;
85
+ /** Clear all active transient highlights. */
86
+ clearTransientHighlights(): void;
87
+ /**
88
+ * Search for a snippet or citation query, scroll to its page, and highlight it temporarily.
89
+ * Returns the matched SearchResult or null if not found.
90
+ */
91
+ highlightSnippet(query: string, options?: {
92
+ pageIndex?: number;
93
+ pulse?: boolean;
94
+ scrollTo?: boolean;
95
+ color?: string;
96
+ tooltip?: string;
97
+ }): Promise<SearchResult | null>;
59
98
  getAnnotations(): Annotation[];
60
99
  /** Print the exported document (annotations, redactions and watermark baked in). */
61
100
  print(): void;
@@ -67,6 +106,8 @@ export declare class WebViewerInstance {
67
106
  readonly UI: {
68
107
  /** Theming is not implemented; kept for API compatibility. */
69
108
  setTheme: (_theme: string) => void;
109
+ goToPage: (pageNumber: number) => void;
110
+ setCurrentPageNumber: (pageNumber: number) => void;
70
111
  openElements: (elements: string[]) => void;
71
112
  closeElements: (elements: string[]) => void;
72
113
  enableElements: (elements: string[]) => void;
@@ -90,6 +131,9 @@ export declare class WebViewerInstance {
90
131
  removeEventListener: (event: string, callback: (detail: unknown) => void) => void;
91
132
  getCurrentPage: () => number;
92
133
  getPageCount: () => number;
134
+ goToPage: (pageNumber: number) => void;
135
+ setCurrentPage: (pageNumber: number) => void;
136
+ setCurrentPageNumber: (pageNumber: number) => void;
93
137
  getDocument: () => {
94
138
  getFileData: (options?: ExportOptions) => Promise<Uint8Array<ArrayBufferLike>>;
95
139
  };
@@ -3,6 +3,27 @@ import { Annotation } from '../annotations/types';
3
3
  import { AnnotationChangedEvent } from '../annotations/AnnotationManager';
4
4
  import { PdfAssetPaths } from './pdfAssets';
5
5
  export type { PdfAssetPaths };
6
+ export interface SearchBounds {
7
+ x: number;
8
+ y: number;
9
+ width: number;
10
+ height: number;
11
+ }
12
+ export interface TransientHighlight {
13
+ id?: string;
14
+ /** 1-based page number */
15
+ pageIndex: number;
16
+ /** Bounding boxes on the page in PDF coordinate points */
17
+ bounds: SearchBounds[];
18
+ /** Background fill color (default 'rgba(250, 204, 21, 0.4)') */
19
+ color?: string;
20
+ /** Border color (default 'rgba(234, 179, 8, 0.8)') */
21
+ borderColor?: string;
22
+ /** Whether to animate with a pulsing focus ring */
23
+ pulse?: boolean;
24
+ /** Optional tooltip text displayed above the highlight */
25
+ tooltip?: string;
26
+ }
6
27
  export interface Redaction {
7
28
  id?: string;
8
29
  /** 1-based page number */
@@ -47,6 +68,10 @@ export interface WebViewerOptions {
47
68
  initialScale?: InitialScale;
48
69
  /** 1-based page to scroll to after load. */
49
70
  initialPage?: number;
71
+ /** Current 1-based page to display. Changes navigate to the given page. */
72
+ page?: number;
73
+ /** Transient visual highlights that render on pages without polluting AnnotationManager. */
74
+ transientHighlights?: TransientHighlight[];
50
75
  plugins?: ViewerPlugin[];
51
76
  redactions?: Redaction[];
52
77
  regexRedactions?: RegExp[];
@@ -100,6 +125,9 @@ export interface ViewerEventMap {
100
125
  };
101
126
  /** Granular add / modify / delete (with the `imported` flag) from the AnnotationManager. */
102
127
  annotationChanged: AnnotationChangedEvent;
128
+ transientHighlightsChanged: {
129
+ highlights: TransientHighlight[];
130
+ };
103
131
  toolChanged: {
104
132
  tool: string | null;
105
133
  };
@@ -1,11 +1,6 @@
1
- import { Redaction } from '../core/types';
2
- import * as pdfjsLib from 'pdfjs-dist';
3
- export interface SearchBounds {
4
- x: number;
5
- y: number;
6
- width: number;
7
- height: number;
8
- }
1
+ import { Redaction, SearchBounds } from '../core/types';
2
+ import type * as pdfjsLib from 'pdfjs-dist';
3
+ export type { SearchBounds };
9
4
  export interface SearchResult {
10
5
  id: string;
11
6
  pageIndex: number;
@@ -14,6 +9,11 @@ export interface SearchResult {
14
9
  matchLength: number;
15
10
  bounds: SearchBounds[];
16
11
  }
12
+ /**
13
+ * Standalone asynchronous text search across all pages of a PDFDocumentProxy.
14
+ * Sanitizes redacted regions and returns matching snippets and bounding boxes.
15
+ */
16
+ export declare function searchPdfText(pdfDoc: pdfjsLib.PDFDocumentProxy, query: string, redactions?: Redaction[], onProgress?: (progress: number) => void): Promise<SearchResult[]>;
17
17
  export declare function usePdfSearch(pdfDoc: pdfjsLib.PDFDocumentProxy | null, redactions?: Redaction[]): {
18
18
  search: (query: string) => Promise<void>;
19
19
  searchResults: SearchResult[];
@@ -5,9 +5,19 @@ export interface Shortcut {
5
5
  defaultCommand: string;
6
6
  command: string;
7
7
  }
8
+ export declare const DEFAULT_SHORTCUTS: Shortcut[];
9
+ export declare function loadStoredShortcuts(): Shortcut[];
8
10
  export declare function useShortcuts(): {
9
11
  shortcuts: Shortcut[];
10
12
  updateShortcut: (action: ActionType, newCommand: string) => void;
13
+ resetShortcuts: () => void;
11
14
  getCommand: (action: ActionType) => string;
12
15
  };
16
+ export interface ParsedCommand {
17
+ meta: boolean;
18
+ shift: boolean;
19
+ alt: boolean;
20
+ key: string;
21
+ }
22
+ export declare function parseCommandString(commandString: string): ParsedCommand;
13
23
  export declare function matchShortcut(e: KeyboardEvent, commandString: string): boolean;
@@ -28,4 +28,7 @@ export { annotationsToXfdf, annotationToXfdfFragment, parseXfdf, toPdfDate, from
28
28
  export type { XfdfExportOptions } from './annotations/xfdf';
29
29
  export { printPdfBytes } from './core/print';
30
30
  export type { PrintOptions } from './core/print';
31
+ export { usePdfSearch, searchPdfText } from './hooks/usePdfSearch';
32
+ export type { SearchResult, SearchBounds } from './hooks/usePdfSearch';
33
+ export type { TransientHighlight } from './core/types';
31
34
  export * from './plugins/types';