@files-preview-app/preview-file 1.2.1 → 1.2.3

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/angular.cjs CHANGED
@@ -4,11 +4,11 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var core = require('@angular/core');
6
6
  var common = require('@angular/common');
7
- var DOMPurify5 = require('dompurify');
7
+ var DOMPurify2 = require('dompurify');
8
8
  var docx = require('docx-preview');
9
+ var fflate = require('fflate');
9
10
  var XLSX = require('xlsx');
10
11
  var hljs = require('highlight.js');
11
- var fflate = require('fflate');
12
12
  var marked = require('marked');
13
13
  var pptxBrowser = require('pptx-browser');
14
14
  var THREE = require('three');
@@ -37,7 +37,7 @@ function _interopNamespace(e) {
37
37
  return Object.freeze(n);
38
38
  }
39
39
 
40
- var DOMPurify5__default = /*#__PURE__*/_interopDefault(DOMPurify5);
40
+ var DOMPurify2__default = /*#__PURE__*/_interopDefault(DOMPurify2);
41
41
  var docx__namespace = /*#__PURE__*/_interopNamespace(docx);
42
42
  var XLSX__namespace = /*#__PURE__*/_interopNamespace(XLSX);
43
43
  var hljs__default = /*#__PURE__*/_interopDefault(hljs);
@@ -418,7 +418,7 @@ async function sourceToArrayBuffer(source, signal) {
418
418
  return { buffer, metadata };
419
419
  }
420
420
  function sanitizeSVG(svg) {
421
- return DOMPurify5__default.default.sanitize(svg, {
421
+ return DOMPurify2__default.default.sanitize(svg, {
422
422
  USE_PROFILES: { svg: true, svgFilters: true }
423
423
  });
424
424
  }
@@ -478,6 +478,9 @@ var ICON_THUMBNAILS = `<svg width="24" height="24" viewBox="0 0 24 24" fill="non
478
478
  var ICON_CLOSE = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>`;
479
479
  var ICON_FULLSCREEN = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"></path></svg>`;
480
480
  var ICON_COPY = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>`;
481
+ var ICON_FAST_FORWARD = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="13 19 22 12 13 5 13 19"></polygon><polygon points="2 19 11 12 2 5 2 19"></polygon></svg>`;
482
+ var ICON_REWIND = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="11 19 2 12 11 5 11 19"></polygon><polygon points="22 19 13 12 22 5 22 19"></polygon></svg>`;
483
+ var ICON_SPEED = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg>`;
481
484
  var ICON_MAP = {
482
485
  "zoom-in": ICON_ZOOM_IN,
483
486
  "zoom-out": ICON_ZOOM_OUT,
@@ -494,7 +497,16 @@ var ICON_MAP = {
494
497
  "close": ICON_CLOSE,
495
498
  "copy": ICON_COPY,
496
499
  "prev": ICON_PAGE_PREV,
497
- "next": ICON_PAGE_NEXT
500
+ "next": ICON_PAGE_NEXT,
501
+ "page-prev": ICON_PAGE_PREV,
502
+ "page-next": ICON_PAGE_NEXT,
503
+ "chevron-left": ICON_PAGE_PREV,
504
+ "chevron-right": ICON_PAGE_NEXT,
505
+ "fast-forward": ICON_FAST_FORWARD,
506
+ "forward-10": ICON_FAST_FORWARD,
507
+ "rewind": ICON_REWIND,
508
+ "replay-10": ICON_REWIND,
509
+ "speed": ICON_SPEED
498
510
  };
499
511
  var ToolbarController = class {
500
512
  el;
@@ -526,7 +538,9 @@ var ToolbarController = class {
526
538
  view: [],
527
539
  actions: []
528
540
  };
529
- for (const action of this.actions) {
541
+ const hasPageNav = this.actions.some((a) => a.type === "page-nav");
542
+ const effectiveActions = hasPageNav ? this.actions.filter((a) => a.id !== "page-prev" && a.id !== "page-next" && a.id !== "prev" && a.id !== "next") : this.actions;
543
+ for (const action of effectiveActions) {
530
544
  if (groups[action.group]) {
531
545
  groups[action.group].push(action);
532
546
  }
@@ -549,13 +563,26 @@ var ToolbarController = class {
549
563
  "prev",
550
564
  ICON_PAGE_PREV,
551
565
  "Previous Page",
552
- () => action.execute("prev")
566
+ () => {
567
+ const cur = parseInt(input.value, 10) || 1;
568
+ if (cur > 1) {
569
+ input.value = (cur - 1).toString();
570
+ action.execute("prev", cur - 1);
571
+ }
572
+ }
553
573
  );
554
574
  const nextBtn = this.createButton(
555
575
  "next",
556
576
  ICON_PAGE_NEXT,
557
577
  "Next Page",
558
- () => action.execute("next")
578
+ () => {
579
+ const cur = parseInt(input.value, 10) || 1;
580
+ const max = action.max ?? 1;
581
+ if (cur < max) {
582
+ input.value = (cur + 1).toString();
583
+ action.execute("next", cur + 1);
584
+ }
585
+ }
559
586
  );
560
587
  const input = createElement("input", {
561
588
  className: "fp-toolbar-input",
@@ -565,7 +592,10 @@ var ToolbarController = class {
565
592
  max: (action.max ?? 1).toString()
566
593
  });
567
594
  input.addEventListener("change", () => {
568
- action.execute("go", parseInt(input.value, 10));
595
+ const val = parseInt(input.value, 10);
596
+ if (!isNaN(val)) {
597
+ action.execute("go", val);
598
+ }
569
599
  });
570
600
  const label = createElement("span", { className: "fp-toolbar-label" }, ` / ${action.max ?? 1}`);
571
601
  groupEl.appendChild(prevBtn);
@@ -606,11 +636,11 @@ var ToolbarController = class {
606
636
  type: "button",
607
637
  "data-action-id": id
608
638
  });
609
- const svg = ICON_MAP[iconHtml] || iconHtml;
610
- if (svg && svg.startsWith("<svg")) {
639
+ const svg = ICON_MAP[iconHtml] || ICON_MAP[id] || (iconHtml && iconHtml.startsWith("<svg") ? iconHtml : null);
640
+ if (svg) {
611
641
  btn.innerHTML = sanitizeSVG(svg);
612
642
  } else {
613
- btn.textContent = svg || title;
643
+ btn.textContent = title || id;
614
644
  }
615
645
  btn.addEventListener("click", onClick);
616
646
  return btn;
@@ -1353,12 +1383,12 @@ function pdfPlugin() {
1353
1383
  }
1354
1384
 
1355
1385
  // ../plugins/media/dist/index.js
1356
- var IMAGE_EXTS = [".jpg", ".jpeg", ".png", ".gif", ".webp", ".svg"];
1357
- var VIDEO_EXTS = [".mp4", ".webm", ".ogg"];
1358
- var AUDIO_EXTS = [".mp3", ".wav", ".ogg"];
1386
+ var IMAGE_EXTS = [".jpg", ".jpeg", ".png", ".gif", ".webp", ".svg", ".bmp", ".ico", ".tiff", ".tif", ".avif"];
1387
+ var VIDEO_EXTS = [".mp4", ".m4v", ".webm", ".ogv", ".ogg", ".mov", ".avi", ".mkv", ".flv", ".wmv", ".3gp", ".mpg", ".mpeg"];
1388
+ var AUDIO_EXTS = [".mp3", ".wav", ".ogg", ".flac", ".aac", ".m4a", ".wma", ".opus", ".weba"];
1359
1389
  var MediaPlugin = class {
1360
1390
  id = "media";
1361
- name = "Media Preview";
1391
+ name = "Media Preview (Image, Video, Audio)";
1362
1392
  extensions = [...IMAGE_EXTS, ...VIDEO_EXTS, ...AUDIO_EXTS];
1363
1393
  mimeTypes = [
1364
1394
  "image/jpeg",
@@ -1366,12 +1396,29 @@ var MediaPlugin = class {
1366
1396
  "image/gif",
1367
1397
  "image/webp",
1368
1398
  "image/svg+xml",
1399
+ "image/bmp",
1400
+ "image/x-icon",
1401
+ "image/tiff",
1402
+ "image/avif",
1369
1403
  "video/mp4",
1370
1404
  "video/webm",
1371
1405
  "video/ogg",
1406
+ "video/quicktime",
1407
+ "video/x-msvideo",
1408
+ "video/x-matroska",
1409
+ "video/x-flv",
1410
+ "video/x-ms-wmv",
1411
+ "video/3gpp",
1412
+ "video/mpeg",
1372
1413
  "audio/mpeg",
1373
1414
  "audio/wav",
1374
- "audio/ogg"
1415
+ "audio/ogg",
1416
+ "audio/flac",
1417
+ "audio/aac",
1418
+ "audio/mp4",
1419
+ "audio/x-ms-wma",
1420
+ "audio/opus",
1421
+ "audio/webm"
1375
1422
  ];
1376
1423
  weight = 90;
1377
1424
  supports(file) {
@@ -1427,6 +1474,16 @@ var MediaPlugin = class {
1427
1474
  }
1428
1475
  if (instance.play) {
1429
1476
  actions.push(
1477
+ {
1478
+ id: "replay-10",
1479
+ icon: "replay-10",
1480
+ label: "Rewind 10s",
1481
+ type: "button",
1482
+ group: "actions",
1483
+ execute: () => {
1484
+ instance.rewind?.(10);
1485
+ }
1486
+ },
1430
1487
  {
1431
1488
  id: "play",
1432
1489
  icon: "play",
@@ -1446,6 +1503,26 @@ var MediaPlugin = class {
1446
1503
  execute: () => {
1447
1504
  instance.pause?.();
1448
1505
  }
1506
+ },
1507
+ {
1508
+ id: "forward-10",
1509
+ icon: "forward-10",
1510
+ label: "Fast Forward 10s",
1511
+ type: "button",
1512
+ group: "actions",
1513
+ execute: () => {
1514
+ instance.fastForward?.(10);
1515
+ }
1516
+ },
1517
+ {
1518
+ id: "speed",
1519
+ icon: "speed",
1520
+ label: "Playback Speed",
1521
+ type: "button",
1522
+ group: "actions",
1523
+ execute: () => {
1524
+ instance.cycleSpeed?.();
1525
+ }
1449
1526
  }
1450
1527
  );
1451
1528
  }
@@ -1504,8 +1581,12 @@ var MediaPlugin = class {
1504
1581
  const video = document.createElement("video");
1505
1582
  video.src = url;
1506
1583
  video.controls = true;
1507
- video.style.maxWidth = "100%";
1508
- video.style.maxHeight = "100%";
1584
+ video.playsInline = true;
1585
+ video.style.maxWidth = "90%";
1586
+ video.style.maxHeight = "90%";
1587
+ video.style.borderRadius = "8px";
1588
+ video.style.boxShadow = "0 8px 30px rgba(0,0,0,0.3)";
1589
+ video.style.backgroundColor = "#000000";
1509
1590
  element = video;
1510
1591
  mediaElement = video;
1511
1592
  } else if (isAudio) {
@@ -1564,6 +1645,28 @@ var MediaPlugin = class {
1564
1645
  play: mediaElement ? () => mediaElement?.play() : void 0,
1565
1646
  pause: mediaElement ? () => mediaElement?.pause() : void 0,
1566
1647
  isPlaying: mediaElement ? () => !mediaElement?.paused : void 0,
1648
+ fastForward: mediaElement ? (seconds = 10) => {
1649
+ if (mediaElement) {
1650
+ mediaElement.currentTime = Math.min(mediaElement.duration || Infinity, mediaElement.currentTime + seconds);
1651
+ }
1652
+ } : void 0,
1653
+ rewind: mediaElement ? (seconds = 10) => {
1654
+ if (mediaElement) {
1655
+ mediaElement.currentTime = Math.max(0, mediaElement.currentTime - seconds);
1656
+ }
1657
+ } : void 0,
1658
+ cycleSpeed: mediaElement ? () => {
1659
+ if (mediaElement) {
1660
+ const speeds = [1, 1.25, 1.5, 2, 0.5];
1661
+ const curIndex = speeds.indexOf(mediaElement.playbackRate);
1662
+ const nextIndex = curIndex === -1 ? 0 : (curIndex + 1) % speeds.length;
1663
+ mediaElement.playbackRate = speeds[nextIndex];
1664
+ }
1665
+ } : void 0,
1666
+ setPlaybackRate: mediaElement ? (rate) => {
1667
+ if (mediaElement) mediaElement.playbackRate = rate;
1668
+ } : void 0,
1669
+ getPlaybackRate: mediaElement ? () => mediaElement?.playbackRate ?? 1 : void 0,
1567
1670
  download: () => {
1568
1671
  const a = document.createElement("a");
1569
1672
  a.href = url;
@@ -1581,7 +1684,7 @@ function mediaPlugin() {
1581
1684
  }
1582
1685
  var DocxPlugin = class {
1583
1686
  id = "docx";
1584
- name = "Word Document Preview";
1687
+ name = "Word Document Preview (.docx, .docm, .dotx, .dotm)";
1585
1688
  extensions = [".docx", ".docm", ".dotx", ".dotm"];
1586
1689
  mimeTypes = [
1587
1690
  "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
@@ -1603,9 +1706,7 @@ var DocxPlugin = class {
1603
1706
  label: "Zoom Out",
1604
1707
  type: "button",
1605
1708
  group: "zoom",
1606
- execute: () => {
1607
- instance.zoomOut?.();
1608
- }
1709
+ execute: () => instance.zoomOut?.()
1609
1710
  },
1610
1711
  {
1611
1712
  id: "zoom-in",
@@ -1613,9 +1714,7 @@ var DocxPlugin = class {
1613
1714
  label: "Zoom In",
1614
1715
  type: "button",
1615
1716
  group: "zoom",
1616
- execute: () => {
1617
- instance.zoomIn?.();
1618
- }
1717
+ execute: () => instance.zoomIn?.()
1619
1718
  },
1620
1719
  {
1621
1720
  id: "fit-page",
@@ -1623,9 +1722,7 @@ var DocxPlugin = class {
1623
1722
  label: "Fit to Page",
1624
1723
  type: "button",
1625
1724
  group: "zoom",
1626
- execute: () => {
1627
- instance.fitToPage?.();
1628
- }
1725
+ execute: () => instance.fitToPage?.()
1629
1726
  },
1630
1727
  {
1631
1728
  id: "download",
@@ -1633,9 +1730,7 @@ var DocxPlugin = class {
1633
1730
  label: "Download",
1634
1731
  type: "button",
1635
1732
  group: "actions",
1636
- execute: () => {
1637
- instance.download?.();
1638
- }
1733
+ execute: () => instance.download?.()
1639
1734
  },
1640
1735
  {
1641
1736
  id: "print",
@@ -1643,9 +1738,7 @@ var DocxPlugin = class {
1643
1738
  label: "Print",
1644
1739
  type: "button",
1645
1740
  group: "actions",
1646
- execute: () => {
1647
- instance.print?.();
1648
- }
1741
+ execute: () => instance.print?.()
1649
1742
  }
1650
1743
  ];
1651
1744
  }
@@ -1654,27 +1747,81 @@ var DocxPlugin = class {
1654
1747
  wrapper.className = "fp-docx-wrapper";
1655
1748
  wrapper.style.transformOrigin = "top center";
1656
1749
  wrapper.style.transition = "transform 0.2s ease";
1657
- wrapper.style.padding = "16px";
1750
+ wrapper.style.padding = "24px 16px";
1751
+ wrapper.style.maxWidth = "950px";
1752
+ wrapper.style.margin = "0 auto";
1753
+ wrapper.style.boxSizing = "border-box";
1658
1754
  ctx.container.style.overflow = "auto";
1755
+ ctx.container.style.backgroundColor = "#f1f5f9";
1659
1756
  ctx.container.appendChild(wrapper);
1757
+ const styleOverride = document.createElement("style");
1758
+ styleOverride.textContent = `
1759
+ .fp-docx-wrapper .docx-wrapper {
1760
+ background: transparent !important;
1761
+ padding: 0 !important;
1762
+ display: flex !important;
1763
+ flex-direction: column !important;
1764
+ align-items: center !important;
1765
+ box-sizing: border-box !important;
1766
+ }
1767
+ .fp-docx-wrapper section.docx {
1768
+ box-shadow: 0 4px 24px rgba(0, 0, 0, 0.08) !important;
1769
+ border-radius: 4px !important;
1770
+ margin-bottom: 24px !important;
1771
+ background-color: #ffffff !important;
1772
+ }
1773
+ `;
1774
+ ctx.container.appendChild(styleOverride);
1660
1775
  let scale = 1;
1776
+ let renderedSuccessfully = false;
1777
+ const createdBlobUrls = [];
1661
1778
  try {
1662
- await docx__namespace.renderAsync(ctx.buffer, wrapper, ctx.container, {
1779
+ await docx__namespace.renderAsync(ctx.buffer, wrapper, wrapper, {
1663
1780
  inWrapper: true,
1664
1781
  ignoreWidth: false,
1665
- ignoreHeight: false
1782
+ ignoreHeight: false,
1783
+ ignoreFonts: true,
1784
+ // Avoid crashes on embedded obfuscated fonts
1785
+ breakPages: true,
1786
+ experimental: true
1666
1787
  });
1667
- } catch {
1668
- wrapper.innerHTML = `
1669
- <div style="text-align:center; padding: 40px; color: #666;">
1670
- <div style="font-size:48px; margin-bottom: 16px;">\u{1F4C4}</div>
1671
- <h3>${ctx.metadata.name || "Word Document"}</h3>
1672
- <p>DOCX render preview</p>
1673
- </div>
1674
- `;
1788
+ if (!ctx.container.contains(wrapper)) {
1789
+ ctx.container.appendChild(wrapper);
1790
+ }
1791
+ if (wrapper.children.length > 0 && (wrapper.textContent?.trim().length ?? 0) > 0) {
1792
+ renderedSuccessfully = true;
1793
+ }
1794
+ } catch (err) {
1795
+ console.warn("[DocxPlugin] docx-preview failed, triggering native fallback:", err);
1796
+ }
1797
+ if (!renderedSuccessfully) {
1798
+ try {
1799
+ wrapper.innerHTML = "";
1800
+ this.renderXmlFallback(ctx, wrapper, createdBlobUrls);
1801
+ if (!ctx.container.contains(wrapper)) {
1802
+ ctx.container.appendChild(wrapper);
1803
+ }
1804
+ renderedSuccessfully = true;
1805
+ } catch (fallbackErr) {
1806
+ console.error("[DocxPlugin] Native fallback failed:", fallbackErr);
1807
+ wrapper.innerHTML = `
1808
+ <div style="text-align:center; padding: 48px; background: #fff; border-radius: 8px; box-shadow: 0 4px 20px rgba(0,0,0,0.06);">
1809
+ <div style="font-size:48px; margin-bottom: 16px;">\u{1F4C4}</div>
1810
+ <h3 style="margin: 0 0 8px; color: #1e293b;">${ctx.metadata.name || "Word Document"}</h3>
1811
+ <p style="color: #64748b; margin: 0;">Could not parse document content</p>
1812
+ </div>
1813
+ `;
1814
+ if (!ctx.container.contains(wrapper)) {
1815
+ ctx.container.appendChild(wrapper);
1816
+ }
1817
+ }
1675
1818
  }
1676
1819
  const cleanup = () => {
1820
+ for (const url of createdBlobUrls) {
1821
+ URL.revokeObjectURL(url);
1822
+ }
1677
1823
  wrapper.remove();
1824
+ styleOverride.remove();
1678
1825
  ctx.container.innerHTML = "";
1679
1826
  };
1680
1827
  ctx.signal.addEventListener("abort", cleanup);
@@ -1695,7 +1842,7 @@ var DocxPlugin = class {
1695
1842
  },
1696
1843
  fitToPage: () => {
1697
1844
  scale = 1;
1698
- wrapper.style.transform = `scale(1)`;
1845
+ wrapper.style.transform = "scale(1)";
1699
1846
  },
1700
1847
  download: () => {
1701
1848
  const blob = new Blob([ctx.buffer], { type: this.mimeTypes[0] });
@@ -1711,6 +1858,212 @@ var DocxPlugin = class {
1711
1858
  }
1712
1859
  };
1713
1860
  }
1861
+ renderXmlFallback(ctx, wrapper, createdBlobUrls) {
1862
+ const magic = new Uint8Array(ctx.buffer.slice(0, 8));
1863
+ if (magic[0] === 208 && magic[1] === 207 && magic[2] === 17 && magic[3] === 224) {
1864
+ this.renderBinaryDocFallback(ctx, wrapper);
1865
+ return;
1866
+ }
1867
+ const unzipped = fflate.unzipSync(new Uint8Array(ctx.buffer));
1868
+ const docKey = Object.keys(unzipped).find((k) => k.replace(/^[./\\]+/, "").toLowerCase() === "word/document.xml");
1869
+ const docXmlEntry = docKey ? unzipped[docKey] : void 0;
1870
+ if (!docXmlEntry) throw new Error("Missing word/document.xml in DOCX package");
1871
+ const xmlStr = fflate.strFromU8(docXmlEntry);
1872
+ const parser = new DOMParser();
1873
+ const doc = parser.parseFromString(xmlStr, "application/xml");
1874
+ const imageMap = {};
1875
+ const relsKey = Object.keys(unzipped).find((k) => k.replace(/^[./\\]+/, "").toLowerCase() === "word/_rels/document.xml.rels");
1876
+ if (relsKey && unzipped[relsKey]) {
1877
+ try {
1878
+ const relsXml = fflate.strFromU8(unzipped[relsKey]);
1879
+ const relsDoc = parser.parseFromString(relsXml, "application/xml");
1880
+ const relEls = Array.from(relsDoc.getElementsByTagName("Relationship"));
1881
+ for (const rel of relEls) {
1882
+ const rId = rel.getAttribute("Id");
1883
+ const target = rel.getAttribute("Target");
1884
+ if (rId && target) {
1885
+ const cleanTarget = target.replace(/^\.\.\//, "").replace(/^\//, "");
1886
+ const fullTargetKey = Object.keys(unzipped).find((k) => {
1887
+ const norm = k.replace(/^[./\\]+/, "").toLowerCase();
1888
+ return norm === ("word/" + cleanTarget).toLowerCase() || norm === cleanTarget.toLowerCase();
1889
+ });
1890
+ if (fullTargetKey && unzipped[fullTargetKey]) {
1891
+ const bytes = unzipped[fullTargetKey];
1892
+ const ext = cleanTarget.split(".").pop()?.toLowerCase() || "png";
1893
+ const mime = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : ext === "gif" ? "image/gif" : ext === "svg" ? "image/svg+xml" : "image/png";
1894
+ const blob = new Blob([bytes], { type: mime });
1895
+ const blobUrl = URL.createObjectURL(blob);
1896
+ imageMap[rId] = blobUrl;
1897
+ createdBlobUrls.push(blobUrl);
1898
+ }
1899
+ }
1900
+ }
1901
+ } catch (relsErr) {
1902
+ console.warn("[DocxPlugin] Error parsing relationships:", relsErr);
1903
+ }
1904
+ }
1905
+ const card = document.createElement("div");
1906
+ card.className = "fp-docx-page-card";
1907
+ card.style.backgroundColor = "#ffffff";
1908
+ card.style.borderRadius = "6px";
1909
+ card.style.boxShadow = "0 4px 24px rgba(0,0,0,0.08)";
1910
+ card.style.padding = "48px 56px";
1911
+ card.style.fontFamily = 'Calibri, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
1912
+ card.style.color = "#1e293b";
1913
+ card.style.lineHeight = "1.6";
1914
+ const body = doc.getElementsByTagNameNS("*", "body")[0] || doc.documentElement;
1915
+ let html = "";
1916
+ for (const child of Array.from(body.children)) {
1917
+ const tag = child.localName || child.nodeName.split(":").pop();
1918
+ if (tag === "p") {
1919
+ const pStyle = child.getElementsByTagNameNS("*", "pStyle")[0];
1920
+ const styleVal = pStyle?.getAttribute("w:val") || pStyle?.getAttribute("val") || "";
1921
+ const numPr = child.getElementsByTagNameNS("*", "numPr")[0];
1922
+ const textContent = this.extractParagraphHtml(child, imageMap);
1923
+ if (!textContent.trim()) {
1924
+ html += '<div style="height: 10px;"></div>';
1925
+ continue;
1926
+ }
1927
+ const lowerStyle = styleVal.toLowerCase();
1928
+ if (lowerStyle.includes("title")) {
1929
+ html += `<h1 style="font-size: 28px; font-weight: 700; color: #1e3a8a; margin: 24px 0 12px; border-bottom: 2px solid #e2e8f0; padding-bottom: 8px;">${textContent}</h1>`;
1930
+ } else if (lowerStyle.includes("heading1") || styleVal === "1") {
1931
+ html += `<h2 style="font-size: 22px; font-weight: 700; color: #1e40af; margin: 20px 0 10px;">${textContent}</h2>`;
1932
+ } else if (lowerStyle.includes("heading2") || styleVal === "2") {
1933
+ html += `<h3 style="font-size: 18px; font-weight: 600; color: #2563eb; margin: 16px 0 8px;">${textContent}</h3>`;
1934
+ } else if (lowerStyle.includes("heading3") || styleVal === "3") {
1935
+ html += `<h4 style="font-size: 15px; font-weight: 600; color: #334155; margin: 12px 0 6px;">${textContent}</h4>`;
1936
+ } else if (numPr) {
1937
+ html += `<div style="display: flex; gap: 8px; margin: 4px 0 4px 20px;"><span style="color: #2563eb; font-weight: bold;">\u2022</span><span>${textContent}</span></div>`;
1938
+ } else {
1939
+ html += `<p style="margin: 8px 0; font-size: 14px;">${textContent}</p>`;
1940
+ }
1941
+ } else if (tag === "tbl") {
1942
+ html += '<table style="width: 100%; border-collapse: collapse; margin: 20px 0; border: 1px solid #cbd5e1;">';
1943
+ const rows = Array.from(child.getElementsByTagNameNS("*", "tr"));
1944
+ rows.forEach((tr, rIdx) => {
1945
+ html += `<tr style="${rIdx === 0 ? "background-color: #f8fafc; font-weight: 600;" : ""}">`;
1946
+ const cells = Array.from(tr.getElementsByTagNameNS("*", "tc"));
1947
+ cells.forEach((tc) => {
1948
+ const cellText = this.extractParagraphHtml(tc, imageMap);
1949
+ html += `<td style="border: 1px solid #cbd5e1; padding: 8px 12px; font-size: 13px;">${cellText || "&nbsp;"}</td>`;
1950
+ });
1951
+ html += "</tr>";
1952
+ });
1953
+ html += "</table>";
1954
+ }
1955
+ }
1956
+ card.innerHTML = DOMPurify2__default.default.sanitize(html, {
1957
+ ADD_TAGS: ["h1", "h2", "h3", "h4", "p", "table", "tr", "td", "span", "b", "i", "u", "s", "strike", "img", "div", "br"],
1958
+ ADD_ATTR: ["style", "src", "alt", "colspan", "rowspan"]
1959
+ });
1960
+ wrapper.appendChild(card);
1961
+ }
1962
+ extractParagraphHtml(pElement, imageMap = {}) {
1963
+ let result = "";
1964
+ const drawings = Array.from(pElement.getElementsByTagNameNS("*", "drawing"));
1965
+ for (const drawing of drawings) {
1966
+ const blip = drawing.getElementsByTagNameNS("*", "blip")[0];
1967
+ const rId = blip?.getAttribute("r:embed") || blip?.getAttribute("r:id");
1968
+ if (rId && imageMap[rId]) {
1969
+ result += `<div style="text-align:center; margin: 12px 0;"><img src="${imageMap[rId]}" style="max-width: 100%; height: auto; border-radius: 4px;" /></div>`;
1970
+ }
1971
+ }
1972
+ const runs = Array.from(pElement.getElementsByTagNameNS("*", "r"));
1973
+ if (runs.length === 0 && !result) {
1974
+ return DOMPurify2__default.default.sanitize(pElement.textContent || "");
1975
+ }
1976
+ for (const r of runs) {
1977
+ const blip = r.getElementsByTagNameNS("*", "blip")[0] || r.getElementsByTagNameNS("*", "imagedata")[0];
1978
+ const rId = blip?.getAttribute("r:embed") || blip?.getAttribute("r:id");
1979
+ if (rId && imageMap[rId]) {
1980
+ result += `<img src="${imageMap[rId]}" style="max-width: 100%; height: auto; display: inline-block; margin: 4px;" />`;
1981
+ }
1982
+ const brs = r.getElementsByTagNameNS("*", "br");
1983
+ for (let i = 0; i < brs.length; i++) {
1984
+ result += "<br/>";
1985
+ }
1986
+ const tabs = r.getElementsByTagNameNS("*", "tab");
1987
+ if (tabs.length > 0) {
1988
+ result += "&emsp;";
1989
+ }
1990
+ const rPr = r.getElementsByTagNameNS("*", "rPr")[0];
1991
+ const isBold = !!rPr?.getElementsByTagNameNS("*", "b")[0];
1992
+ const isItalic = !!rPr?.getElementsByTagNameNS("*", "i")[0];
1993
+ const isUnderline = !!rPr?.getElementsByTagNameNS("*", "u")[0];
1994
+ const isStrike = !!rPr?.getElementsByTagNameNS("*", "strike")[0];
1995
+ const colorNode = rPr?.getElementsByTagNameNS("*", "color")[0];
1996
+ const colorVal = colorNode?.getAttribute("w:val") || colorNode?.getAttribute("val");
1997
+ const szNode = rPr?.getElementsByTagNameNS("*", "sz")[0];
1998
+ const szVal = szNode?.getAttribute("w:val") || szNode?.getAttribute("val");
1999
+ const texts = Array.from(r.getElementsByTagNameNS("*", "t"));
2000
+ let text = texts.map((t) => t.textContent || "").join("");
2001
+ if (!text) continue;
2002
+ text = DOMPurify2__default.default.sanitize(text);
2003
+ let styles = "";
2004
+ if (isBold) styles += "font-weight: bold; ";
2005
+ if (isItalic) styles += "font-style: italic; ";
2006
+ if (isUnderline) styles += "text-decoration: underline; ";
2007
+ if (isStrike) styles += "text-decoration: line-through; ";
2008
+ if (colorVal && colorVal !== "auto") styles += `color: #${colorVal}; `;
2009
+ if (szVal) {
2010
+ const pt = parseInt(szVal, 10) / 2;
2011
+ if (!isNaN(pt) && pt > 0) styles += `font-size: ${pt}pt; `;
2012
+ }
2013
+ if (styles) {
2014
+ result += `<span style="${styles}">${text}</span>`;
2015
+ } else {
2016
+ result += text;
2017
+ }
2018
+ }
2019
+ return result;
2020
+ }
2021
+ renderBinaryDocFallback(ctx, wrapper) {
2022
+ const card = document.createElement("div");
2023
+ card.className = "fp-docx-page-card";
2024
+ card.style.backgroundColor = "#ffffff";
2025
+ card.style.borderRadius = "6px";
2026
+ card.style.boxShadow = "0 4px 24px rgba(0,0,0,0.08)";
2027
+ card.style.padding = "48px 56px";
2028
+ card.style.fontFamily = 'Calibri, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
2029
+ card.style.color = "#1e293b";
2030
+ card.style.lineHeight = "1.6";
2031
+ try {
2032
+ const cfbf = new CfbfReader(ctx.buffer);
2033
+ const wordDocStream = cfbf.readStream("WordDocument");
2034
+ if (wordDocStream && wordDocStream.length >= 512) {
2035
+ const text2 = this.extractReadableStrings(wordDocStream);
2036
+ card.innerHTML = `<div style="white-space: pre-wrap;">${DOMPurify2__default.default.sanitize(text2)}</div>`;
2037
+ wrapper.appendChild(card);
2038
+ return;
2039
+ }
2040
+ } catch {
2041
+ }
2042
+ const text = this.extractReadableStrings(new Uint8Array(ctx.buffer));
2043
+ card.innerHTML = `<div style="white-space: pre-wrap;">${DOMPurify2__default.default.sanitize(text)}</div>`;
2044
+ wrapper.appendChild(card);
2045
+ }
2046
+ extractReadableStrings(bytes) {
2047
+ let result = "";
2048
+ let current = "";
2049
+ for (let i = 0; i < bytes.length; i++) {
2050
+ const b = bytes[i];
2051
+ if (b >= 32 && b <= 126 || b === 10 || b === 13 || b === 9) {
2052
+ current += String.fromCharCode(b);
2053
+ } else if (b === 0 && i + 1 < bytes.length && bytes[i + 1] >= 32 && bytes[i + 1] <= 126) {
2054
+ continue;
2055
+ } else {
2056
+ if (current.trim().length > 3) {
2057
+ result += current.trim() + "\n\n";
2058
+ }
2059
+ current = "";
2060
+ }
2061
+ }
2062
+ if (current.trim().length > 3) {
2063
+ result += current.trim();
2064
+ }
2065
+ return result;
2066
+ }
1714
2067
  };
1715
2068
  function docxPlugin() {
1716
2069
  return new DocxPlugin();
@@ -2522,7 +2875,7 @@ var MarkdownPlugin = class {
2522
2875
  gfm: true,
2523
2876
  breaks: true
2524
2877
  });
2525
- const cleanHtml = DOMPurify5__default.default.sanitize(rawHtml, {
2878
+ const cleanHtml = DOMPurify2__default.default.sanitize(rawHtml, {
2526
2879
  USE_PROFILES: { html: true }
2527
2880
  });
2528
2881
  const wrapper = document.createElement("div");
@@ -2686,35 +3039,27 @@ var PptxPlugin = class {
2686
3039
  group: "navigation",
2687
3040
  execute: () => instance.toggleThumbnails?.()
2688
3041
  },
2689
- {
2690
- id: "page-prev",
2691
- icon: "page-prev",
2692
- label: "Previous Slide",
2693
- type: "button",
2694
- group: "navigation",
2695
- execute: () => {
2696
- const cur = instance.getCurrentPage?.() ?? 1;
2697
- if (cur > 1) instance.goToPage?.(cur - 1);
2698
- }
2699
- },
2700
3042
  {
2701
3043
  id: "page-nav",
2702
3044
  icon: "",
2703
- label: "Slide Number",
3045
+ label: "Slide Navigation",
2704
3046
  type: "page-nav",
2705
3047
  group: "navigation",
2706
- execute: (page) => instance.goToPage?.(Number(page))
2707
- },
2708
- {
2709
- id: "page-next",
2710
- icon: "page-next",
2711
- label: "Next Slide",
2712
- type: "button",
2713
- group: "navigation",
2714
- execute: () => {
2715
- const cur = instance.getCurrentPage?.() ?? 1;
2716
- const total = instance.getPageCount?.() ?? 1;
2717
- if (cur < total) instance.goToPage?.(cur + 1);
3048
+ value: instance.getCurrentPage?.() ?? 1,
3049
+ max: instance.getPageCount?.() ?? 1,
3050
+ execute: (action, page) => {
3051
+ if (action === "prev") {
3052
+ const cur = instance.getCurrentPage?.() ?? 1;
3053
+ if (cur > 1) instance.goToPage?.(cur - 1);
3054
+ } else if (action === "next") {
3055
+ const cur = instance.getCurrentPage?.() ?? 1;
3056
+ const total = instance.getPageCount?.() ?? 1;
3057
+ if (cur < total) instance.goToPage?.(cur + 1);
3058
+ } else if (typeof page === "number") {
3059
+ instance.goToPage?.(page);
3060
+ } else if (typeof action === "number") {
3061
+ instance.goToPage?.(action);
3062
+ }
2718
3063
  }
2719
3064
  },
2720
3065
  {
@@ -3245,7 +3590,7 @@ var HtmlPreviewPlugin = class {
3245
3590
  }
3246
3591
  async render(ctx) {
3247
3592
  const rawHtml = new TextDecoder("utf-8").decode(ctx.buffer);
3248
- const sanitized = DOMPurify5__default.default.sanitize(rawHtml, {
3593
+ const sanitized = DOMPurify2__default.default.sanitize(rawHtml, {
3249
3594
  WHOLE_DOCUMENT: true,
3250
3595
  ADD_TAGS: ["style", "link"],
3251
3596
  ADD_ATTR: ["target", "rel"]
@@ -3340,35 +3685,27 @@ var OpenDocumentPlugin = class {
3340
3685
  group: "navigation",
3341
3686
  execute: () => instance.toggleThumbnails?.()
3342
3687
  },
3343
- {
3344
- id: "page-prev",
3345
- icon: "page-prev",
3346
- label: "Previous Slide",
3347
- type: "button",
3348
- group: "navigation",
3349
- execute: () => {
3350
- const cur = instance.getCurrentPage?.() ?? 1;
3351
- if (cur > 1) instance.goToPage?.(cur - 1);
3352
- }
3353
- },
3354
3688
  {
3355
3689
  id: "page-nav",
3356
3690
  icon: "",
3357
- label: "Slide Number",
3691
+ label: "Slide Navigation",
3358
3692
  type: "page-nav",
3359
3693
  group: "navigation",
3360
- execute: (p) => instance.goToPage?.(Number(p))
3361
- },
3362
- {
3363
- id: "page-next",
3364
- icon: "page-next",
3365
- label: "Next Slide",
3366
- type: "button",
3367
- group: "navigation",
3368
- execute: () => {
3369
- const cur = instance.getCurrentPage?.() ?? 1;
3370
- const total = instance.getPageCount?.() ?? 1;
3371
- if (cur < total) instance.goToPage?.(cur + 1);
3694
+ value: instance.getCurrentPage?.() ?? 1,
3695
+ max: instance.getPageCount?.() ?? 1,
3696
+ execute: (action, page) => {
3697
+ if (action === "prev") {
3698
+ const cur = instance.getCurrentPage?.() ?? 1;
3699
+ if (cur > 1) instance.goToPage?.(cur - 1);
3700
+ } else if (action === "next") {
3701
+ const cur = instance.getCurrentPage?.() ?? 1;
3702
+ const total = instance.getPageCount?.() ?? 1;
3703
+ if (cur < total) instance.goToPage?.(cur + 1);
3704
+ } else if (typeof page === "number") {
3705
+ instance.goToPage?.(page);
3706
+ } else if (typeof action === "number") {
3707
+ instance.goToPage?.(action);
3708
+ }
3372
3709
  }
3373
3710
  }
3374
3711
  );
@@ -3496,7 +3833,7 @@ var OpenDocumentPlugin = class {
3496
3833
  wrapper.style.padding = "48px";
3497
3834
  wrapper.style.minHeight = "100%";
3498
3835
  const bodyHtml = this.renderOdfBody(contentDoc, styleMap, imageUrls);
3499
- wrapper.innerHTML = DOMPurify5__default.default.sanitize(bodyHtml, {
3836
+ wrapper.innerHTML = DOMPurify2__default.default.sanitize(bodyHtml, {
3500
3837
  ADD_TAGS: ["math", "semantics", "mrow", "mi", "mo", "mn", "msup", "msub"],
3501
3838
  ADD_ATTR: ["style", "colspan", "rowspan"]
3502
3839
  });
@@ -4016,24 +4353,24 @@ var DocPlugin = class {
4016
4353
  html += "</ul>";
4017
4354
  inList = false;
4018
4355
  }
4019
- html += `<h2 style="font-size: 18px; font-weight: 700; color: #1e3a8a; margin: 20px 0 8px; border-bottom: 1px solid #e2e8f0; padding-bottom: 4px;">${DOMPurify5__default.default.sanitize(line)}</h2>`;
4356
+ html += `<h2 style="font-size: 18px; font-weight: 700; color: #1e3a8a; margin: 20px 0 8px; border-bottom: 1px solid #e2e8f0; padding-bottom: 4px;">${DOMPurify2__default.default.sanitize(line)}</h2>`;
4020
4357
  } else if (line.startsWith("\u2022") || line.startsWith("-") || line.startsWith("*")) {
4021
4358
  if (!inList) {
4022
4359
  html += '<ul style="margin: 8px 0; padding-left: 24px;">';
4023
4360
  inList = true;
4024
4361
  }
4025
4362
  const bulletText = line.replace(/^[•\-\*]\s*/, "");
4026
- html += `<li style="margin: 4px 0; line-height: 1.6;">${DOMPurify5__default.default.sanitize(bulletText)}</li>`;
4363
+ html += `<li style="margin: 4px 0; line-height: 1.6;">${DOMPurify2__default.default.sanitize(bulletText)}</li>`;
4027
4364
  } else {
4028
4365
  if (inList) {
4029
4366
  html += "</ul>";
4030
4367
  inList = false;
4031
4368
  }
4032
- html += `<p style="line-height: 1.7; margin: 10px 0; font-size: 14px; text-align: justify;">${DOMPurify5__default.default.sanitize(line)}</p>`;
4369
+ html += `<p style="line-height: 1.7; margin: 10px 0; font-size: 14px; text-align: justify;">${DOMPurify2__default.default.sanitize(line)}</p>`;
4033
4370
  }
4034
4371
  }
4035
4372
  if (inList) html += "</ul>";
4036
- return html || `<p style="color: #64748b; font-style: italic;">(No readable text found in ${DOMPurify5__default.default.sanitize(filename)})</p>`;
4373
+ return html || `<p style="color: #64748b; font-style: italic;">(No readable text found in ${DOMPurify2__default.default.sanitize(filename)})</p>`;
4037
4374
  }
4038
4375
  };
4039
4376
  function docPlugin() {
@@ -4060,35 +4397,27 @@ var PptPlugin = class {
4060
4397
  group: "navigation",
4061
4398
  execute: () => instance.toggleThumbnails?.()
4062
4399
  },
4063
- {
4064
- id: "page-prev",
4065
- icon: "page-prev",
4066
- label: "Previous Slide",
4067
- type: "button",
4068
- group: "navigation",
4069
- execute: () => {
4070
- const cur = instance.getCurrentPage?.() ?? 1;
4071
- if (cur > 1) instance.goToPage?.(cur - 1);
4072
- }
4073
- },
4074
4400
  {
4075
4401
  id: "page-nav",
4076
4402
  icon: "",
4077
- label: "Slide Number",
4403
+ label: "Slide Navigation",
4078
4404
  type: "page-nav",
4079
4405
  group: "navigation",
4080
- execute: (p) => instance.goToPage?.(Number(p))
4081
- },
4082
- {
4083
- id: "page-next",
4084
- icon: "page-next",
4085
- label: "Next Slide",
4086
- type: "button",
4087
- group: "navigation",
4088
- execute: () => {
4089
- const cur = instance.getCurrentPage?.() ?? 1;
4090
- const total = instance.getPageCount?.() ?? 1;
4091
- if (cur < total) instance.goToPage?.(cur + 1);
4406
+ value: instance.getCurrentPage?.() ?? 1,
4407
+ max: instance.getPageCount?.() ?? 1,
4408
+ execute: (action, page) => {
4409
+ if (action === "prev") {
4410
+ const cur = instance.getCurrentPage?.() ?? 1;
4411
+ if (cur > 1) instance.goToPage?.(cur - 1);
4412
+ } else if (action === "next") {
4413
+ const cur = instance.getCurrentPage?.() ?? 1;
4414
+ const total = instance.getPageCount?.() ?? 1;
4415
+ if (cur < total) instance.goToPage?.(cur + 1);
4416
+ } else if (typeof page === "number") {
4417
+ instance.goToPage?.(page);
4418
+ } else if (typeof action === "number") {
4419
+ instance.goToPage?.(action);
4420
+ }
4092
4421
  }
4093
4422
  },
4094
4423
  {
@@ -4196,10 +4525,10 @@ var PptPlugin = class {
4196
4525
  </div>
4197
4526
  <div style="text-align: center; width: 100%;">
4198
4527
  <h1 style="font-size: ${idx === 1 ? "36px" : "28px"}; color: #1e3a8a; margin: 0 0 24px; font-family: -apple-system, BlinkMacSystemFont, sans-serif; font-weight: 700;">
4199
- ${DOMPurify5__default.default.sanitize(s.title || `Slide ${idx}`)}
4528
+ ${DOMPurify2__default.default.sanitize(s.title || `Slide ${idx}`)}
4200
4529
  </h1>
4201
4530
  <div style="display: flex; flex-direction: column; gap: 12px; max-width: 80%; margin: 0 auto; text-align: ${idx === 1 ? "center" : "left"};">
4202
- ${s.texts.map((t) => `<div style="font-size: 18px; color: #334155; line-height: 1.5; font-family: -apple-system, BlinkMacSystemFont, sans-serif;">${DOMPurify5__default.default.sanitize(t)}</div>`).join("")}
4531
+ ${s.texts.map((t) => `<div style="font-size: 18px; color: #334155; line-height: 1.5; font-family: -apple-system, BlinkMacSystemFont, sans-serif;">${DOMPurify2__default.default.sanitize(t)}</div>`).join("")}
4203
4532
  </div>
4204
4533
  </div>
4205
4534
  `;