web-annotation-renderer 0.1.0 → 0.1.2

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
- # @ai-annotator/renderer
1
+ # web-annotation-renderer
2
2
 
3
- [![npm version](https://badge.fury.io/js/@ai-annotator%2Frenderer.svg)](https://www.npmjs.com/package/@ai-annotator/renderer)
3
+ [![npm version](https://badge.fury.io/js/web-annotation-renderer.svg)](https://www.npmjs.com/package/web-annotation-renderer)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
5
 
6
6
  A framework-agnostic PDF annotation renderer with timeline synchronization for educational content, interactive presentations, and annotated documents.
@@ -21,7 +21,7 @@ This library renders structured annotation data (highlights, text boxes, drawing
21
21
  ## Installation
22
22
 
23
23
  ```bash
24
- npm install @ai-annotator/renderer
24
+ npm install web-annotation-renderer
25
25
  ```
26
26
 
27
27
  **Requirements:**
@@ -47,7 +47,7 @@ This must be done once at application startup, before using any PDF functionalit
47
47
  ## Quick Start - Vanilla JavaScript
48
48
 
49
49
  ```javascript
50
- import { AnnotationRenderer } from "@ai-annotator/renderer";
50
+ import { AnnotationRenderer } from "web-annotation-renderer";
51
51
  import * as pdfjsLib from "pdfjs-dist";
52
52
 
53
53
  // Configure PDF.js worker (call once at app startup)
@@ -63,7 +63,10 @@ const renderer = new AnnotationRenderer({
63
63
  });
64
64
 
65
65
  // Load PDF
66
- await renderer.loadPDF("/path/to/document.pdf");
66
+ const result = await renderer.loadPDF("/path/to/document.pdf");
67
+ if (result.success) {
68
+ console.log(`PDF loaded with ${result.pageCount} pages`);
69
+ }
67
70
 
68
71
  // Set annotations
69
72
  renderer.setAnnotations([
@@ -73,19 +76,25 @@ renderer.setAnnotations([
73
76
  page: 1,
74
77
  start: 0,
75
78
  end: 5,
79
+ mode: "quads",
76
80
  quads: [{ x: 0.1, y: 0.2, w: 0.3, h: 0.05 }],
77
81
  style: { color: "rgba(255, 255, 0, 0.3)" },
78
82
  },
79
83
  ]);
80
84
 
85
+ // Set initial page and scale
86
+ await renderer.setPage(1);
87
+ await renderer.setScale(1.0);
88
+
81
89
  // Update timeline position
82
- renderer.updateTimeline(2.5); // seconds
90
+ renderer.setTime(2.5); // seconds
83
91
  ```
84
92
 
85
93
  ## Quick Start - React
86
94
 
87
95
  ```javascript
88
- import { AnnotPdf } from "@ai-annotator/renderer";
96
+ import { useState } from "react";
97
+ import { AnnotPdf } from "web-annotation-renderer";
89
98
  import * as pdfjsLib from "pdfjs-dist";
90
99
 
91
100
  // Configure PDF.js worker (call once at app startup)
@@ -96,6 +105,8 @@ pdfjsLib.GlobalWorkerOptions.workerSrc = new URL(
96
105
 
97
106
  function App() {
98
107
  const [currentTime, setCurrentTime] = useState(0);
108
+ const [page, setPage] = useState(1);
109
+ const [totalPages, setTotalPages] = useState(1);
99
110
 
100
111
  const annotations = [
101
112
  {
@@ -104,30 +115,550 @@ function App() {
104
115
  page: 1,
105
116
  start: 0,
106
117
  end: 5,
118
+ mode: "quads",
107
119
  quads: [{ x: 0.1, y: 0.2, w: 0.3, h: 0.05 }],
108
120
  style: { color: "rgba(255, 255, 0, 0.3)" },
109
121
  },
110
122
  ];
111
123
 
124
+ const handleLoad = (pdfDocument) => {
125
+ setTotalPages(pdfDocument.pageCount);
126
+ console.log(`PDF loaded with ${pdfDocument.pageCount} pages`);
127
+ };
128
+
129
+ const handleError = (error) => {
130
+ console.error("PDF Error:", error);
131
+ };
132
+
112
133
  return (
113
134
  <AnnotPdf
114
135
  pdfUrl="/path/to/document.pdf"
115
136
  annotations={annotations}
116
137
  currentTime={currentTime}
117
- page={1}
118
- scale={1.0}
119
- width={800}
120
- height={600}
138
+ page={page}
139
+ scale={1.5}
140
+ onLoad={handleLoad}
141
+ onError={handleError}
142
+ onPageChange={setPage}
121
143
  />
122
144
  );
123
145
  }
124
146
  ```
125
147
 
126
- ## Documentation
148
+ ## API Reference
149
+
150
+ ### AnnotationRenderer Class
151
+
152
+ #### Constructor
153
+
154
+ ```javascript
155
+ const renderer = new AnnotationRenderer(config);
156
+ ```
157
+
158
+ **Configuration Options:**
159
+
160
+ | Property | Type | Required | Default | Description |
161
+ | --------------- | ----------------- | -------- | ------- | -------------------------------------- |
162
+ | `container` | HTMLElement | ✅ Yes | - | DOM element for annotation layers |
163
+ | `canvasElement` | HTMLCanvasElement | ✅ Yes | - | Canvas element for PDF rendering |
164
+ | `pdfUrl` | string | No | `null` | PDF URL to auto-load on initialization |
165
+ | `initialPage` | number | No | `1` | Initial page number to display |
166
+ | `initialScale` | number | No | `1.0` | Initial zoom/scale factor |
167
+ | `annotations` | Array | No | `[]` | Initial annotation data |
168
+
169
+ **Example:**
170
+
171
+ ```javascript
172
+ const renderer = new AnnotationRenderer({
173
+ container: document.getElementById("annotation-container"),
174
+ canvasElement: document.getElementById("pdf-canvas"),
175
+ pdfUrl: "/document.pdf", // Optional: auto-load
176
+ initialPage: 1,
177
+ initialScale: 1.5,
178
+ annotations: [],
179
+ });
180
+ ```
181
+
182
+ #### Methods
183
+
184
+ ##### `loadPDF(url)`
185
+
186
+ Load a PDF document from URL.
187
+
188
+ ```javascript
189
+ const result = await renderer.loadPDF("/path/to/document.pdf");
190
+ ```
191
+
192
+ **Parameters:**
193
+
194
+ - `url` (string): URL or path to PDF file
195
+
196
+ **Returns:** `Promise<Object>`
197
+
198
+ ```javascript
199
+ {
200
+ success: boolean, // Whether loading succeeded
201
+ pageCount?: number, // Number of pages (if successful)
202
+ error?: string // Error message (if failed)
203
+ }
204
+ ```
205
+
206
+ ##### `setPage(pageNum)`
207
+
208
+ Navigate to a specific page.
209
+
210
+ ```javascript
211
+ const result = await renderer.setPage(2);
212
+ ```
213
+
214
+ **Parameters:**
215
+
216
+ - `pageNum` (number): Page number (1-indexed)
217
+
218
+ **Returns:** `Promise<Object>`
219
+
220
+ ```javascript
221
+ {
222
+ success: boolean,
223
+ viewport?: Object, // Viewport dimensions (if successful)
224
+ error?: string // Error message (if failed)
225
+ }
226
+ ```
227
+
228
+ ##### `setScale(scale)`
229
+
230
+ Change the zoom level.
231
+
232
+ ```javascript
233
+ const result = await renderer.setScale(1.5);
234
+ ```
235
+
236
+ **Parameters:**
237
+
238
+ - `scale` (number): Scale factor (e.g., 0.5, 1.0, 1.5, 2.0)
239
+
240
+ **Returns:** `Promise<Object>` - Same structure as `setPage()`
241
+
242
+ ##### `setAnnotations(annotations)`
243
+
244
+ Update the annotation data.
245
+
246
+ ```javascript
247
+ renderer.setAnnotations(annotationsArray);
248
+ ```
249
+
250
+ **Parameters:**
251
+
252
+ - `annotations` (Array): Array of annotation objects
253
+
254
+ **Returns:** `void`
255
+
256
+ ##### `setTime(timestamp)`
257
+
258
+ Update the timeline position for animation synchronization.
259
+
260
+ ```javascript
261
+ renderer.setTime(5.2); // 5.2 seconds
262
+ ```
263
+
264
+ **Parameters:**
265
+
266
+ - `timestamp` (number): Current timeline position in seconds
267
+
268
+ **Returns:** `void`
269
+
270
+ ##### `getState()`
271
+
272
+ Get the current renderer state.
273
+
274
+ ```javascript
275
+ const state = renderer.getState();
276
+ ```
277
+
278
+ **Returns:** `Object`
279
+
280
+ ```javascript
281
+ {
282
+ page: number, // Current page number
283
+ scale: number, // Current scale factor
284
+ annotations: Array, // Current annotations array
285
+ pageCount: number, // Total page count
286
+ time: number, // Current timeline position
287
+ viewport: Object|null, // Current viewport dimensions
288
+ pdfUrl: string|null // Current PDF URL
289
+ }
290
+ ```
291
+
292
+ ##### `destroy()`
293
+
294
+ Clean up all resources and subsystems.
295
+
296
+ ```javascript
297
+ renderer.destroy();
298
+ ```
299
+
300
+ **Returns:** `void`
301
+
302
+ **Important:** Call this before removing the renderer instance to prevent memory leaks.
303
+
304
+ ---
305
+
306
+ ### AnnotPdf Component (React)
307
+
308
+ #### Props
309
+
310
+ ```javascript
311
+ <AnnotPdf
312
+ pdfUrl={string}
313
+ page={number}
314
+ scale={number}
315
+ annotations={array}
316
+ currentTime={number}
317
+ onLoad={function}
318
+ onError={function}
319
+ onPageChange={function}
320
+ className={string}
321
+ style={object}
322
+ canvasStyle={object}
323
+ />
324
+ ```
325
+
326
+ **Prop Reference:**
327
+
328
+ | Prop | Type | Required | Default | Description |
329
+ | -------------- | -------- | -------- | ------- | -------------------------------------------- |
330
+ | `pdfUrl` | string | ✅ Yes | - | URL or path to PDF file |
331
+ | `page` | number | No | `1` | Current page number (1-indexed) |
332
+ | `scale` | number | No | `1.5` | Zoom level / scale factor |
333
+ | `annotations` | Array | No | `[]` | Array of annotation objects |
334
+ | `currentTime` | number | No | `0` | Current timeline position in seconds |
335
+ | `onLoad` | function | No | - | Callback when PDF loads: `(doc) => void` |
336
+ | `onError` | function | No | - | Callback on error: `(error) => void` |
337
+ | `onPageChange` | function | No | - | Callback on page change: `(pageNum) => void` |
338
+ | `className` | string | No | - | CSS class for container div |
339
+ | `style` | object | No | `{}` | Inline styles for container div |
340
+ | `canvasStyle` | object | No | `{}` | Inline styles for canvas element |
341
+
342
+ **Note:** The component auto-sizes based on PDF dimensions and scale. There are no `width` or `height` props.
343
+
344
+ **Example with all props:**
345
+
346
+ ```javascript
347
+ <AnnotPdf
348
+ pdfUrl="/document.pdf"
349
+ page={currentPage}
350
+ scale={1.5}
351
+ annotations={annotations}
352
+ currentTime={audioTime}
353
+ onLoad={(doc) => setTotalPages(doc.pageCount)}
354
+ onError={(err) => console.error(err)}
355
+ onPageChange={(num) => setCurrentPage(num)}
356
+ className="pdf-viewer"
357
+ style={{ border: "1px solid #ccc" }}
358
+ canvasStyle={{ boxShadow: "0 2px 8px rgba(0,0,0,0.1)" }}
359
+ />
360
+ ```
361
+
362
+ ## Annotation Data Format
363
+
364
+ All annotations use **normalized coordinates** (0-1 range) for positioning, making them resolution-independent.
365
+
366
+ ### Common Fields
367
+
368
+ All annotation types share these base fields:
369
+
370
+ | Field | Type | Required | Description |
371
+ | ------- | ------ | -------- | ---------------------------------------------------- |
372
+ | `id` | string | ✅ Yes | Unique identifier for the annotation |
373
+ | `type` | string | ✅ Yes | Annotation type: `"highlight"`, `"text"`, or `"ink"` |
374
+ | `page` | number | ✅ Yes | Page number (1-indexed) |
375
+ | `start` | number | ✅ Yes | Timeline start time in seconds |
376
+ | `end` | number | ✅ Yes | Timeline end time in seconds |
377
+
378
+ ---
379
+
380
+ ### Highlight Annotations
381
+
382
+ Highlights rectangular regions on the PDF with progressive reveal animation.
383
+
384
+ **Type:** `"highlight"`
385
+
386
+ **Structure:**
387
+
388
+ ```javascript
389
+ {
390
+ id: "highlight-1",
391
+ type: "highlight",
392
+ page: 1,
393
+ start: 0,
394
+ end: 5,
395
+ mode: "quads", // ✅ REQUIRED - must be "quads"
396
+ quads: [
397
+ { x: 0.1, y: 0.2, w: 0.3, h: 0.05 }, // First quad
398
+ { x: 0.1, y: 0.25, w: 0.35, h: 0.05 } // Second quad (optional)
399
+ ],
400
+ style: {
401
+ color: "rgba(255, 255, 0, 0.3)" // Highlight color
402
+ }
403
+ }
404
+ ```
405
+
406
+ **Fields:**
127
407
 
128
- - [Full API Reference](https://github.com/jhl72e/pdfAutoAnnotator/blob/main/docs/API.md)
129
- - [Annotation Data Format](https://github.com/jhl72e/pdfAutoAnnotator/blob/main/docs/ANNOTATION_FORMAT.md)
130
- - [Examples](https://github.com/jhl72e/pdfAutoAnnotator/tree/main/examples)
408
+ | Field | Type | Required | Description |
409
+ | ------------- | ------ | -------- | -------------------------------------------------- |
410
+ | `mode` | string | ✅ Yes | Must be `"quads"` |
411
+ | `quads` | Array | ✅ Yes | Array of quad objects defining highlighted regions |
412
+ | `quads[].x` | number | ✅ Yes | Left position (0-1, normalized) |
413
+ | `quads[].y` | number | ✅ Yes | Top position (0-1, normalized) |
414
+ | `quads[].w` | number | ✅ Yes | Width (0-1, normalized) |
415
+ | `quads[].h` | number | ✅ Yes | Height (0-1, normalized) |
416
+ | `style.color` | string | ✅ Yes | CSS color for highlight |
417
+
418
+ **Animation:** Highlights reveal progressively from left to right across all quads during the `start` to `end` timeline.
419
+
420
+ **Example:**
421
+
422
+ ```javascript
423
+ {
424
+ id: "hl-1",
425
+ type: "highlight",
426
+ page: 1,
427
+ start: 2.0,
428
+ end: 7.0,
429
+ mode: "quads",
430
+ quads: [
431
+ { x: 0.1, y: 0.3, w: 0.4, h: 0.05 }
432
+ ],
433
+ style: {
434
+ color: "rgba(255, 255, 0, 0.4)"
435
+ }
436
+ }
437
+ ```
438
+
439
+ ---
440
+
441
+ ### Text Annotations
442
+
443
+ Display text boxes with progressive typing animation.
444
+
445
+ **Type:** `"text"`
446
+
447
+ **Structure:**
448
+
449
+ ```javascript
450
+ {
451
+ id: "text-1",
452
+ type: "text",
453
+ page: 1,
454
+ start: 3,
455
+ end: 8,
456
+ content: "This is the annotation text content",
457
+ x: 0.5, // Left position (normalized)
458
+ y: 0.2, // Top position (normalized)
459
+ w: 0.3, // Width (normalized)
460
+ h: 0.1, // Height (normalized)
461
+ style: {
462
+ bg: "rgba(255, 255, 255, 0.95)", // Background color
463
+ color: "#1f2937" // Text color
464
+ }
465
+ }
466
+ ```
467
+
468
+ **Fields:**
469
+
470
+ | Field | Type | Required | Default | Description |
471
+ | ------------- | ------ | -------- | ------------------------- | ------------------------------- |
472
+ | `content` | string | ✅ Yes | - | Text to display |
473
+ | `x` | number | ✅ Yes | - | Left position (0-1, normalized) |
474
+ | `y` | number | ✅ Yes | - | Top position (0-1, normalized) |
475
+ | `w` | number | ✅ Yes | - | Width (0-1, normalized) |
476
+ | `h` | number | ✅ Yes | - | Height (0-1, normalized) |
477
+ | `style.bg` | string | No | `"rgba(255,255,255,0.9)"` | Background color |
478
+ | `style.color` | string | No | `"#1f2937"` | Text color |
479
+
480
+ **Animation:** Text appears word-by-word with a typing effect during the `start` to `end` timeline.
481
+
482
+ **Example:**
483
+
484
+ ```javascript
485
+ {
486
+ id: "txt-1",
487
+ type: "text",
488
+ page: 1,
489
+ start: 5.0,
490
+ end: 12.0,
491
+ content: "Important concept to remember",
492
+ x: 0.6,
493
+ y: 0.4,
494
+ w: 0.25,
495
+ h: 0.08,
496
+ style: {
497
+ bg: "rgba(255, 255, 200, 0.95)",
498
+ color: "#0066cc"
499
+ }
500
+ }
501
+ ```
502
+
503
+ ---
504
+
505
+ ### Ink Annotations
506
+
507
+ Draw strokes/paths with progressive reveal animation.
508
+
509
+ **Type:** `"ink"`
510
+
511
+ **Structure:**
512
+
513
+ ```javascript
514
+ {
515
+ id: "ink-1",
516
+ type: "ink",
517
+ page: 1,
518
+ start: 10,
519
+ end: 15,
520
+ strokes: [
521
+ {
522
+ color: "rgb(255, 0, 0)",
523
+ size: 3,
524
+ points: [
525
+ { t: 0.0, x: 0.2, y: 0.5 },
526
+ { t: 0.5, x: 0.3, y: 0.45 },
527
+ { t: 1.0, x: 0.4, y: 0.5 }
528
+ ]
529
+ }
530
+ ]
531
+ }
532
+ ```
533
+
534
+ **Fields:**
535
+
536
+ | Field | Type | Required | Default | Description |
537
+ | ------------------ | ------ | -------- | ----------- | ------------------------------------ |
538
+ | `strokes` | Array | ✅ Yes | - | Array of stroke objects |
539
+ | `strokes[].color` | string | No | `"#1f2937"` | CSS color for stroke |
540
+ | `strokes[].size` | number | No | `3` | Line width in pixels |
541
+ | `strokes[].points` | Array | ✅ Yes | - | Array of point objects |
542
+ | `points[].t` | number | ✅ Yes | - | Time within stroke (0-1, normalized) |
543
+ | `points[].x` | number | ✅ Yes | - | X position (0-1, normalized) |
544
+ | `points[].y` | number | ✅ Yes | - | Y position (0-1, normalized) |
545
+
546
+ **Important:**
547
+
548
+ - Each point **must have a `t` parameter** representing its position in time within the stroke (0-1 range)
549
+ - The `t` values are used for progressive drawing animation
550
+ - Points should be ordered by increasing `t` values
551
+
552
+ **Animation:** Strokes are drawn progressively based on the `t` parameter of each point during the `start` to `end` timeline.
553
+
554
+ **Example:**
555
+
556
+ ```javascript
557
+ {
558
+ id: "ink-1",
559
+ type: "ink",
560
+ page: 1,
561
+ start: 8.0,
562
+ end: 13.0,
563
+ strokes: [
564
+ {
565
+ color: "rgb(255, 0, 0)",
566
+ size: 2,
567
+ points: [
568
+ { t: 0.0, x: 0.2, y: 0.4 },
569
+ { t: 0.25, x: 0.25, y: 0.38 },
570
+ { t: 0.5, x: 0.3, y: 0.4 },
571
+ { t: 0.75, x: 0.35, y: 0.42 },
572
+ { t: 1.0, x: 0.4, y: 0.4 }
573
+ ]
574
+ },
575
+ {
576
+ color: "rgb(255, 0, 0)",
577
+ size: 2,
578
+ points: [
579
+ { t: 0.0, x: 0.22, y: 0.45 },
580
+ { t: 1.0, x: 0.38, y: 0.45 }
581
+ ]
582
+ }
583
+ ]
584
+ }
585
+ ```
586
+
587
+ ---
588
+
589
+ ### Coordinate System
590
+
591
+ All position and size values use **normalized coordinates** (0-1 range):
592
+
593
+ - `0` = left edge / top edge
594
+ - `1` = right edge / bottom edge
595
+ - `0.5` = center
596
+
597
+ **Example:** `x: 0.1, y: 0.2, w: 0.3, h: 0.05` means:
598
+
599
+ - Starts at 10% from left, 20% from top
600
+ - Width is 30% of page width
601
+ - Height is 5% of page height
602
+
603
+ This makes annotations resolution-independent and responsive to different screen sizes.
604
+
605
+ ---
606
+
607
+ ### Complete Example
608
+
609
+ ```javascript
610
+ const annotations = [
611
+ // Highlight
612
+ {
613
+ id: "h1",
614
+ type: "highlight",
615
+ page: 1,
616
+ start: 0,
617
+ end: 5,
618
+ mode: "quads",
619
+ quads: [{ x: 0.1, y: 0.2, w: 0.3, h: 0.05 }],
620
+ style: { color: "rgba(255, 255, 0, 0.4)" },
621
+ },
622
+
623
+ // Text
624
+ {
625
+ id: "t1",
626
+ type: "text",
627
+ page: 1,
628
+ start: 3,
629
+ end: 8,
630
+ content: "Key concept here",
631
+ x: 0.6,
632
+ y: 0.2,
633
+ w: 0.25,
634
+ h: 0.08,
635
+ style: {
636
+ bg: "rgba(255, 255, 255, 0.95)",
637
+ color: "#0066cc",
638
+ },
639
+ },
640
+
641
+ // Ink
642
+ {
643
+ id: "i1",
644
+ type: "ink",
645
+ page: 1,
646
+ start: 6,
647
+ end: 10,
648
+ strokes: [
649
+ {
650
+ color: "rgb(255, 0, 0)",
651
+ size: 3,
652
+ points: [
653
+ { t: 0.0, x: 0.2, y: 0.5 },
654
+ { t: 0.5, x: 0.3, y: 0.45 },
655
+ { t: 1.0, x: 0.4, y: 0.5 },
656
+ ],
657
+ },
658
+ ],
659
+ },
660
+ ];
661
+ ```
131
662
 
132
663
  ## Browser Compatibility
133
664
 
@@ -143,13 +674,94 @@ Requires:
143
674
  - Canvas API
144
675
  - Web Workers
145
676
 
677
+ ## Troubleshooting
678
+
679
+ ### Annotations not appearing
680
+
681
+ **Symptoms:** PDF renders but annotations don't show when moving timeline
682
+
683
+ **Solutions:**
684
+
685
+ 1. Check that highlight annotations include `mode: "quads"` field
686
+ 2. Verify text annotations use `type: "text"` (not `"textBox"`)
687
+ 3. Ensure ink annotation points have `t` parameter
688
+ 4. Check browser console for validation warnings
689
+ 5. Verify timeline position is within annotation `start`/`end` range
690
+
691
+ ### Worker errors
692
+
693
+ **Symptoms:** "Setting up fake worker" or worker-related errors
694
+
695
+ **Solution:** Ensure PDF.js worker is configured before using the library:
696
+
697
+ ```javascript
698
+ import * as pdfjsLib from "pdfjs-dist";
699
+
700
+ pdfjsLib.GlobalWorkerOptions.workerSrc = new URL(
701
+ "pdfjs-dist/build/pdf.worker.min.mjs",
702
+ import.meta.url
703
+ ).toString();
704
+ ```
705
+
706
+ ### Type errors with React
707
+
708
+ **Symptoms:** TypeScript errors about missing props
709
+
710
+ **Solution:** Install React type definitions:
711
+
712
+ ```bash
713
+ npm install --save-dev @types/react @types/react-dom
714
+ ```
715
+
716
+ ### Canvas rendering issues in React StrictMode
717
+
718
+ **Symptoms:** Double rendering or canvas errors in development
719
+
720
+ **Solution:** Consider removing `<React.StrictMode>` wrapper during development (it's safe to keep in production).
721
+
722
+ ## Migration Guide
723
+
724
+ If you're upgrading from documentation examples that used the old format:
725
+
726
+ ### Breaking Changes
727
+
728
+ 1. **Package name:** `@ai-annotator/renderer` → `web-annotation-renderer`
729
+ 2. **Method name:** `renderer.updateTimeline()` → `renderer.setTime()`
730
+ 3. **Highlight annotations:** Must include `mode: "quads"` field
731
+ 4. **Text annotations:** Use `type: "text"` (not `"textBox"`), `content` (not `text`), flat coordinates, and `style.bg`/`style.color`
732
+ 5. **Ink annotations:** Use `strokes` (not `paths`), `size` (not `width`), and points must have `t` parameter
733
+
734
+ ### Quick Migration
735
+
736
+ ```javascript
737
+ // OLD (won't work)
738
+ import { AnnotationRenderer } from "@ai-annotator/renderer";
739
+ renderer.updateTimeline(5.0);
740
+
741
+ // NEW (correct)
742
+ import { AnnotationRenderer } from "web-annotation-renderer";
743
+ renderer.setTime(5.0);
744
+ ```
745
+
746
+ ## Additional Resources
747
+
748
+ - [GitHub Repository](https://github.com/jhl72e/pdfAutoAnnotator)
749
+ - [Issue Tracker](https://github.com/jhl72e/pdfAutoAnnotator/issues)
750
+ - [Changelog](CHANGELOG.md)
751
+
146
752
  ## Examples
147
753
 
148
- Check out the [examples directory](https://github.com/jhl72e/pdfAutoAnnotator/tree/main/examples) for complete working examples:
754
+ Check out working examples in the test projects:
755
+
756
+ - **Vanilla JavaScript:** See `test-vanilla/` for a complete implementation
757
+ - **React:** See `test-react/` for React component usage
758
+
759
+ Both examples include:
149
760
 
150
- - Vanilla JavaScript
151
- - React with timeline sync
152
- - React with audio integration
761
+ - PDF loading and rendering
762
+ - Page navigation and zoom controls
763
+ - Timeline slider with annotation synchronization
764
+ - All three annotation types (highlight, text, ink)
153
765
 
154
766
  ## License
155
767
 
package/dist/index14.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const y=require("react/jsx-runtime"),r=require("react"),p=require("./index2.cjs");function w({pdfUrl:u,page:i=1,scale:s=1.5,annotations:P=[],currentTime:f=0,onLoad:l,onError:t,onPageChange:o,className:A,style:h,canvasStyle:v}){const a=r.useRef(null),d=r.useRef(null),n=r.useRef(null);r.useEffect(()=>{if(!(!a.current||!d.current)){try{n.current=new p.AnnotationRenderer({canvasElement:a.current,container:d.current})}catch(e){console.error("AnnotPdf: Failed to initialize renderer:",e),t&&t(e)}return()=>{n.current&&(n.current.destroy(),n.current=null)}}},[]),r.useEffect(()=>{if(!n.current||!u)return;let e=!1;return(async()=>{try{const c=await n.current.loadPDF(u);if(e)return;if(!c.success){console.error("AnnotPdf: Failed to load PDF:",c.error),t&&t(new Error(c.error));return}l&&l({pageCount:c.pageCount})}catch(c){if(e)return;console.error("AnnotPdf: Failed to load PDF:",c),t&&t(c)}})(),()=>{e=!0}},[u,l,t]),r.useEffect(()=>{!n.current||!i||typeof i!="number"||n.current.setPage(i).then(e=>{if(!e.success){console.error("AnnotPdf: Failed to set page:",e.error),t&&t(new Error(e.error));return}o&&o(i)}).catch(e=>{console.error("AnnotPdf: Failed to set page:",e),t&&t(e)})},[i,o,t]),r.useEffect(()=>{!n.current||!s||typeof s!="number"||n.current.setScale(s).then(e=>{e.success||(console.error("AnnotPdf: Failed to set scale:",e.error),t&&t(new Error(e.error)))}).catch(e=>{console.error("AnnotPdf: Failed to set scale:",e),t&&t(e)})},[s,t]),r.useEffect(()=>{if(n.current)try{n.current.setAnnotations(P||[])}catch(e){console.error("AnnotPdf: Failed to set annotations:",e),t&&t(e)}},[P,t]),r.useEffect(()=>{if(!(!n.current||f===void 0||f===null))try{n.current.setTime(f)}catch(e){console.error("AnnotPdf: Failed to set time:",e),t&&t(e)}},[f,t]);const F={position:"relative",display:"inline-block",lineHeight:0,...h},R={position:"absolute",top:0,left:0,width:"100%",height:"100%",pointerEvents:"none",overflow:"hidden"},b={display:"block",...v};return y.jsxs("div",{className:A,style:F,children:[y.jsx("canvas",{ref:a,style:b}),y.jsx("div",{ref:d,style:R})]})}exports.default=w;
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const P=require("react/jsx-runtime"),n=require("react"),j=require("./index2.cjs");function m({pdfUrl:l,page:u=1,scale:i=1.5,annotations:A=[],currentTime:o=0,onLoad:f,onError:t,onPageChange:a,className:F,style:R,canvasStyle:p}){const d=n.useRef(null),y=n.useRef(null),r=n.useRef(null),v=n.useRef(Promise.resolve()),c=n.useCallback(e=>{v.current=v.current.then(e).catch(h=>{console.error("AnnotPdf: Queued operation failed:",h)})},[]);n.useEffect(()=>{if(!(!d.current||!y.current)){try{r.current=new j.AnnotationRenderer({canvasElement:d.current,container:y.current})}catch(e){console.error("AnnotPdf: Failed to initialize renderer:",e),t&&t(e)}return()=>{r.current&&(r.current.destroy(),r.current=null)}}},[]),n.useEffect(()=>{if(!r.current||!l)return;let e=!1;return c(async()=>{try{const s=await r.current.loadPDF(l);if(e)return;if(!s.success){console.error("AnnotPdf: Failed to load PDF:",s.error),t&&t(new Error(s.error));return}f&&f({pageCount:s.pageCount})}catch(s){if(e)return;console.error("AnnotPdf: Failed to load PDF:",s),t&&t(s)}}),()=>{e=!0}},[l,f,t,c]),n.useEffect(()=>{!r.current||!u||typeof u!="number"||c(async()=>{try{const e=await r.current.setPage(u);if(!e.success){console.error("AnnotPdf: Failed to set page:",e.error),t&&t(new Error(e.error));return}a&&a(u)}catch(e){console.error("AnnotPdf: Failed to set page:",e),t&&t(e)}})},[u,a,t,c]),n.useEffect(()=>{!r.current||!i||typeof i!="number"||c(async()=>{try{const e=await r.current.setScale(i);e.success||(console.error("AnnotPdf: Failed to set scale:",e.error),t&&t(new Error(e.error)))}catch(e){console.error("AnnotPdf: Failed to set scale:",e),t&&t(e)}})},[i,t,c]),n.useEffect(()=>{if(r.current)try{r.current.setAnnotations(A||[])}catch(e){console.error("AnnotPdf: Failed to set annotations:",e),t&&t(e)}},[A,t]),n.useEffect(()=>{if(!(!r.current||o===void 0||o===null))try{r.current.setTime(o)}catch(e){console.error("AnnotPdf: Failed to set time:",e),t&&t(e)}},[o,t]);const w={position:"relative",display:"inline-block",lineHeight:0,...R},b={position:"absolute",top:0,left:0,width:"100%",height:"100%",pointerEvents:"none",overflow:"hidden"},S={display:"block",...p};return P.jsxs("div",{className:F,style:w,children:[P.jsx("canvas",{ref:d,style:S}),P.jsx("div",{ref:y,style:b})]})}exports.default=m;
2
2
  //# sourceMappingURL=index14.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index14.cjs","sources":["../src/adapters/AnnotPdf.jsx"],"sourcesContent":["// ============================================================================\n// SECTION 1: IMPORTS\n// ============================================================================\n\nimport { useRef, useEffect } from 'react';\nimport { AnnotationRenderer } from '../core/AnnotationRenderer.js';\n\n// ============================================================================\n// SECTION 2: JSDOC DOCUMENTATION\n// ============================================================================\n\n/**\n * AnnotPdf - Declarative React component for PDF annotation rendering\n *\n * A React wrapper around the AnnotationRenderer core engine that provides\n * a declarative, props-based API for rendering PDF documents with\n * timeline-synchronized annotations.\n *\n * Features:\n * - Automatic lifecycle management (initialization and cleanup)\n * - Declarative prop-to-method synchronization\n * - PDF rendering with pdf.js\n * - Timeline-synchronized annotation display\n * - Support for highlight, text, and ink annotations\n * - Page navigation and zoom control\n *\n * @component\n * @example\n * // Basic usage\n * <AnnotPdf\n * pdfUrl=\"/document.pdf\"\n * page={1}\n * scale={1.5}\n * annotations={[]}\n * currentTime={0}\n * />\n *\n * @example\n * // With audio synchronization\n * const [currentTime, setCurrentTime] = useState(0);\n *\n * <div>\n * <AnnotPdf\n * pdfUrl=\"/lecture.pdf\"\n * page={1}\n * scale={1.5}\n * annotations={annotations}\n * currentTime={currentTime}\n * onLoad={({ pageCount }) => console.log('Loaded:', pageCount)}\n * />\n * <audio\n * src=\"/lecture.mp3\"\n * onTimeUpdate={(e) => setCurrentTime(e.target.currentTime)}\n * controls\n * />\n * </div>\n *\n * @param {Object} props - Component props\n * @param {string} props.pdfUrl - PDF document URL (required)\n * @param {number} [props.page=1] - Current page number (1-indexed)\n * @param {number} [props.scale=1.5] - Zoom scale factor\n * @param {Array} [props.annotations=[]] - Array of annotation objects\n * @param {number} [props.currentTime=0] - Timeline position in seconds\n * @param {Function} [props.onLoad] - Callback when PDF loads: ({pageCount}) => void\n * @param {Function} [props.onError] - Callback on error: (error) => void\n * @param {Function} [props.onPageChange] - Callback when page changes: (page) => void\n * @param {string} [props.className] - CSS class for container div\n * @param {Object} [props.style] - Inline styles for container div\n * @param {Object} [props.canvasStyle] - Inline styles for canvas element\n * @returns {JSX.Element} PDF viewer component with annotation layers\n */\n\n// ============================================================================\n// SECTION 3: COMPONENT DEFINITION\n// ============================================================================\n\nfunction AnnotPdf({\n // Required props\n pdfUrl,\n\n // Optional props with defaults\n page = 1,\n scale = 1.5,\n annotations = [],\n currentTime = 0,\n\n // Callbacks\n onLoad,\n onError,\n onPageChange,\n\n // Styling\n className,\n style,\n canvasStyle\n}) {\n\n // ==========================================================================\n // SECTION 4: REFS INITIALIZATION\n // ==========================================================================\n\n /**\n * Reference to the canvas element for PDF rendering\n * @type {React.RefObject<HTMLCanvasElement>}\n */\n const canvasRef = useRef(null);\n\n /**\n * Reference to the layer container div for annotation layers\n * @type {React.RefObject<HTMLDivElement>}\n */\n const layerContainerRef = useRef(null);\n\n /**\n * Reference to the AnnotationRenderer engine instance\n * Stored in ref to avoid triggering re-renders\n * @type {React.RefObject<AnnotationRenderer|null>}\n */\n const engineRef = useRef(null);\n\n // ==========================================================================\n // SECTION 5: ENGINE INITIALIZATION AND CLEANUP\n // ==========================================================================\n\n /**\n * Initialize AnnotationRenderer on component mount\n * Cleanup on component unmount\n */\n useEffect(() => {\n // Guard: Wait for DOM elements to be ready\n if (!canvasRef.current || !layerContainerRef.current) {\n return;\n }\n\n // Initialize engine\n try {\n engineRef.current = new AnnotationRenderer({\n canvasElement: canvasRef.current,\n container: layerContainerRef.current\n });\n } catch (error) {\n console.error('AnnotPdf: Failed to initialize renderer:', error);\n if (onError) {\n onError(error);\n }\n }\n\n // Cleanup on unmount\n return () => {\n if (engineRef.current) {\n engineRef.current.destroy();\n engineRef.current = null;\n }\n };\n }, []); // Empty deps - run once on mount\n\n // ==========================================================================\n // SECTION 6: PDF LOADING SYNCHRONIZATION\n // ==========================================================================\n\n /**\n * Load PDF document when pdfUrl prop changes\n * Handles async operation with cancellation support\n */\n useEffect(() => {\n // Guard: Engine must exist and pdfUrl must be valid\n if (!engineRef.current || !pdfUrl) {\n return;\n }\n\n let cancelled = false;\n\n const loadPdf = async () => {\n try {\n const result = await engineRef.current.loadPDF(pdfUrl);\n\n // Check if component unmounted during async operation\n if (cancelled) return;\n\n // Check if load was successful\n if (!result.success) {\n console.error('AnnotPdf: Failed to load PDF:', result.error);\n if (onError) {\n onError(new Error(result.error));\n }\n return;\n }\n\n // Call onLoad callback with pageCount from result\n if (onLoad) {\n onLoad({ pageCount: result.pageCount });\n }\n } catch (error) {\n if (cancelled) return;\n\n console.error('AnnotPdf: Failed to load PDF:', error);\n if (onError) {\n onError(error);\n }\n }\n };\n\n loadPdf();\n\n // Cleanup: Prevent state updates if component unmounts during load\n return () => {\n cancelled = true;\n };\n }, [pdfUrl, onLoad, onError]);\n\n // ==========================================================================\n // SECTION 7: PAGE SYNCHRONIZATION\n // ==========================================================================\n\n /**\n * Sync page prop to engine.setPage() method\n */\n useEffect(() => {\n // Guard: Engine must exist and page must be valid\n if (!engineRef.current || !page || typeof page !== 'number') {\n return;\n }\n\n // Sync page to engine\n engineRef.current.setPage(page)\n .then((result) => {\n // Check if page change was successful\n if (!result.success) {\n console.error('AnnotPdf: Failed to set page:', result.error);\n if (onError) {\n onError(new Error(result.error));\n }\n return;\n }\n\n // Optional: Notify parent of successful page change\n if (onPageChange) {\n onPageChange(page);\n }\n })\n .catch((error) => {\n console.error('AnnotPdf: Failed to set page:', error);\n if (onError) {\n onError(error);\n }\n });\n }, [page, onPageChange, onError]);\n\n // ==========================================================================\n // SECTION 8: SCALE SYNCHRONIZATION\n // ==========================================================================\n\n /**\n * Sync scale prop to engine.setScale() method\n */\n useEffect(() => {\n // Guard: Engine must exist and scale must be valid\n if (!engineRef.current || !scale || typeof scale !== 'number') {\n return;\n }\n\n // Sync scale to engine (async method)\n engineRef.current.setScale(scale)\n .then((result) => {\n // Check if scale change was successful\n if (!result.success) {\n console.error('AnnotPdf: Failed to set scale:', result.error);\n if (onError) {\n onError(new Error(result.error));\n }\n }\n })\n .catch((error) => {\n console.error('AnnotPdf: Failed to set scale:', error);\n if (onError) {\n onError(error);\n }\n });\n }, [scale, onError]);\n\n // ==========================================================================\n // SECTION 9: ANNOTATIONS SYNCHRONIZATION\n // ==========================================================================\n\n /**\n * Sync annotations prop to engine.setAnnotations() method\n */\n useEffect(() => {\n // Guard: Engine must exist\n if (!engineRef.current) {\n return;\n }\n\n // Sync annotations to engine (default to empty array)\n try {\n engineRef.current.setAnnotations(annotations || []);\n } catch (error) {\n console.error('AnnotPdf: Failed to set annotations:', error);\n if (onError) {\n onError(error);\n }\n }\n }, [annotations, onError]);\n\n // ==========================================================================\n // SECTION 10: TIMELINE SYNCHRONIZATION\n // ==========================================================================\n\n /**\n * Sync currentTime prop to engine.setTime() method\n */\n useEffect(() => {\n // Guard: Engine must exist and currentTime must be defined\n if (!engineRef.current || currentTime === undefined || currentTime === null) {\n return;\n }\n\n // Sync timeline to engine\n try {\n engineRef.current.setTime(currentTime);\n } catch (error) {\n console.error('AnnotPdf: Failed to set time:', error);\n if (onError) {\n onError(error);\n }\n }\n }, [currentTime, onError]);\n\n // ==========================================================================\n // SECTION 11: STYLING DEFINITIONS\n // ==========================================================================\n\n /**\n * Default container styles\n * Merged with user-provided styles (user styles override defaults)\n */\n const defaultContainerStyle = {\n position: 'relative',\n display: 'inline-block',\n lineHeight: 0, // Remove extra space below canvas\n ...style // User styles override defaults\n };\n\n /**\n * Default layer container styles\n * Positions layer div absolutely over canvas\n */\n const defaultLayerStyle = {\n position: 'absolute',\n top: 0,\n left: 0,\n width: '100%',\n height: '100%',\n pointerEvents: 'none', // Allow clicks to pass through to canvas\n overflow: 'hidden'\n };\n\n /**\n * Default canvas styles\n * Merged with user-provided canvasStyle\n */\n const defaultCanvasStyle = {\n display: 'block',\n ...canvasStyle // User styles override defaults\n };\n\n // ==========================================================================\n // SECTION 12: JSX RETURN\n // ==========================================================================\n\n return (\n <div className={className} style={defaultContainerStyle}>\n <canvas ref={canvasRef} style={defaultCanvasStyle} />\n <div ref={layerContainerRef} style={defaultLayerStyle} />\n </div>\n );\n}\n\n// ============================================================================\n// SECTION 13: EXPORT\n// ============================================================================\n\nexport default AnnotPdf;\n"],"names":["AnnotPdf","pdfUrl","page","scale","annotations","currentTime","onLoad","onError","onPageChange","className","style","canvasStyle","canvasRef","useRef","layerContainerRef","engineRef","useEffect","AnnotationRenderer","error","cancelled","result","defaultContainerStyle","defaultLayerStyle","defaultCanvasStyle","jsxs","jsx"],"mappings":"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"}
1
+ {"version":3,"file":"index14.cjs","sources":["../src/adapters/AnnotPdf.jsx"],"sourcesContent":["// ============================================================================\n// SECTION 1: IMPORTS\n// ============================================================================\n\nimport { useRef, useEffect, useCallback } from 'react';\nimport { AnnotationRenderer } from '../core/AnnotationRenderer.js';\n\n// ============================================================================\n// SECTION 2: JSDOC DOCUMENTATION\n// ============================================================================\n\n/**\n * AnnotPdf - Declarative React component for PDF annotation rendering\n *\n * A React wrapper around the AnnotationRenderer core engine that provides\n * a declarative, props-based API for rendering PDF documents with\n * timeline-synchronized annotations.\n *\n * Features:\n * - Automatic lifecycle management (initialization and cleanup)\n * - Declarative prop-to-method synchronization\n * - PDF rendering with pdf.js\n * - Timeline-synchronized annotation display\n * - Support for highlight, text, and ink annotations\n * - Page navigation and zoom control\n *\n * @component\n * @example\n * // Basic usage\n * <AnnotPdf\n * pdfUrl=\"/document.pdf\"\n * page={1}\n * scale={1.5}\n * annotations={[]}\n * currentTime={0}\n * />\n *\n * @example\n * // With audio synchronization\n * const [currentTime, setCurrentTime] = useState(0);\n *\n * <div>\n * <AnnotPdf\n * pdfUrl=\"/lecture.pdf\"\n * page={1}\n * scale={1.5}\n * annotations={annotations}\n * currentTime={currentTime}\n * onLoad={({ pageCount }) => console.log('Loaded:', pageCount)}\n * />\n * <audio\n * src=\"/lecture.mp3\"\n * onTimeUpdate={(e) => setCurrentTime(e.target.currentTime)}\n * controls\n * />\n * </div>\n *\n * @param {Object} props - Component props\n * @param {string} props.pdfUrl - PDF document URL (required)\n * @param {number} [props.page=1] - Current page number (1-indexed)\n * @param {number} [props.scale=1.5] - Zoom scale factor\n * @param {Array} [props.annotations=[]] - Array of annotation objects\n * @param {number} [props.currentTime=0] - Timeline position in seconds\n * @param {Function} [props.onLoad] - Callback when PDF loads: ({pageCount}) => void\n * @param {Function} [props.onError] - Callback on error: (error) => void\n * @param {Function} [props.onPageChange] - Callback when page changes: (page) => void\n * @param {string} [props.className] - CSS class for container div\n * @param {Object} [props.style] - Inline styles for container div\n * @param {Object} [props.canvasStyle] - Inline styles for canvas element\n * @returns {JSX.Element} PDF viewer component with annotation layers\n */\n\n// ============================================================================\n// SECTION 3: COMPONENT DEFINITION\n// ============================================================================\n\nfunction AnnotPdf({\n // Required props\n pdfUrl,\n\n // Optional props with defaults\n page = 1,\n scale = 1.5,\n annotations = [],\n currentTime = 0,\n\n // Callbacks\n onLoad,\n onError,\n onPageChange,\n\n // Styling\n className,\n style,\n canvasStyle\n}) {\n\n // ==========================================================================\n // SECTION 4: REFS INITIALIZATION\n // ==========================================================================\n\n /**\n * Reference to the canvas element for PDF rendering\n * @type {React.RefObject<HTMLCanvasElement>}\n */\n const canvasRef = useRef(null);\n\n /**\n * Reference to the layer container div for annotation layers\n * @type {React.RefObject<HTMLDivElement>}\n */\n const layerContainerRef = useRef(null);\n\n /**\n * Reference to the AnnotationRenderer engine instance\n * Stored in ref to avoid triggering re-renders\n * @type {React.RefObject<AnnotationRenderer|null>}\n */\n const engineRef = useRef(null);\n\n /**\n * Reference to the render operation queue\n * Ensures sequential execution of async canvas operations\n * Prevents PDF.js race condition: \"Cannot use the same canvas during multiple render() operations\"\n * @type {React.RefObject<Promise<void>>}\n */\n const renderQueue = useRef(Promise.resolve());\n\n // ==========================================================================\n // SECTION 4.5: RENDER QUEUE HELPER\n // ==========================================================================\n\n /**\n * Queue a render operation to execute sequentially\n *\n * This helper ensures that async canvas operations (loadPDF, setPage, setScale)\n * execute one at a time, preventing concurrent access to the PDF.js canvas.\n * Uses Promise chaining to maintain operation order.\n *\n * @param {Function} operation - Async function returning a Promise\n * @returns {void}\n */\n const queueOperation = useCallback((operation) => {\n renderQueue.current = renderQueue.current\n .then(operation)\n .catch(error => {\n // Log errors but don't break the queue\n console.error('AnnotPdf: Queued operation failed:', error);\n });\n }, []);\n\n // ==========================================================================\n // SECTION 5: ENGINE INITIALIZATION AND CLEANUP\n // ==========================================================================\n\n /**\n * Initialize AnnotationRenderer on component mount\n * Cleanup on component unmount\n */\n useEffect(() => {\n // Guard: Wait for DOM elements to be ready\n if (!canvasRef.current || !layerContainerRef.current) {\n return;\n }\n\n // Initialize engine\n try {\n engineRef.current = new AnnotationRenderer({\n canvasElement: canvasRef.current,\n container: layerContainerRef.current\n });\n } catch (error) {\n console.error('AnnotPdf: Failed to initialize renderer:', error);\n if (onError) {\n onError(error);\n }\n }\n\n // Cleanup on unmount\n return () => {\n if (engineRef.current) {\n engineRef.current.destroy();\n engineRef.current = null;\n }\n };\n }, []); // Empty deps - run once on mount\n\n // ==========================================================================\n // SECTION 6: PDF LOADING SYNCHRONIZATION\n // ==========================================================================\n\n /**\n * Load PDF document when pdfUrl prop changes\n * Handles async operation with cancellation support\n * Uses render queue to prevent concurrent canvas operations\n */\n useEffect(() => {\n // Guard: Engine must exist and pdfUrl must be valid\n if (!engineRef.current || !pdfUrl) {\n return;\n }\n\n let cancelled = false;\n\n const loadPdf = async () => {\n try {\n const result = await engineRef.current.loadPDF(pdfUrl);\n\n // Check if component unmounted during async operation\n if (cancelled) return;\n\n // Check if load was successful\n if (!result.success) {\n console.error('AnnotPdf: Failed to load PDF:', result.error);\n if (onError) {\n onError(new Error(result.error));\n }\n return;\n }\n\n // Call onLoad callback with pageCount from result\n if (onLoad) {\n onLoad({ pageCount: result.pageCount });\n }\n } catch (error) {\n if (cancelled) return;\n\n console.error('AnnotPdf: Failed to load PDF:', error);\n if (onError) {\n onError(error);\n }\n }\n };\n\n // Queue the PDF loading operation to prevent race conditions\n queueOperation(loadPdf);\n\n // Cleanup: Prevent state updates if component unmounts during load\n return () => {\n cancelled = true;\n };\n }, [pdfUrl, onLoad, onError, queueOperation]);\n\n // ==========================================================================\n // SECTION 7: PAGE SYNCHRONIZATION\n // ==========================================================================\n\n /**\n * Sync page prop to engine.setPage() method\n * Uses render queue to prevent concurrent canvas operations\n */\n useEffect(() => {\n // Guard: Engine must exist and page must be valid\n if (!engineRef.current || !page || typeof page !== 'number') {\n return;\n }\n\n // Queue the page change operation to prevent race conditions\n queueOperation(async () => {\n try {\n const result = await engineRef.current.setPage(page);\n\n // Check if page change was successful\n if (!result.success) {\n console.error('AnnotPdf: Failed to set page:', result.error);\n if (onError) {\n onError(new Error(result.error));\n }\n return;\n }\n\n // Optional: Notify parent of successful page change\n if (onPageChange) {\n onPageChange(page);\n }\n } catch (error) {\n console.error('AnnotPdf: Failed to set page:', error);\n if (onError) {\n onError(error);\n }\n }\n });\n }, [page, onPageChange, onError, queueOperation]);\n\n // ==========================================================================\n // SECTION 8: SCALE SYNCHRONIZATION\n // ==========================================================================\n\n /**\n * Sync scale prop to engine.setScale() method\n * Uses render queue to prevent concurrent canvas operations\n */\n useEffect(() => {\n // Guard: Engine must exist and scale must be valid\n if (!engineRef.current || !scale || typeof scale !== 'number') {\n return;\n }\n\n // Queue the scale change operation to prevent race conditions\n queueOperation(async () => {\n try {\n const result = await engineRef.current.setScale(scale);\n\n // Check if scale change was successful\n if (!result.success) {\n console.error('AnnotPdf: Failed to set scale:', result.error);\n if (onError) {\n onError(new Error(result.error));\n }\n }\n } catch (error) {\n console.error('AnnotPdf: Failed to set scale:', error);\n if (onError) {\n onError(error);\n }\n }\n });\n }, [scale, onError, queueOperation]);\n\n // ==========================================================================\n // SECTION 9: ANNOTATIONS SYNCHRONIZATION\n // ==========================================================================\n\n /**\n * Sync annotations prop to engine.setAnnotations() method\n */\n useEffect(() => {\n // Guard: Engine must exist\n if (!engineRef.current) {\n return;\n }\n\n // Sync annotations to engine (default to empty array)\n try {\n engineRef.current.setAnnotations(annotations || []);\n } catch (error) {\n console.error('AnnotPdf: Failed to set annotations:', error);\n if (onError) {\n onError(error);\n }\n }\n }, [annotations, onError]);\n\n // ==========================================================================\n // SECTION 10: TIMELINE SYNCHRONIZATION\n // ==========================================================================\n\n /**\n * Sync currentTime prop to engine.setTime() method\n */\n useEffect(() => {\n // Guard: Engine must exist and currentTime must be defined\n if (!engineRef.current || currentTime === undefined || currentTime === null) {\n return;\n }\n\n // Sync timeline to engine\n try {\n engineRef.current.setTime(currentTime);\n } catch (error) {\n console.error('AnnotPdf: Failed to set time:', error);\n if (onError) {\n onError(error);\n }\n }\n }, [currentTime, onError]);\n\n // ==========================================================================\n // SECTION 11: STYLING DEFINITIONS\n // ==========================================================================\n\n /**\n * Default container styles\n * Merged with user-provided styles (user styles override defaults)\n */\n const defaultContainerStyle = {\n position: 'relative',\n display: 'inline-block',\n lineHeight: 0, // Remove extra space below canvas\n ...style // User styles override defaults\n };\n\n /**\n * Default layer container styles\n * Positions layer div absolutely over canvas\n */\n const defaultLayerStyle = {\n position: 'absolute',\n top: 0,\n left: 0,\n width: '100%',\n height: '100%',\n pointerEvents: 'none', // Allow clicks to pass through to canvas\n overflow: 'hidden'\n };\n\n /**\n * Default canvas styles\n * Merged with user-provided canvasStyle\n */\n const defaultCanvasStyle = {\n display: 'block',\n ...canvasStyle // User styles override defaults\n };\n\n // ==========================================================================\n // SECTION 12: JSX RETURN\n // ==========================================================================\n\n return (\n <div className={className} style={defaultContainerStyle}>\n <canvas ref={canvasRef} style={defaultCanvasStyle} />\n <div ref={layerContainerRef} style={defaultLayerStyle} />\n </div>\n );\n}\n\n// ============================================================================\n// SECTION 13: EXPORT\n// ============================================================================\n\nexport default AnnotPdf;\n"],"names":["AnnotPdf","pdfUrl","page","scale","annotations","currentTime","onLoad","onError","onPageChange","className","style","canvasStyle","canvasRef","useRef","layerContainerRef","engineRef","renderQueue","queueOperation","useCallback","operation","error","useEffect","AnnotationRenderer","cancelled","result","defaultContainerStyle","defaultLayerStyle","defaultCanvasStyle","jsxs","jsx"],"mappings":"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"}
package/dist/index14.js CHANGED
@@ -1,97 +1,107 @@
1
- import { jsxs as b, jsx as h } from "react/jsx-runtime";
2
- import { useRef as y, useEffect as i } from "react";
3
- import { AnnotationRenderer as R } from "./index2.js";
4
- function j({
1
+ import { jsxs as S, jsx as F } from "react/jsx-runtime";
2
+ import { useRef as u, useCallback as k, useEffect as i } from "react";
3
+ import { AnnotationRenderer as x } from "./index2.js";
4
+ function q({
5
5
  // Required props
6
- pdfUrl: f,
6
+ pdfUrl: a,
7
7
  // Optional props with defaults
8
- page: c = 1,
9
- scale: l = 1.5,
10
- annotations: P = [],
11
- currentTime: s = 0,
8
+ page: s = 1,
9
+ scale: o = 1.5,
10
+ annotations: A = [],
11
+ currentTime: l = 0,
12
12
  // Callbacks
13
- onLoad: o,
13
+ onLoad: f,
14
14
  onError: t,
15
- onPageChange: u,
15
+ onPageChange: d,
16
16
  // Styling
17
- className: A,
18
- style: F,
19
- canvasStyle: p
17
+ className: v,
18
+ style: m,
19
+ canvasStyle: w
20
20
  }) {
21
- const a = y(null), d = y(null), n = y(null);
21
+ const y = u(null), P = u(null), r = u(null), h = u(Promise.resolve()), c = k((e) => {
22
+ h.current = h.current.then(e).catch((p) => {
23
+ console.error("AnnotPdf: Queued operation failed:", p);
24
+ });
25
+ }, []);
22
26
  i(() => {
23
- if (!(!a.current || !d.current)) {
27
+ if (!(!y.current || !P.current)) {
24
28
  try {
25
- n.current = new R({
26
- canvasElement: a.current,
27
- container: d.current
29
+ r.current = new x({
30
+ canvasElement: y.current,
31
+ container: P.current
28
32
  });
29
33
  } catch (e) {
30
34
  console.error("AnnotPdf: Failed to initialize renderer:", e), t && t(e);
31
35
  }
32
36
  return () => {
33
- n.current && (n.current.destroy(), n.current = null);
37
+ r.current && (r.current.destroy(), r.current = null);
34
38
  };
35
39
  }
36
40
  }, []), i(() => {
37
- if (!n.current || !f)
41
+ if (!r.current || !a)
38
42
  return;
39
43
  let e = !1;
40
- return (async () => {
44
+ return c(async () => {
41
45
  try {
42
- const r = await n.current.loadPDF(f);
46
+ const n = await r.current.loadPDF(a);
43
47
  if (e) return;
44
- if (!r.success) {
45
- console.error("AnnotPdf: Failed to load PDF:", r.error), t && t(new Error(r.error));
48
+ if (!n.success) {
49
+ console.error("AnnotPdf: Failed to load PDF:", n.error), t && t(new Error(n.error));
46
50
  return;
47
51
  }
48
- o && o({ pageCount: r.pageCount });
49
- } catch (r) {
52
+ f && f({ pageCount: n.pageCount });
53
+ } catch (n) {
50
54
  if (e) return;
51
- console.error("AnnotPdf: Failed to load PDF:", r), t && t(r);
55
+ console.error("AnnotPdf: Failed to load PDF:", n), t && t(n);
52
56
  }
53
- })(), () => {
57
+ }), () => {
54
58
  e = !0;
55
59
  };
56
- }, [f, o, t]), i(() => {
57
- !n.current || !c || typeof c != "number" || n.current.setPage(c).then((e) => {
58
- if (!e.success) {
59
- console.error("AnnotPdf: Failed to set page:", e.error), t && t(new Error(e.error));
60
- return;
60
+ }, [a, f, t, c]), i(() => {
61
+ !r.current || !s || typeof s != "number" || c(async () => {
62
+ try {
63
+ const e = await r.current.setPage(s);
64
+ if (!e.success) {
65
+ console.error("AnnotPdf: Failed to set page:", e.error), t && t(new Error(e.error));
66
+ return;
67
+ }
68
+ d && d(s);
69
+ } catch (e) {
70
+ console.error("AnnotPdf: Failed to set page:", e), t && t(e);
61
71
  }
62
- u && u(c);
63
- }).catch((e) => {
64
- console.error("AnnotPdf: Failed to set page:", e), t && t(e);
65
72
  });
66
- }, [c, u, t]), i(() => {
67
- !n.current || !l || typeof l != "number" || n.current.setScale(l).then((e) => {
68
- e.success || (console.error("AnnotPdf: Failed to set scale:", e.error), t && t(new Error(e.error)));
69
- }).catch((e) => {
70
- console.error("AnnotPdf: Failed to set scale:", e), t && t(e);
73
+ }, [s, d, t, c]), i(() => {
74
+ !r.current || !o || typeof o != "number" || c(async () => {
75
+ try {
76
+ const e = await r.current.setScale(o);
77
+ e.success || (console.error("AnnotPdf: Failed to set scale:", e.error), t && t(new Error(e.error)));
78
+ } catch (e) {
79
+ console.error("AnnotPdf: Failed to set scale:", e), t && t(e);
80
+ }
71
81
  });
72
- }, [l, t]), i(() => {
73
- if (n.current)
82
+ }, [o, t, c]), i(() => {
83
+ if (r.current)
74
84
  try {
75
- n.current.setAnnotations(P || []);
85
+ r.current.setAnnotations(A || []);
76
86
  } catch (e) {
77
87
  console.error("AnnotPdf: Failed to set annotations:", e), t && t(e);
78
88
  }
79
- }, [P, t]), i(() => {
80
- if (!(!n.current || s === void 0 || s === null))
89
+ }, [A, t]), i(() => {
90
+ if (!(!r.current || l === void 0 || l === null))
81
91
  try {
82
- n.current.setTime(s);
92
+ r.current.setTime(l);
83
93
  } catch (e) {
84
94
  console.error("AnnotPdf: Failed to set time:", e), t && t(e);
85
95
  }
86
- }, [s, t]);
87
- const v = {
96
+ }, [l, t]);
97
+ const b = {
88
98
  position: "relative",
89
99
  display: "inline-block",
90
100
  lineHeight: 0,
91
101
  // Remove extra space below canvas
92
- ...F
102
+ ...m
93
103
  // User styles override defaults
94
- }, m = {
104
+ }, R = {
95
105
  position: "absolute",
96
106
  top: 0,
97
107
  left: 0,
@@ -100,17 +110,17 @@ function j({
100
110
  pointerEvents: "none",
101
111
  // Allow clicks to pass through to canvas
102
112
  overflow: "hidden"
103
- }, w = {
113
+ }, C = {
104
114
  display: "block",
105
- ...p
115
+ ...w
106
116
  // User styles override defaults
107
117
  };
108
- return /* @__PURE__ */ b("div", { className: A, style: v, children: [
109
- /* @__PURE__ */ h("canvas", { ref: a, style: w }),
110
- /* @__PURE__ */ h("div", { ref: d, style: m })
118
+ return /* @__PURE__ */ S("div", { className: v, style: b, children: [
119
+ /* @__PURE__ */ F("canvas", { ref: y, style: C }),
120
+ /* @__PURE__ */ F("div", { ref: P, style: R })
111
121
  ] });
112
122
  }
113
123
  export {
114
- j as default
124
+ q as default
115
125
  };
116
126
  //# sourceMappingURL=index14.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index14.js","sources":["../src/adapters/AnnotPdf.jsx"],"sourcesContent":["// ============================================================================\n// SECTION 1: IMPORTS\n// ============================================================================\n\nimport { useRef, useEffect } from 'react';\nimport { AnnotationRenderer } from '../core/AnnotationRenderer.js';\n\n// ============================================================================\n// SECTION 2: JSDOC DOCUMENTATION\n// ============================================================================\n\n/**\n * AnnotPdf - Declarative React component for PDF annotation rendering\n *\n * A React wrapper around the AnnotationRenderer core engine that provides\n * a declarative, props-based API for rendering PDF documents with\n * timeline-synchronized annotations.\n *\n * Features:\n * - Automatic lifecycle management (initialization and cleanup)\n * - Declarative prop-to-method synchronization\n * - PDF rendering with pdf.js\n * - Timeline-synchronized annotation display\n * - Support for highlight, text, and ink annotations\n * - Page navigation and zoom control\n *\n * @component\n * @example\n * // Basic usage\n * <AnnotPdf\n * pdfUrl=\"/document.pdf\"\n * page={1}\n * scale={1.5}\n * annotations={[]}\n * currentTime={0}\n * />\n *\n * @example\n * // With audio synchronization\n * const [currentTime, setCurrentTime] = useState(0);\n *\n * <div>\n * <AnnotPdf\n * pdfUrl=\"/lecture.pdf\"\n * page={1}\n * scale={1.5}\n * annotations={annotations}\n * currentTime={currentTime}\n * onLoad={({ pageCount }) => console.log('Loaded:', pageCount)}\n * />\n * <audio\n * src=\"/lecture.mp3\"\n * onTimeUpdate={(e) => setCurrentTime(e.target.currentTime)}\n * controls\n * />\n * </div>\n *\n * @param {Object} props - Component props\n * @param {string} props.pdfUrl - PDF document URL (required)\n * @param {number} [props.page=1] - Current page number (1-indexed)\n * @param {number} [props.scale=1.5] - Zoom scale factor\n * @param {Array} [props.annotations=[]] - Array of annotation objects\n * @param {number} [props.currentTime=0] - Timeline position in seconds\n * @param {Function} [props.onLoad] - Callback when PDF loads: ({pageCount}) => void\n * @param {Function} [props.onError] - Callback on error: (error) => void\n * @param {Function} [props.onPageChange] - Callback when page changes: (page) => void\n * @param {string} [props.className] - CSS class for container div\n * @param {Object} [props.style] - Inline styles for container div\n * @param {Object} [props.canvasStyle] - Inline styles for canvas element\n * @returns {JSX.Element} PDF viewer component with annotation layers\n */\n\n// ============================================================================\n// SECTION 3: COMPONENT DEFINITION\n// ============================================================================\n\nfunction AnnotPdf({\n // Required props\n pdfUrl,\n\n // Optional props with defaults\n page = 1,\n scale = 1.5,\n annotations = [],\n currentTime = 0,\n\n // Callbacks\n onLoad,\n onError,\n onPageChange,\n\n // Styling\n className,\n style,\n canvasStyle\n}) {\n\n // ==========================================================================\n // SECTION 4: REFS INITIALIZATION\n // ==========================================================================\n\n /**\n * Reference to the canvas element for PDF rendering\n * @type {React.RefObject<HTMLCanvasElement>}\n */\n const canvasRef = useRef(null);\n\n /**\n * Reference to the layer container div for annotation layers\n * @type {React.RefObject<HTMLDivElement>}\n */\n const layerContainerRef = useRef(null);\n\n /**\n * Reference to the AnnotationRenderer engine instance\n * Stored in ref to avoid triggering re-renders\n * @type {React.RefObject<AnnotationRenderer|null>}\n */\n const engineRef = useRef(null);\n\n // ==========================================================================\n // SECTION 5: ENGINE INITIALIZATION AND CLEANUP\n // ==========================================================================\n\n /**\n * Initialize AnnotationRenderer on component mount\n * Cleanup on component unmount\n */\n useEffect(() => {\n // Guard: Wait for DOM elements to be ready\n if (!canvasRef.current || !layerContainerRef.current) {\n return;\n }\n\n // Initialize engine\n try {\n engineRef.current = new AnnotationRenderer({\n canvasElement: canvasRef.current,\n container: layerContainerRef.current\n });\n } catch (error) {\n console.error('AnnotPdf: Failed to initialize renderer:', error);\n if (onError) {\n onError(error);\n }\n }\n\n // Cleanup on unmount\n return () => {\n if (engineRef.current) {\n engineRef.current.destroy();\n engineRef.current = null;\n }\n };\n }, []); // Empty deps - run once on mount\n\n // ==========================================================================\n // SECTION 6: PDF LOADING SYNCHRONIZATION\n // ==========================================================================\n\n /**\n * Load PDF document when pdfUrl prop changes\n * Handles async operation with cancellation support\n */\n useEffect(() => {\n // Guard: Engine must exist and pdfUrl must be valid\n if (!engineRef.current || !pdfUrl) {\n return;\n }\n\n let cancelled = false;\n\n const loadPdf = async () => {\n try {\n const result = await engineRef.current.loadPDF(pdfUrl);\n\n // Check if component unmounted during async operation\n if (cancelled) return;\n\n // Check if load was successful\n if (!result.success) {\n console.error('AnnotPdf: Failed to load PDF:', result.error);\n if (onError) {\n onError(new Error(result.error));\n }\n return;\n }\n\n // Call onLoad callback with pageCount from result\n if (onLoad) {\n onLoad({ pageCount: result.pageCount });\n }\n } catch (error) {\n if (cancelled) return;\n\n console.error('AnnotPdf: Failed to load PDF:', error);\n if (onError) {\n onError(error);\n }\n }\n };\n\n loadPdf();\n\n // Cleanup: Prevent state updates if component unmounts during load\n return () => {\n cancelled = true;\n };\n }, [pdfUrl, onLoad, onError]);\n\n // ==========================================================================\n // SECTION 7: PAGE SYNCHRONIZATION\n // ==========================================================================\n\n /**\n * Sync page prop to engine.setPage() method\n */\n useEffect(() => {\n // Guard: Engine must exist and page must be valid\n if (!engineRef.current || !page || typeof page !== 'number') {\n return;\n }\n\n // Sync page to engine\n engineRef.current.setPage(page)\n .then((result) => {\n // Check if page change was successful\n if (!result.success) {\n console.error('AnnotPdf: Failed to set page:', result.error);\n if (onError) {\n onError(new Error(result.error));\n }\n return;\n }\n\n // Optional: Notify parent of successful page change\n if (onPageChange) {\n onPageChange(page);\n }\n })\n .catch((error) => {\n console.error('AnnotPdf: Failed to set page:', error);\n if (onError) {\n onError(error);\n }\n });\n }, [page, onPageChange, onError]);\n\n // ==========================================================================\n // SECTION 8: SCALE SYNCHRONIZATION\n // ==========================================================================\n\n /**\n * Sync scale prop to engine.setScale() method\n */\n useEffect(() => {\n // Guard: Engine must exist and scale must be valid\n if (!engineRef.current || !scale || typeof scale !== 'number') {\n return;\n }\n\n // Sync scale to engine (async method)\n engineRef.current.setScale(scale)\n .then((result) => {\n // Check if scale change was successful\n if (!result.success) {\n console.error('AnnotPdf: Failed to set scale:', result.error);\n if (onError) {\n onError(new Error(result.error));\n }\n }\n })\n .catch((error) => {\n console.error('AnnotPdf: Failed to set scale:', error);\n if (onError) {\n onError(error);\n }\n });\n }, [scale, onError]);\n\n // ==========================================================================\n // SECTION 9: ANNOTATIONS SYNCHRONIZATION\n // ==========================================================================\n\n /**\n * Sync annotations prop to engine.setAnnotations() method\n */\n useEffect(() => {\n // Guard: Engine must exist\n if (!engineRef.current) {\n return;\n }\n\n // Sync annotations to engine (default to empty array)\n try {\n engineRef.current.setAnnotations(annotations || []);\n } catch (error) {\n console.error('AnnotPdf: Failed to set annotations:', error);\n if (onError) {\n onError(error);\n }\n }\n }, [annotations, onError]);\n\n // ==========================================================================\n // SECTION 10: TIMELINE SYNCHRONIZATION\n // ==========================================================================\n\n /**\n * Sync currentTime prop to engine.setTime() method\n */\n useEffect(() => {\n // Guard: Engine must exist and currentTime must be defined\n if (!engineRef.current || currentTime === undefined || currentTime === null) {\n return;\n }\n\n // Sync timeline to engine\n try {\n engineRef.current.setTime(currentTime);\n } catch (error) {\n console.error('AnnotPdf: Failed to set time:', error);\n if (onError) {\n onError(error);\n }\n }\n }, [currentTime, onError]);\n\n // ==========================================================================\n // SECTION 11: STYLING DEFINITIONS\n // ==========================================================================\n\n /**\n * Default container styles\n * Merged with user-provided styles (user styles override defaults)\n */\n const defaultContainerStyle = {\n position: 'relative',\n display: 'inline-block',\n lineHeight: 0, // Remove extra space below canvas\n ...style // User styles override defaults\n };\n\n /**\n * Default layer container styles\n * Positions layer div absolutely over canvas\n */\n const defaultLayerStyle = {\n position: 'absolute',\n top: 0,\n left: 0,\n width: '100%',\n height: '100%',\n pointerEvents: 'none', // Allow clicks to pass through to canvas\n overflow: 'hidden'\n };\n\n /**\n * Default canvas styles\n * Merged with user-provided canvasStyle\n */\n const defaultCanvasStyle = {\n display: 'block',\n ...canvasStyle // User styles override defaults\n };\n\n // ==========================================================================\n // SECTION 12: JSX RETURN\n // ==========================================================================\n\n return (\n <div className={className} style={defaultContainerStyle}>\n <canvas ref={canvasRef} style={defaultCanvasStyle} />\n <div ref={layerContainerRef} style={defaultLayerStyle} />\n </div>\n );\n}\n\n// ============================================================================\n// SECTION 13: EXPORT\n// ============================================================================\n\nexport default AnnotPdf;\n"],"names":["AnnotPdf","pdfUrl","page","scale","annotations","currentTime","onLoad","onError","onPageChange","className","style","canvasStyle","canvasRef","useRef","layerContainerRef","engineRef","useEffect","AnnotationRenderer","error","cancelled","result","defaultContainerStyle","defaultLayerStyle","defaultCanvasStyle","jsxs","jsx"],"mappings":"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"}
1
+ {"version":3,"file":"index14.js","sources":["../src/adapters/AnnotPdf.jsx"],"sourcesContent":["// ============================================================================\n// SECTION 1: IMPORTS\n// ============================================================================\n\nimport { useRef, useEffect, useCallback } from 'react';\nimport { AnnotationRenderer } from '../core/AnnotationRenderer.js';\n\n// ============================================================================\n// SECTION 2: JSDOC DOCUMENTATION\n// ============================================================================\n\n/**\n * AnnotPdf - Declarative React component for PDF annotation rendering\n *\n * A React wrapper around the AnnotationRenderer core engine that provides\n * a declarative, props-based API for rendering PDF documents with\n * timeline-synchronized annotations.\n *\n * Features:\n * - Automatic lifecycle management (initialization and cleanup)\n * - Declarative prop-to-method synchronization\n * - PDF rendering with pdf.js\n * - Timeline-synchronized annotation display\n * - Support for highlight, text, and ink annotations\n * - Page navigation and zoom control\n *\n * @component\n * @example\n * // Basic usage\n * <AnnotPdf\n * pdfUrl=\"/document.pdf\"\n * page={1}\n * scale={1.5}\n * annotations={[]}\n * currentTime={0}\n * />\n *\n * @example\n * // With audio synchronization\n * const [currentTime, setCurrentTime] = useState(0);\n *\n * <div>\n * <AnnotPdf\n * pdfUrl=\"/lecture.pdf\"\n * page={1}\n * scale={1.5}\n * annotations={annotations}\n * currentTime={currentTime}\n * onLoad={({ pageCount }) => console.log('Loaded:', pageCount)}\n * />\n * <audio\n * src=\"/lecture.mp3\"\n * onTimeUpdate={(e) => setCurrentTime(e.target.currentTime)}\n * controls\n * />\n * </div>\n *\n * @param {Object} props - Component props\n * @param {string} props.pdfUrl - PDF document URL (required)\n * @param {number} [props.page=1] - Current page number (1-indexed)\n * @param {number} [props.scale=1.5] - Zoom scale factor\n * @param {Array} [props.annotations=[]] - Array of annotation objects\n * @param {number} [props.currentTime=0] - Timeline position in seconds\n * @param {Function} [props.onLoad] - Callback when PDF loads: ({pageCount}) => void\n * @param {Function} [props.onError] - Callback on error: (error) => void\n * @param {Function} [props.onPageChange] - Callback when page changes: (page) => void\n * @param {string} [props.className] - CSS class for container div\n * @param {Object} [props.style] - Inline styles for container div\n * @param {Object} [props.canvasStyle] - Inline styles for canvas element\n * @returns {JSX.Element} PDF viewer component with annotation layers\n */\n\n// ============================================================================\n// SECTION 3: COMPONENT DEFINITION\n// ============================================================================\n\nfunction AnnotPdf({\n // Required props\n pdfUrl,\n\n // Optional props with defaults\n page = 1,\n scale = 1.5,\n annotations = [],\n currentTime = 0,\n\n // Callbacks\n onLoad,\n onError,\n onPageChange,\n\n // Styling\n className,\n style,\n canvasStyle\n}) {\n\n // ==========================================================================\n // SECTION 4: REFS INITIALIZATION\n // ==========================================================================\n\n /**\n * Reference to the canvas element for PDF rendering\n * @type {React.RefObject<HTMLCanvasElement>}\n */\n const canvasRef = useRef(null);\n\n /**\n * Reference to the layer container div for annotation layers\n * @type {React.RefObject<HTMLDivElement>}\n */\n const layerContainerRef = useRef(null);\n\n /**\n * Reference to the AnnotationRenderer engine instance\n * Stored in ref to avoid triggering re-renders\n * @type {React.RefObject<AnnotationRenderer|null>}\n */\n const engineRef = useRef(null);\n\n /**\n * Reference to the render operation queue\n * Ensures sequential execution of async canvas operations\n * Prevents PDF.js race condition: \"Cannot use the same canvas during multiple render() operations\"\n * @type {React.RefObject<Promise<void>>}\n */\n const renderQueue = useRef(Promise.resolve());\n\n // ==========================================================================\n // SECTION 4.5: RENDER QUEUE HELPER\n // ==========================================================================\n\n /**\n * Queue a render operation to execute sequentially\n *\n * This helper ensures that async canvas operations (loadPDF, setPage, setScale)\n * execute one at a time, preventing concurrent access to the PDF.js canvas.\n * Uses Promise chaining to maintain operation order.\n *\n * @param {Function} operation - Async function returning a Promise\n * @returns {void}\n */\n const queueOperation = useCallback((operation) => {\n renderQueue.current = renderQueue.current\n .then(operation)\n .catch(error => {\n // Log errors but don't break the queue\n console.error('AnnotPdf: Queued operation failed:', error);\n });\n }, []);\n\n // ==========================================================================\n // SECTION 5: ENGINE INITIALIZATION AND CLEANUP\n // ==========================================================================\n\n /**\n * Initialize AnnotationRenderer on component mount\n * Cleanup on component unmount\n */\n useEffect(() => {\n // Guard: Wait for DOM elements to be ready\n if (!canvasRef.current || !layerContainerRef.current) {\n return;\n }\n\n // Initialize engine\n try {\n engineRef.current = new AnnotationRenderer({\n canvasElement: canvasRef.current,\n container: layerContainerRef.current\n });\n } catch (error) {\n console.error('AnnotPdf: Failed to initialize renderer:', error);\n if (onError) {\n onError(error);\n }\n }\n\n // Cleanup on unmount\n return () => {\n if (engineRef.current) {\n engineRef.current.destroy();\n engineRef.current = null;\n }\n };\n }, []); // Empty deps - run once on mount\n\n // ==========================================================================\n // SECTION 6: PDF LOADING SYNCHRONIZATION\n // ==========================================================================\n\n /**\n * Load PDF document when pdfUrl prop changes\n * Handles async operation with cancellation support\n * Uses render queue to prevent concurrent canvas operations\n */\n useEffect(() => {\n // Guard: Engine must exist and pdfUrl must be valid\n if (!engineRef.current || !pdfUrl) {\n return;\n }\n\n let cancelled = false;\n\n const loadPdf = async () => {\n try {\n const result = await engineRef.current.loadPDF(pdfUrl);\n\n // Check if component unmounted during async operation\n if (cancelled) return;\n\n // Check if load was successful\n if (!result.success) {\n console.error('AnnotPdf: Failed to load PDF:', result.error);\n if (onError) {\n onError(new Error(result.error));\n }\n return;\n }\n\n // Call onLoad callback with pageCount from result\n if (onLoad) {\n onLoad({ pageCount: result.pageCount });\n }\n } catch (error) {\n if (cancelled) return;\n\n console.error('AnnotPdf: Failed to load PDF:', error);\n if (onError) {\n onError(error);\n }\n }\n };\n\n // Queue the PDF loading operation to prevent race conditions\n queueOperation(loadPdf);\n\n // Cleanup: Prevent state updates if component unmounts during load\n return () => {\n cancelled = true;\n };\n }, [pdfUrl, onLoad, onError, queueOperation]);\n\n // ==========================================================================\n // SECTION 7: PAGE SYNCHRONIZATION\n // ==========================================================================\n\n /**\n * Sync page prop to engine.setPage() method\n * Uses render queue to prevent concurrent canvas operations\n */\n useEffect(() => {\n // Guard: Engine must exist and page must be valid\n if (!engineRef.current || !page || typeof page !== 'number') {\n return;\n }\n\n // Queue the page change operation to prevent race conditions\n queueOperation(async () => {\n try {\n const result = await engineRef.current.setPage(page);\n\n // Check if page change was successful\n if (!result.success) {\n console.error('AnnotPdf: Failed to set page:', result.error);\n if (onError) {\n onError(new Error(result.error));\n }\n return;\n }\n\n // Optional: Notify parent of successful page change\n if (onPageChange) {\n onPageChange(page);\n }\n } catch (error) {\n console.error('AnnotPdf: Failed to set page:', error);\n if (onError) {\n onError(error);\n }\n }\n });\n }, [page, onPageChange, onError, queueOperation]);\n\n // ==========================================================================\n // SECTION 8: SCALE SYNCHRONIZATION\n // ==========================================================================\n\n /**\n * Sync scale prop to engine.setScale() method\n * Uses render queue to prevent concurrent canvas operations\n */\n useEffect(() => {\n // Guard: Engine must exist and scale must be valid\n if (!engineRef.current || !scale || typeof scale !== 'number') {\n return;\n }\n\n // Queue the scale change operation to prevent race conditions\n queueOperation(async () => {\n try {\n const result = await engineRef.current.setScale(scale);\n\n // Check if scale change was successful\n if (!result.success) {\n console.error('AnnotPdf: Failed to set scale:', result.error);\n if (onError) {\n onError(new Error(result.error));\n }\n }\n } catch (error) {\n console.error('AnnotPdf: Failed to set scale:', error);\n if (onError) {\n onError(error);\n }\n }\n });\n }, [scale, onError, queueOperation]);\n\n // ==========================================================================\n // SECTION 9: ANNOTATIONS SYNCHRONIZATION\n // ==========================================================================\n\n /**\n * Sync annotations prop to engine.setAnnotations() method\n */\n useEffect(() => {\n // Guard: Engine must exist\n if (!engineRef.current) {\n return;\n }\n\n // Sync annotations to engine (default to empty array)\n try {\n engineRef.current.setAnnotations(annotations || []);\n } catch (error) {\n console.error('AnnotPdf: Failed to set annotations:', error);\n if (onError) {\n onError(error);\n }\n }\n }, [annotations, onError]);\n\n // ==========================================================================\n // SECTION 10: TIMELINE SYNCHRONIZATION\n // ==========================================================================\n\n /**\n * Sync currentTime prop to engine.setTime() method\n */\n useEffect(() => {\n // Guard: Engine must exist and currentTime must be defined\n if (!engineRef.current || currentTime === undefined || currentTime === null) {\n return;\n }\n\n // Sync timeline to engine\n try {\n engineRef.current.setTime(currentTime);\n } catch (error) {\n console.error('AnnotPdf: Failed to set time:', error);\n if (onError) {\n onError(error);\n }\n }\n }, [currentTime, onError]);\n\n // ==========================================================================\n // SECTION 11: STYLING DEFINITIONS\n // ==========================================================================\n\n /**\n * Default container styles\n * Merged with user-provided styles (user styles override defaults)\n */\n const defaultContainerStyle = {\n position: 'relative',\n display: 'inline-block',\n lineHeight: 0, // Remove extra space below canvas\n ...style // User styles override defaults\n };\n\n /**\n * Default layer container styles\n * Positions layer div absolutely over canvas\n */\n const defaultLayerStyle = {\n position: 'absolute',\n top: 0,\n left: 0,\n width: '100%',\n height: '100%',\n pointerEvents: 'none', // Allow clicks to pass through to canvas\n overflow: 'hidden'\n };\n\n /**\n * Default canvas styles\n * Merged with user-provided canvasStyle\n */\n const defaultCanvasStyle = {\n display: 'block',\n ...canvasStyle // User styles override defaults\n };\n\n // ==========================================================================\n // SECTION 12: JSX RETURN\n // ==========================================================================\n\n return (\n <div className={className} style={defaultContainerStyle}>\n <canvas ref={canvasRef} style={defaultCanvasStyle} />\n <div ref={layerContainerRef} style={defaultLayerStyle} />\n </div>\n );\n}\n\n// ============================================================================\n// SECTION 13: EXPORT\n// ============================================================================\n\nexport default AnnotPdf;\n"],"names":["AnnotPdf","pdfUrl","page","scale","annotations","currentTime","onLoad","onError","onPageChange","className","style","canvasStyle","canvasRef","useRef","layerContainerRef","engineRef","renderQueue","queueOperation","useCallback","operation","error","useEffect","AnnotationRenderer","cancelled","result","defaultContainerStyle","defaultLayerStyle","defaultCanvasStyle","jsxs","jsx"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "web-annotation-renderer",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "type": "module",
5
5
  "description": "Framework-agnostic PDF annotation renderer with timeline synchronization for educational content and interactive documents",
6
6
  "keywords": [