react-pdf-levelup 2.0.20 → 2.0.25

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/dist/index.mjs CHANGED
@@ -71,7 +71,7 @@ var LayoutPDF_default = LayoutPDF;
71
71
 
72
72
  // src/components/core/Img.tsx
73
73
  import React2 from "react";
74
- import { Image as Image2, StyleSheet as StyleSheet2 } from "@react-pdf/renderer";
74
+ import { Image, StyleSheet as StyleSheet2 } from "@react-pdf/renderer";
75
75
  var styles2 = StyleSheet2.create({
76
76
  image: {
77
77
  width: "100%",
@@ -80,7 +80,7 @@ var styles2 = StyleSheet2.create({
80
80
  }
81
81
  });
82
82
  var Img = ({ src, style }) => {
83
- return /* @__PURE__ */ React2.createElement(Image2, { src, style: [styles2.image, style] });
83
+ return /* @__PURE__ */ React2.createElement(Image, { src, style: [styles2.image, style] });
84
84
  };
85
85
  var Img_default = Img;
86
86
 
@@ -237,81 +237,100 @@ var styles5 = StyleSheet5.create({
237
237
  width: "100%",
238
238
  borderWidth: 1,
239
239
  borderColor: "#000",
240
- overflow: "hidden",
241
240
  marginBottom: 20
242
241
  },
243
242
  thead: {
244
- backgroundColor: "#f0f0f0"
243
+ backgroundColor: "#ccc"
245
244
  },
246
- tbody: {},
247
245
  tr: {
248
- flexDirection: "row",
249
- borderBottomWidth: 1,
250
- borderColor: "#000"
246
+ flexDirection: "row"
251
247
  },
252
- th: {
253
- paddingTop: 4,
248
+ textBold: {
254
249
  fontSize: 10,
255
250
  fontFamily: "Helvetica",
256
251
  fontWeight: "bold",
257
- borderRight: 1,
258
- borderColor: "#000",
259
- textAlign: "center"
252
+ textAlign: "center",
253
+ paddingTop: 4
260
254
  },
261
- td: {
262
- paddingTop: 4,
263
- paddingLeft: 8,
264
- paddingRight: 8,
255
+ text: {
265
256
  fontSize: 10,
266
257
  fontFamily: "Helvetica",
267
- borderRight: 1,
268
- borderColor: "#000"
269
- // borderTopWidth: 0,
270
- },
271
- cellSmall: {
272
- width: "25%"
273
- },
274
- cellMedium: {
275
- width: "33.33%"
258
+ paddingTop: 4,
259
+ paddingLeft: 8,
260
+ paddingRight: 8
276
261
  },
277
- cellLarge: {
278
- width: "50%"
262
+ zebraOdd: {
263
+ backgroundColor: "#eeeeee"
279
264
  }
280
265
  });
281
- var cellSizeMapping = {
282
- small: styles5.cellSmall,
283
- medium: styles5.cellMedium,
284
- large: styles5.cellLarge
285
- };
286
- var Table = ({ children, style }) => {
287
- return /* @__PURE__ */ React5.createElement(View2, { style: [styles5.table, style] }, children);
288
- };
289
- var Thead = ({ children, style }) => {
290
- return /* @__PURE__ */ React5.createElement(View2, { style: [styles5.thead, style] }, children);
291
- };
266
+ var Table = ({ children, style }) => /* @__PURE__ */ React5.createElement(View2, { style: [styles5.table, style] }, children);
267
+ var Thead = ({ children, style }) => /* @__PURE__ */ React5.createElement(View2, { style: [styles5.thead, style] }, children);
292
268
  var Tbody = ({ children, style }) => {
293
- return /* @__PURE__ */ React5.createElement(View2, { style: [styles5.tbody, style] }, children);
294
- };
295
- var Tr = ({ children, style }) => {
296
- return /* @__PURE__ */ React5.createElement(View2, { style: [styles5.tr, style] }, children);
297
- };
298
- var Th = ({ children, style, cellSize = "medium", width, height, colSpan }) => {
299
- const spanWidth = colSpan ? `${100 / 3 * colSpan}%` : void 0;
300
- const sizeStyle = cellSizeMapping[cellSize];
301
- const customStyle = {
302
- width: width || spanWidth || sizeStyle?.width,
269
+ const rows = React5.Children.toArray(children);
270
+ const count = rows.length;
271
+ return /* @__PURE__ */ React5.createElement(React5.Fragment, null, rows.map(
272
+ (row, idx) => React5.cloneElement(row, {
273
+ isLastRow: idx === count - 1,
274
+ isOdd: idx % 2 === 1
275
+ })
276
+ ));
277
+ };
278
+ var Tr = ({
279
+ children,
280
+ style,
281
+ isLastRow = false,
282
+ isOdd = false
283
+ }) => {
284
+ const elements = React5.Children.toArray(children);
285
+ const count = elements.length;
286
+ return /* @__PURE__ */ React5.createElement(View2, { style: [styles5.tr, style] }, elements.map((child, idx) => {
287
+ const isLast = idx === count - 1;
288
+ const width = `${(100 / count).toFixed(2)}%`;
289
+ return React5.cloneElement(child, { width, isLast, isLastRow, isOdd });
290
+ }));
291
+ };
292
+ var Th = ({
293
+ children,
294
+ style,
295
+ width,
296
+ height,
297
+ colSpan,
298
+ isLast = false,
299
+ isLastRow = false
300
+ }) => {
301
+ const baseWidth = typeof width === "string" && colSpan ? `${(parseFloat(width) * colSpan).toFixed(2)}%` : width;
302
+ const borders = {
303
+ borderRightWidth: isLast ? 0 : 1,
304
+ borderBottomWidth: isLastRow ? 0 : 1,
305
+ borderColor: "#000",
303
306
  ...height !== void 0 && { height }
304
307
  };
305
- return /* @__PURE__ */ React5.createElement(View2, { style: [styles5.th, customStyle, style] }, /* @__PURE__ */ React5.createElement(Text3, null, children));
308
+ return /* @__PURE__ */ React5.createElement(View2, { style: [styles5.textBold, { width: baseWidth }, borders, style] }, /* @__PURE__ */ React5.createElement(Text3, null, children));
306
309
  };
307
- var Td = ({ children, style, cellSize = "medium", width, height, colSpan }) => {
308
- const spanWidth = colSpan ? `${100 / 3 * colSpan}%` : void 0;
309
- const sizeStyle = cellSizeMapping[cellSize];
310
- const customStyle = {
311
- width: width || spanWidth || sizeStyle?.width,
310
+ var Td = ({
311
+ children,
312
+ style,
313
+ width,
314
+ height,
315
+ colSpan,
316
+ isLast = false,
317
+ isLastRow = false,
318
+ isOdd = false
319
+ }) => {
320
+ const baseWidth = typeof width === "string" && colSpan ? `${(parseFloat(width) * colSpan).toFixed(2)}%` : width;
321
+ const borders = {
322
+ borderRightWidth: isLast ? 0 : 1,
323
+ borderBottomWidth: isLastRow ? 0 : 1,
324
+ borderColor: "#000",
312
325
  ...height !== void 0 && { height }
313
326
  };
314
- return /* @__PURE__ */ React5.createElement(View2, { style: [styles5.td, customStyle, style] }, /* @__PURE__ */ React5.createElement(Text3, null, children));
327
+ return /* @__PURE__ */ React5.createElement(View2, { style: [
328
+ styles5.text,
329
+ isOdd && styles5.zebraOdd,
330
+ { width: baseWidth },
331
+ borders,
332
+ style
333
+ ] }, /* @__PURE__ */ React5.createElement(Text3, null, children));
315
334
  };
316
335
 
317
336
  // src/components/core/Grid.tsx
@@ -419,155 +438,197 @@ var Footer = ({ children, style, fixed = false }) => {
419
438
  };
420
439
 
421
440
  // src/components/core/QR.tsx
422
- import React8 from "react";
423
- import { Image as Image3, StyleSheet as StyleSheet8, View as View5 } from "@react-pdf/renderer";
424
- import { useEffect, useState } from "react";
425
-
426
- // src/components/core/QRGenerator.tsx
427
- import QRCode from "qrcode";
428
- var generateQRAsBase64 = async ({
429
- value,
430
- size = 150,
431
- colorDark = "#282828",
432
- colorLight = "#ffffff",
433
- margin = 0,
434
- errorCorrectionLevel = "M"
435
- }) => {
441
+ import React8, { useEffect, useState } from "react";
442
+ import { Image as Image2, View as View5, Text as Text5 } from "@react-pdf/renderer";
443
+ import QRCodeStyling from "qr-code-styling";
444
+ if (typeof window === "undefined" && typeof process !== "undefined") {
436
445
  try {
437
- const options = {
438
- errorCorrectionLevel,
439
- type: "image/png",
440
- quality: 0.92,
441
- margin,
442
- color: {
443
- dark: colorDark,
444
- light: colorLight
445
- },
446
- width: size
447
- };
448
- const qrDataUrl = QRCode.toDataURL(value, options);
449
- return qrDataUrl;
446
+ import("jsdom").then((jsdom) => {
447
+ const { JSDOM } = jsdom;
448
+ import("canvas").then((canvas) => {
449
+ const { Canvas, Image: CanvasImage } = canvas;
450
+ const { window: domWindow } = new JSDOM("<!DOCTYPE html><html><body></body></html>");
451
+ global.window = domWindow;
452
+ global.document = domWindow.document;
453
+ global.HTMLElement = domWindow.HTMLElement;
454
+ global.HTMLCanvasElement = Canvas;
455
+ global.CanvasRenderingContext2D = Canvas;
456
+ global.Image = CanvasImage;
457
+ }).catch((err) => {
458
+ console.error("Error loading canvas:", err);
459
+ });
460
+ }).catch((err) => {
461
+ console.error("Error loading jsdom:", err);
462
+ });
450
463
  } catch (error) {
451
- console.error("Error generando QR:", error);
452
- return "";
453
- }
454
- };
455
- var addLogoToQR = async (qrDataUrl, logoUrl, logoWidth, logoHeight) => {
456
- return new Promise((resolve) => {
457
- if (!qrDataUrl || !logoUrl) {
458
- resolve(qrDataUrl);
459
- return;
460
- }
461
- try {
462
- const canvas = document.createElement("canvas");
463
- const ctx = canvas.getContext("2d");
464
- if (!ctx) {
465
- resolve(qrDataUrl);
466
- return;
467
- }
468
- const qrImage = new Image();
469
- qrImage.crossOrigin = "anonymous";
470
- qrImage.onload = () => {
471
- canvas.width = qrImage.width;
472
- canvas.height = qrImage.height;
473
- ctx.drawImage(qrImage, 0, 0, canvas.width, canvas.height);
474
- const logoImage = new Image();
475
- logoImage.crossOrigin = "anonymous";
476
- logoImage.onload = () => {
477
- const x = (canvas.width - logoWidth) / 2;
478
- const y = (canvas.height - logoHeight) / 2;
479
- ctx.fillStyle = "#FFFFFF";
480
- ctx.fillRect(x - 5, y - 5, logoWidth + 10, logoHeight + 10);
481
- ctx.drawImage(logoImage, x, y, logoWidth, logoHeight);
482
- const finalQrDataUrl = canvas.toDataURL("image/png");
483
- resolve(finalQrDataUrl);
484
- };
485
- logoImage.onerror = () => {
486
- console.error("Error cargando el logo");
487
- resolve(qrDataUrl);
488
- };
489
- logoImage.src = logoUrl;
490
- };
491
- qrImage.onerror = () => {
492
- console.error("Error cargando el QR");
493
- resolve("");
494
- };
495
- qrImage.src = qrDataUrl;
496
- } catch (error) {
497
- console.error("Error procesando el QR con logo:", error);
498
- resolve(qrDataUrl);
499
- }
500
- });
501
- };
502
-
503
- // src/components/core/QR.tsx
504
- var styles8 = StyleSheet8.create({
505
- qrContainer: {
506
- display: "flex",
507
- alignItems: "center",
508
- justifyContent: "center",
509
- margin: 10
464
+ console.error("Error setting up Node.js environment:", error);
510
465
  }
511
- });
512
- var errorLevelMap = {
513
- 0: "L",
514
- 1: "M",
515
- 2: "Q",
516
- 3: "H"
517
- };
466
+ }
518
467
  var QR = ({
519
- value,
520
- size = 150,
521
- style,
522
- colorDark = "#000000",
523
- colorLight = "#ffffff",
524
- margin = 0,
525
- logo = "",
468
+ url,
469
+ size = 200,
470
+ colorData = "#000000",
471
+ colorDataBG = "#ffffff",
472
+ logo,
526
473
  logoWidth = 30,
527
- logoHeight = 30,
528
- errorCorrectionLevel = "M"
474
+ logoHeight,
475
+ margin = 0,
476
+ errorCorrectionLevel = "H",
477
+ style,
478
+ dotType = "square",
479
+ cornerSquareType = "square",
480
+ cornerDotType = "square",
481
+ cornerSquareColor,
482
+ cornerDotColor,
483
+ logoBG = colorDataBG,
484
+ logoText,
485
+ moveText = 0,
486
+ textColor = colorData,
487
+ fontSize = 12,
488
+ fontFamily = "Helvetica",
489
+ textBackgroundColor = colorDataBG,
490
+ textPadding = 1,
491
+ textBold = true
529
492
  }) => {
530
- const [qrDataUrl, setQrDataUrl] = useState("");
493
+ const [qrDataURL, setQrDataURL] = useState(null);
494
+ const actualLogoWidth = logoWidth || Math.floor(size * 0.2);
495
+ const actualLogoHeight = logoHeight || actualLogoWidth;
531
496
  useEffect(() => {
532
- const generateQR = async () => {
497
+ if (typeof window === "undefined") {
498
+ return;
499
+ }
500
+ const generateQRCode = async () => {
533
501
  try {
534
- const baseQrDataUrl = await generateQRAsBase64({
535
- value,
536
- size,
537
- colorDark,
538
- colorLight,
539
- margin,
540
- errorCorrectionLevel: typeof errorCorrectionLevel === "number" ? errorLevelMap[errorCorrectionLevel] || "M" : errorCorrectionLevel
502
+ const qrCode = new QRCodeStyling({
503
+ width: size,
504
+ height: size,
505
+ type: "canvas",
506
+ data: url,
507
+ dotsOptions: {
508
+ color: colorData,
509
+ type: dotType
510
+ },
511
+ cornersSquareOptions: {
512
+ color: cornerSquareColor || colorData,
513
+ type: cornerSquareType
514
+ },
515
+ cornersDotOptions: {
516
+ color: cornerDotColor || colorData,
517
+ type: cornerDotType
518
+ },
519
+ backgroundOptions: {
520
+ color: colorDataBG
521
+ },
522
+ qrOptions: {
523
+ errorCorrectionLevel
524
+ },
525
+ margin
541
526
  });
542
- if (logo && logoWidth && logoHeight) {
543
- const qrWithLogo = await addLogoToQR(baseQrDataUrl, logo, logoWidth, logoHeight);
544
- setQrDataUrl(qrWithLogo);
545
- } else {
546
- setQrDataUrl(baseQrDataUrl);
547
- }
527
+ const container = document.createElement("div");
528
+ container.style.position = "absolute";
529
+ container.style.top = "-9999px";
530
+ container.style.left = "-9999px";
531
+ document.body.appendChild(container);
532
+ qrCode.append(container);
533
+ setTimeout(() => {
534
+ try {
535
+ const qrCanvas = container.querySelector("canvas");
536
+ if (qrCanvas) {
537
+ const canvas = document.createElement("canvas");
538
+ canvas.width = size;
539
+ canvas.height = size;
540
+ const ctx = canvas.getContext("2d");
541
+ if (ctx) {
542
+ ctx.drawImage(qrCanvas, 0, 0);
543
+ const dataURL = canvas.toDataURL("image/png");
544
+ setQrDataURL(dataURL);
545
+ }
546
+ }
547
+ document.body.removeChild(container);
548
+ } catch (error) {
549
+ console.error("Error capturando QR:", error);
550
+ }
551
+ }, 100);
548
552
  } catch (error) {
549
553
  console.error("Error generando QR:", error);
550
- const fallbackUrl2 = `https://api.qrserver.com/v1/create-qr-code/?data=${encodeURIComponent(
551
- value
552
- )}&size=${size}x${size}&color=${encodeURIComponent(colorDark.replace("#", ""))}&bgcolor=${encodeURIComponent(
553
- colorLight.replace("#", "")
554
- )}`;
555
- setQrDataUrl(fallbackUrl2);
556
554
  }
557
555
  };
558
- generateQR();
559
- }, [value, size, colorDark, colorLight, margin, logo, logoWidth, logoHeight, errorCorrectionLevel]);
560
- const fallbackUrl = `https://api.qrserver.com/v1/create-qr-code/?data=${encodeURIComponent(
561
- value
562
- )}&size=${size}x${size}`;
563
- return /* @__PURE__ */ React8.createElement(View5, { style: [styles8.qrContainer, style] }, /* @__PURE__ */ React8.createElement(Image3, { src: qrDataUrl || fallbackUrl, style: { width: size, height: size } }));
556
+ generateQRCode();
557
+ }, [
558
+ url,
559
+ size,
560
+ colorData,
561
+ colorDataBG,
562
+ margin,
563
+ errorCorrectionLevel,
564
+ dotType,
565
+ cornerSquareType,
566
+ cornerDotType,
567
+ cornerSquareColor,
568
+ cornerDotColor,
569
+ logoBG
570
+ ]);
571
+ if (!qrDataURL) return null;
572
+ const centerPosition = size / 2;
573
+ const logoContainerSize = Math.max(actualLogoWidth, actualLogoHeight) + 10;
574
+ return /* @__PURE__ */ React8.createElement(View5, { style: { width: size, height: size, position: "relative", ...style } }, /* @__PURE__ */ React8.createElement(Image2, { src: qrDataURL, style: { width: size, height: size } }), logo && /* @__PURE__ */ React8.createElement(
575
+ View5,
576
+ {
577
+ style: {
578
+ position: "absolute",
579
+ width: logoContainerSize,
580
+ height: logoContainerSize,
581
+ backgroundColor: logoBG,
582
+ left: centerPosition - logoContainerSize / 2,
583
+ top: centerPosition - logoContainerSize / 2,
584
+ borderRadius: 100,
585
+ border: `5px solid ${colorData}`,
586
+ padding: 0
587
+ }
588
+ },
589
+ /* @__PURE__ */ React8.createElement(
590
+ Image2,
591
+ {
592
+ src: logo,
593
+ style: {
594
+ objectFit: "contain",
595
+ width: actualLogoWidth,
596
+ height: actualLogoHeight
597
+ }
598
+ }
599
+ )
600
+ ), !logo && logoText && /* @__PURE__ */ React8.createElement(
601
+ View5,
602
+ {
603
+ style: {
604
+ position: "absolute",
605
+ backgroundColor: textBackgroundColor,
606
+ padding: textPadding,
607
+ borderRadius: 4,
608
+ left: moveText + centerPosition - 20,
609
+ top: centerPosition - 10
610
+ }
611
+ },
612
+ /* @__PURE__ */ React8.createElement(
613
+ Text5,
614
+ {
615
+ style: {
616
+ color: textColor,
617
+ fontSize,
618
+ fontFamily,
619
+ fontWeight: textBold ? "bold" : "normal"
620
+ }
621
+ },
622
+ logoText
623
+ )
624
+ ));
564
625
  };
565
626
  var QR_default = QR;
566
627
 
567
628
  // src/components/core/Lista.tsx
568
629
  import React9 from "react";
569
- import { View as View6, Text as Text5, StyleSheet as StyleSheet9 } from "@react-pdf/renderer";
570
- var styles9 = StyleSheet9.create({
630
+ import { View as View6, Text as Text6, StyleSheet as StyleSheet8 } from "@react-pdf/renderer";
631
+ var styles8 = StyleSheet8.create({
571
632
  ul: {
572
633
  marginBottom: 10,
573
634
  paddingLeft: 15
@@ -637,7 +698,7 @@ var UL = ({ children, style, type = "disc" }) => {
637
698
  }
638
699
  return child;
639
700
  });
640
- return /* @__PURE__ */ React9.createElement(View6, { style: [styles9.ul, style] }, childrenWithBullets);
701
+ return /* @__PURE__ */ React9.createElement(View6, { style: [styles8.ul, style] }, childrenWithBullets);
641
702
  };
642
703
  var OL = ({ children, style, type = "decimal", start = 1 }) => {
643
704
  const childrenWithNumbers = React9.Children.map(children, (child, index) => {
@@ -651,7 +712,7 @@ var OL = ({ children, style, type = "decimal", start = 1 }) => {
651
712
  }
652
713
  return child;
653
714
  });
654
- return /* @__PURE__ */ React9.createElement(View6, { style: [styles9.ol, style] }, childrenWithNumbers);
715
+ return /* @__PURE__ */ React9.createElement(View6, { style: [styles8.ol, style] }, childrenWithNumbers);
655
716
  };
656
717
  var LI = ({ children, style, bulletType = "disc", isOrdered = false, index = 1, start = 1, value }) => {
657
718
  let marker;
@@ -661,11 +722,11 @@ var LI = ({ children, style, bulletType = "disc", isOrdered = false, index = 1,
661
722
  } else {
662
723
  marker = getBulletPoint(bulletType);
663
724
  }
664
- return /* @__PURE__ */ React9.createElement(View6, { style: [styles9.li, style] }, /* @__PURE__ */ React9.createElement(Text5, { style: styles9.bulletPoint }, marker), /* @__PURE__ */ React9.createElement(View6, { style: styles9.itemContent }, typeof children === "string" ? /* @__PURE__ */ React9.createElement(Text5, null, children) : children));
725
+ return /* @__PURE__ */ React9.createElement(View6, { style: [styles8.li, style] }, /* @__PURE__ */ React9.createElement(Text6, { style: styles8.bulletPoint }, marker), /* @__PURE__ */ React9.createElement(View6, { style: styles8.itemContent }, typeof children === "string" ? /* @__PURE__ */ React9.createElement(Text6, null, children) : children));
665
726
  };
666
727
 
667
728
  // src/components/core/index.tsx
668
- import { View as View7, Text as Text6, StyleSheet as StyleSheet10, Font } from "@react-pdf/renderer";
729
+ import { View as View7, Text as Text7, StyleSheet as StyleSheet9, Font } from "@react-pdf/renderer";
669
730
 
670
731
  // src/functions/decodeBase64Pdf.ts
671
732
  var decodeBase64Pdf = (base64, fileName) => {
@@ -757,11 +818,11 @@ export {
757
818
  Small,
758
819
  Span,
759
820
  Strong,
760
- StyleSheet10 as StyleSheet,
821
+ StyleSheet9 as StyleSheet,
761
822
  Table,
762
823
  Tbody,
763
824
  Td,
764
- Text6 as Text,
825
+ Text7 as Text,
765
826
  Th,
766
827
  Thead,
767
828
  Tr,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-pdf-levelup",
3
- "version": "2.0.20",
3
+ "version": "2.0.25",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "files": [
@@ -18,16 +18,20 @@
18
18
  "publicar": "npm run upVersion && npm run update-jsx && npm run build-lib && npm publish",
19
19
  "demo": "ts-node ./src/useExample/index.ts"
20
20
  },
21
+ "peerDependencies": {
22
+ "@react-pdf/renderer": "^4.3.0",
23
+ "qr-code-styling": "^1.9.2",
24
+ "react": "^18",
25
+ "react-dom": "^18"
26
+ },
21
27
  "dependencies": {
22
28
  "@babel/standalone": "^7.23.10",
23
29
  "@monaco-editor/react": "^4.7.0",
24
- "@react-pdf/renderer": "^4.3.0",
25
- "easyqrcodejs": "^4.6.2",
30
+ "canvas": "^3.1.0",
31
+ "i": "^0.3.7",
32
+ "jsdom": "^26.1.0",
26
33
  "lucide-react": "^0.485.0",
27
- "qrcode": "^1.5.4",
28
- "react": "^18.2.0",
29
- "react-dom": "^18.2.0",
30
- "react-pdf-levelup": "^2.0.18",
34
+ "npm": "^11.3.0",
31
35
  "react-router-dom": "^7.4.1",
32
36
  "rimraf": "^6.0.1",
33
37
  "ts-node": "^10.9.2"
@@ -35,7 +39,7 @@
35
39
  "devDependencies": {
36
40
  "@eslint/js": "^9.21.0",
37
41
  "@react-pdf/types": "^2.9.0",
38
- "@types/qrcode": "^1.5.5",
42
+ "@types/jsdom": "^21.1.7",
39
43
  "@types/react": "^18.2.56",
40
44
  "@types/react-dom": "^18.2.19",
41
45
  "@vitejs/plugin-react": "^4.3.4",
@@ -44,6 +48,7 @@
44
48
  "eslint-plugin-react-refresh": "^0.4.19",
45
49
  "globals": "^15.15.0",
46
50
  "json": "^11.0.0",
51
+ "react-pdf-levelup": "^2.0.18",
47
52
  "tsup": "^8.4.0",
48
53
  "typescript": "~5.7.2",
49
54
  "typescript-eslint": "^8.24.1",