@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/react.cjs CHANGED
@@ -3,11 +3,11 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var react = require('react');
6
- var DOMPurify5 = require('dompurify');
6
+ var DOMPurify2 = require('dompurify');
7
7
  var docx = require('docx-preview');
8
+ var fflate = require('fflate');
8
9
  var XLSX = require('xlsx');
9
10
  var hljs = require('highlight.js');
10
- var fflate = require('fflate');
11
11
  var marked = require('marked');
12
12
  var pptxBrowser = require('pptx-browser');
13
13
  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);
@@ -371,7 +371,7 @@ async function sourceToArrayBuffer(source, signal) {
371
371
  return { buffer, metadata };
372
372
  }
373
373
  function sanitizeSVG(svg) {
374
- return DOMPurify5__default.default.sanitize(svg, {
374
+ return DOMPurify2__default.default.sanitize(svg, {
375
375
  USE_PROFILES: { svg: true, svgFilters: true }
376
376
  });
377
377
  }
@@ -431,6 +431,9 @@ var ICON_THUMBNAILS = `<svg width="24" height="24" viewBox="0 0 24 24" fill="non
431
431
  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>`;
432
432
  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>`;
433
433
  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>`;
434
+ 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>`;
435
+ 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>`;
436
+ 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>`;
434
437
  var ICON_MAP = {
435
438
  "zoom-in": ICON_ZOOM_IN,
436
439
  "zoom-out": ICON_ZOOM_OUT,
@@ -447,7 +450,16 @@ var ICON_MAP = {
447
450
  "close": ICON_CLOSE,
448
451
  "copy": ICON_COPY,
449
452
  "prev": ICON_PAGE_PREV,
450
- "next": ICON_PAGE_NEXT
453
+ "next": ICON_PAGE_NEXT,
454
+ "page-prev": ICON_PAGE_PREV,
455
+ "page-next": ICON_PAGE_NEXT,
456
+ "chevron-left": ICON_PAGE_PREV,
457
+ "chevron-right": ICON_PAGE_NEXT,
458
+ "fast-forward": ICON_FAST_FORWARD,
459
+ "forward-10": ICON_FAST_FORWARD,
460
+ "rewind": ICON_REWIND,
461
+ "replay-10": ICON_REWIND,
462
+ "speed": ICON_SPEED
451
463
  };
452
464
  var ToolbarController = class {
453
465
  el;
@@ -479,7 +491,9 @@ var ToolbarController = class {
479
491
  view: [],
480
492
  actions: []
481
493
  };
482
- for (const action of this.actions) {
494
+ const hasPageNav = this.actions.some((a) => a.type === "page-nav");
495
+ const effectiveActions = hasPageNav ? this.actions.filter((a) => a.id !== "page-prev" && a.id !== "page-next" && a.id !== "prev" && a.id !== "next") : this.actions;
496
+ for (const action of effectiveActions) {
483
497
  if (groups[action.group]) {
484
498
  groups[action.group].push(action);
485
499
  }
@@ -502,13 +516,26 @@ var ToolbarController = class {
502
516
  "prev",
503
517
  ICON_PAGE_PREV,
504
518
  "Previous Page",
505
- () => action.execute("prev")
519
+ () => {
520
+ const cur = parseInt(input.value, 10) || 1;
521
+ if (cur > 1) {
522
+ input.value = (cur - 1).toString();
523
+ action.execute("prev", cur - 1);
524
+ }
525
+ }
506
526
  );
507
527
  const nextBtn = this.createButton(
508
528
  "next",
509
529
  ICON_PAGE_NEXT,
510
530
  "Next Page",
511
- () => action.execute("next")
531
+ () => {
532
+ const cur = parseInt(input.value, 10) || 1;
533
+ const max = action.max ?? 1;
534
+ if (cur < max) {
535
+ input.value = (cur + 1).toString();
536
+ action.execute("next", cur + 1);
537
+ }
538
+ }
512
539
  );
513
540
  const input = createElement("input", {
514
541
  className: "fp-toolbar-input",
@@ -518,7 +545,10 @@ var ToolbarController = class {
518
545
  max: (action.max ?? 1).toString()
519
546
  });
520
547
  input.addEventListener("change", () => {
521
- action.execute("go", parseInt(input.value, 10));
548
+ const val = parseInt(input.value, 10);
549
+ if (!isNaN(val)) {
550
+ action.execute("go", val);
551
+ }
522
552
  });
523
553
  const label = createElement("span", { className: "fp-toolbar-label" }, ` / ${action.max ?? 1}`);
524
554
  groupEl.appendChild(prevBtn);
@@ -559,11 +589,11 @@ var ToolbarController = class {
559
589
  type: "button",
560
590
  "data-action-id": id
561
591
  });
562
- const svg = ICON_MAP[iconHtml] || iconHtml;
563
- if (svg && svg.startsWith("<svg")) {
592
+ const svg = ICON_MAP[iconHtml] || ICON_MAP[id] || (iconHtml && iconHtml.startsWith("<svg") ? iconHtml : null);
593
+ if (svg) {
564
594
  btn.innerHTML = sanitizeSVG(svg);
565
595
  } else {
566
- btn.textContent = svg || title;
596
+ btn.textContent = title || id;
567
597
  }
568
598
  btn.addEventListener("click", onClick);
569
599
  return btn;
@@ -1306,12 +1336,12 @@ function pdfPlugin() {
1306
1336
  }
1307
1337
 
1308
1338
  // ../plugins/media/dist/index.js
1309
- var IMAGE_EXTS = [".jpg", ".jpeg", ".png", ".gif", ".webp", ".svg"];
1310
- var VIDEO_EXTS = [".mp4", ".webm", ".ogg"];
1311
- var AUDIO_EXTS = [".mp3", ".wav", ".ogg"];
1339
+ var IMAGE_EXTS = [".jpg", ".jpeg", ".png", ".gif", ".webp", ".svg", ".bmp", ".ico", ".tiff", ".tif", ".avif"];
1340
+ var VIDEO_EXTS = [".mp4", ".m4v", ".webm", ".ogv", ".ogg", ".mov", ".avi", ".mkv", ".flv", ".wmv", ".3gp", ".mpg", ".mpeg"];
1341
+ var AUDIO_EXTS = [".mp3", ".wav", ".ogg", ".flac", ".aac", ".m4a", ".wma", ".opus", ".weba"];
1312
1342
  var MediaPlugin = class {
1313
1343
  id = "media";
1314
- name = "Media Preview";
1344
+ name = "Media Preview (Image, Video, Audio)";
1315
1345
  extensions = [...IMAGE_EXTS, ...VIDEO_EXTS, ...AUDIO_EXTS];
1316
1346
  mimeTypes = [
1317
1347
  "image/jpeg",
@@ -1319,12 +1349,29 @@ var MediaPlugin = class {
1319
1349
  "image/gif",
1320
1350
  "image/webp",
1321
1351
  "image/svg+xml",
1352
+ "image/bmp",
1353
+ "image/x-icon",
1354
+ "image/tiff",
1355
+ "image/avif",
1322
1356
  "video/mp4",
1323
1357
  "video/webm",
1324
1358
  "video/ogg",
1359
+ "video/quicktime",
1360
+ "video/x-msvideo",
1361
+ "video/x-matroska",
1362
+ "video/x-flv",
1363
+ "video/x-ms-wmv",
1364
+ "video/3gpp",
1365
+ "video/mpeg",
1325
1366
  "audio/mpeg",
1326
1367
  "audio/wav",
1327
- "audio/ogg"
1368
+ "audio/ogg",
1369
+ "audio/flac",
1370
+ "audio/aac",
1371
+ "audio/mp4",
1372
+ "audio/x-ms-wma",
1373
+ "audio/opus",
1374
+ "audio/webm"
1328
1375
  ];
1329
1376
  weight = 90;
1330
1377
  supports(file) {
@@ -1380,6 +1427,16 @@ var MediaPlugin = class {
1380
1427
  }
1381
1428
  if (instance.play) {
1382
1429
  actions.push(
1430
+ {
1431
+ id: "replay-10",
1432
+ icon: "replay-10",
1433
+ label: "Rewind 10s",
1434
+ type: "button",
1435
+ group: "actions",
1436
+ execute: () => {
1437
+ instance.rewind?.(10);
1438
+ }
1439
+ },
1383
1440
  {
1384
1441
  id: "play",
1385
1442
  icon: "play",
@@ -1399,6 +1456,26 @@ var MediaPlugin = class {
1399
1456
  execute: () => {
1400
1457
  instance.pause?.();
1401
1458
  }
1459
+ },
1460
+ {
1461
+ id: "forward-10",
1462
+ icon: "forward-10",
1463
+ label: "Fast Forward 10s",
1464
+ type: "button",
1465
+ group: "actions",
1466
+ execute: () => {
1467
+ instance.fastForward?.(10);
1468
+ }
1469
+ },
1470
+ {
1471
+ id: "speed",
1472
+ icon: "speed",
1473
+ label: "Playback Speed",
1474
+ type: "button",
1475
+ group: "actions",
1476
+ execute: () => {
1477
+ instance.cycleSpeed?.();
1478
+ }
1402
1479
  }
1403
1480
  );
1404
1481
  }
@@ -1457,8 +1534,12 @@ var MediaPlugin = class {
1457
1534
  const video = document.createElement("video");
1458
1535
  video.src = url;
1459
1536
  video.controls = true;
1460
- video.style.maxWidth = "100%";
1461
- video.style.maxHeight = "100%";
1537
+ video.playsInline = true;
1538
+ video.style.maxWidth = "90%";
1539
+ video.style.maxHeight = "90%";
1540
+ video.style.borderRadius = "8px";
1541
+ video.style.boxShadow = "0 8px 30px rgba(0,0,0,0.3)";
1542
+ video.style.backgroundColor = "#000000";
1462
1543
  element = video;
1463
1544
  mediaElement = video;
1464
1545
  } else if (isAudio) {
@@ -1517,6 +1598,28 @@ var MediaPlugin = class {
1517
1598
  play: mediaElement ? () => mediaElement?.play() : void 0,
1518
1599
  pause: mediaElement ? () => mediaElement?.pause() : void 0,
1519
1600
  isPlaying: mediaElement ? () => !mediaElement?.paused : void 0,
1601
+ fastForward: mediaElement ? (seconds = 10) => {
1602
+ if (mediaElement) {
1603
+ mediaElement.currentTime = Math.min(mediaElement.duration || Infinity, mediaElement.currentTime + seconds);
1604
+ }
1605
+ } : void 0,
1606
+ rewind: mediaElement ? (seconds = 10) => {
1607
+ if (mediaElement) {
1608
+ mediaElement.currentTime = Math.max(0, mediaElement.currentTime - seconds);
1609
+ }
1610
+ } : void 0,
1611
+ cycleSpeed: mediaElement ? () => {
1612
+ if (mediaElement) {
1613
+ const speeds = [1, 1.25, 1.5, 2, 0.5];
1614
+ const curIndex = speeds.indexOf(mediaElement.playbackRate);
1615
+ const nextIndex = curIndex === -1 ? 0 : (curIndex + 1) % speeds.length;
1616
+ mediaElement.playbackRate = speeds[nextIndex];
1617
+ }
1618
+ } : void 0,
1619
+ setPlaybackRate: mediaElement ? (rate) => {
1620
+ if (mediaElement) mediaElement.playbackRate = rate;
1621
+ } : void 0,
1622
+ getPlaybackRate: mediaElement ? () => mediaElement?.playbackRate ?? 1 : void 0,
1520
1623
  download: () => {
1521
1624
  const a = document.createElement("a");
1522
1625
  a.href = url;
@@ -1534,7 +1637,7 @@ function mediaPlugin() {
1534
1637
  }
1535
1638
  var DocxPlugin = class {
1536
1639
  id = "docx";
1537
- name = "Word Document Preview";
1640
+ name = "Word Document Preview (.docx, .docm, .dotx, .dotm)";
1538
1641
  extensions = [".docx", ".docm", ".dotx", ".dotm"];
1539
1642
  mimeTypes = [
1540
1643
  "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
@@ -1556,9 +1659,7 @@ var DocxPlugin = class {
1556
1659
  label: "Zoom Out",
1557
1660
  type: "button",
1558
1661
  group: "zoom",
1559
- execute: () => {
1560
- instance.zoomOut?.();
1561
- }
1662
+ execute: () => instance.zoomOut?.()
1562
1663
  },
1563
1664
  {
1564
1665
  id: "zoom-in",
@@ -1566,9 +1667,7 @@ var DocxPlugin = class {
1566
1667
  label: "Zoom In",
1567
1668
  type: "button",
1568
1669
  group: "zoom",
1569
- execute: () => {
1570
- instance.zoomIn?.();
1571
- }
1670
+ execute: () => instance.zoomIn?.()
1572
1671
  },
1573
1672
  {
1574
1673
  id: "fit-page",
@@ -1576,9 +1675,7 @@ var DocxPlugin = class {
1576
1675
  label: "Fit to Page",
1577
1676
  type: "button",
1578
1677
  group: "zoom",
1579
- execute: () => {
1580
- instance.fitToPage?.();
1581
- }
1678
+ execute: () => instance.fitToPage?.()
1582
1679
  },
1583
1680
  {
1584
1681
  id: "download",
@@ -1586,9 +1683,7 @@ var DocxPlugin = class {
1586
1683
  label: "Download",
1587
1684
  type: "button",
1588
1685
  group: "actions",
1589
- execute: () => {
1590
- instance.download?.();
1591
- }
1686
+ execute: () => instance.download?.()
1592
1687
  },
1593
1688
  {
1594
1689
  id: "print",
@@ -1596,9 +1691,7 @@ var DocxPlugin = class {
1596
1691
  label: "Print",
1597
1692
  type: "button",
1598
1693
  group: "actions",
1599
- execute: () => {
1600
- instance.print?.();
1601
- }
1694
+ execute: () => instance.print?.()
1602
1695
  }
1603
1696
  ];
1604
1697
  }
@@ -1607,27 +1700,81 @@ var DocxPlugin = class {
1607
1700
  wrapper.className = "fp-docx-wrapper";
1608
1701
  wrapper.style.transformOrigin = "top center";
1609
1702
  wrapper.style.transition = "transform 0.2s ease";
1610
- wrapper.style.padding = "16px";
1703
+ wrapper.style.padding = "24px 16px";
1704
+ wrapper.style.maxWidth = "950px";
1705
+ wrapper.style.margin = "0 auto";
1706
+ wrapper.style.boxSizing = "border-box";
1611
1707
  ctx.container.style.overflow = "auto";
1708
+ ctx.container.style.backgroundColor = "#f1f5f9";
1612
1709
  ctx.container.appendChild(wrapper);
1710
+ const styleOverride = document.createElement("style");
1711
+ styleOverride.textContent = `
1712
+ .fp-docx-wrapper .docx-wrapper {
1713
+ background: transparent !important;
1714
+ padding: 0 !important;
1715
+ display: flex !important;
1716
+ flex-direction: column !important;
1717
+ align-items: center !important;
1718
+ box-sizing: border-box !important;
1719
+ }
1720
+ .fp-docx-wrapper section.docx {
1721
+ box-shadow: 0 4px 24px rgba(0, 0, 0, 0.08) !important;
1722
+ border-radius: 4px !important;
1723
+ margin-bottom: 24px !important;
1724
+ background-color: #ffffff !important;
1725
+ }
1726
+ `;
1727
+ ctx.container.appendChild(styleOverride);
1613
1728
  let scale = 1;
1729
+ let renderedSuccessfully = false;
1730
+ const createdBlobUrls = [];
1614
1731
  try {
1615
- await docx__namespace.renderAsync(ctx.buffer, wrapper, ctx.container, {
1732
+ await docx__namespace.renderAsync(ctx.buffer, wrapper, wrapper, {
1616
1733
  inWrapper: true,
1617
1734
  ignoreWidth: false,
1618
- ignoreHeight: false
1735
+ ignoreHeight: false,
1736
+ ignoreFonts: true,
1737
+ // Avoid crashes on embedded obfuscated fonts
1738
+ breakPages: true,
1739
+ experimental: true
1619
1740
  });
1620
- } catch {
1621
- wrapper.innerHTML = `
1622
- <div style="text-align:center; padding: 40px; color: #666;">
1623
- <div style="font-size:48px; margin-bottom: 16px;">\u{1F4C4}</div>
1624
- <h3>${ctx.metadata.name || "Word Document"}</h3>
1625
- <p>DOCX render preview</p>
1626
- </div>
1627
- `;
1741
+ if (!ctx.container.contains(wrapper)) {
1742
+ ctx.container.appendChild(wrapper);
1743
+ }
1744
+ if (wrapper.children.length > 0 && (wrapper.textContent?.trim().length ?? 0) > 0) {
1745
+ renderedSuccessfully = true;
1746
+ }
1747
+ } catch (err) {
1748
+ console.warn("[DocxPlugin] docx-preview failed, triggering native fallback:", err);
1749
+ }
1750
+ if (!renderedSuccessfully) {
1751
+ try {
1752
+ wrapper.innerHTML = "";
1753
+ this.renderXmlFallback(ctx, wrapper, createdBlobUrls);
1754
+ if (!ctx.container.contains(wrapper)) {
1755
+ ctx.container.appendChild(wrapper);
1756
+ }
1757
+ renderedSuccessfully = true;
1758
+ } catch (fallbackErr) {
1759
+ console.error("[DocxPlugin] Native fallback failed:", fallbackErr);
1760
+ wrapper.innerHTML = `
1761
+ <div style="text-align:center; padding: 48px; background: #fff; border-radius: 8px; box-shadow: 0 4px 20px rgba(0,0,0,0.06);">
1762
+ <div style="font-size:48px; margin-bottom: 16px;">\u{1F4C4}</div>
1763
+ <h3 style="margin: 0 0 8px; color: #1e293b;">${ctx.metadata.name || "Word Document"}</h3>
1764
+ <p style="color: #64748b; margin: 0;">Could not parse document content</p>
1765
+ </div>
1766
+ `;
1767
+ if (!ctx.container.contains(wrapper)) {
1768
+ ctx.container.appendChild(wrapper);
1769
+ }
1770
+ }
1628
1771
  }
1629
1772
  const cleanup = () => {
1773
+ for (const url of createdBlobUrls) {
1774
+ URL.revokeObjectURL(url);
1775
+ }
1630
1776
  wrapper.remove();
1777
+ styleOverride.remove();
1631
1778
  ctx.container.innerHTML = "";
1632
1779
  };
1633
1780
  ctx.signal.addEventListener("abort", cleanup);
@@ -1648,7 +1795,7 @@ var DocxPlugin = class {
1648
1795
  },
1649
1796
  fitToPage: () => {
1650
1797
  scale = 1;
1651
- wrapper.style.transform = `scale(1)`;
1798
+ wrapper.style.transform = "scale(1)";
1652
1799
  },
1653
1800
  download: () => {
1654
1801
  const blob = new Blob([ctx.buffer], { type: this.mimeTypes[0] });
@@ -1664,6 +1811,212 @@ var DocxPlugin = class {
1664
1811
  }
1665
1812
  };
1666
1813
  }
1814
+ renderXmlFallback(ctx, wrapper, createdBlobUrls) {
1815
+ const magic = new Uint8Array(ctx.buffer.slice(0, 8));
1816
+ if (magic[0] === 208 && magic[1] === 207 && magic[2] === 17 && magic[3] === 224) {
1817
+ this.renderBinaryDocFallback(ctx, wrapper);
1818
+ return;
1819
+ }
1820
+ const unzipped = fflate.unzipSync(new Uint8Array(ctx.buffer));
1821
+ const docKey = Object.keys(unzipped).find((k) => k.replace(/^[./\\]+/, "").toLowerCase() === "word/document.xml");
1822
+ const docXmlEntry = docKey ? unzipped[docKey] : void 0;
1823
+ if (!docXmlEntry) throw new Error("Missing word/document.xml in DOCX package");
1824
+ const xmlStr = fflate.strFromU8(docXmlEntry);
1825
+ const parser = new DOMParser();
1826
+ const doc = parser.parseFromString(xmlStr, "application/xml");
1827
+ const imageMap = {};
1828
+ const relsKey = Object.keys(unzipped).find((k) => k.replace(/^[./\\]+/, "").toLowerCase() === "word/_rels/document.xml.rels");
1829
+ if (relsKey && unzipped[relsKey]) {
1830
+ try {
1831
+ const relsXml = fflate.strFromU8(unzipped[relsKey]);
1832
+ const relsDoc = parser.parseFromString(relsXml, "application/xml");
1833
+ const relEls = Array.from(relsDoc.getElementsByTagName("Relationship"));
1834
+ for (const rel of relEls) {
1835
+ const rId = rel.getAttribute("Id");
1836
+ const target = rel.getAttribute("Target");
1837
+ if (rId && target) {
1838
+ const cleanTarget = target.replace(/^\.\.\//, "").replace(/^\//, "");
1839
+ const fullTargetKey = Object.keys(unzipped).find((k) => {
1840
+ const norm = k.replace(/^[./\\]+/, "").toLowerCase();
1841
+ return norm === ("word/" + cleanTarget).toLowerCase() || norm === cleanTarget.toLowerCase();
1842
+ });
1843
+ if (fullTargetKey && unzipped[fullTargetKey]) {
1844
+ const bytes = unzipped[fullTargetKey];
1845
+ const ext = cleanTarget.split(".").pop()?.toLowerCase() || "png";
1846
+ const mime = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : ext === "gif" ? "image/gif" : ext === "svg" ? "image/svg+xml" : "image/png";
1847
+ const blob = new Blob([bytes], { type: mime });
1848
+ const blobUrl = URL.createObjectURL(blob);
1849
+ imageMap[rId] = blobUrl;
1850
+ createdBlobUrls.push(blobUrl);
1851
+ }
1852
+ }
1853
+ }
1854
+ } catch (relsErr) {
1855
+ console.warn("[DocxPlugin] Error parsing relationships:", relsErr);
1856
+ }
1857
+ }
1858
+ const card = document.createElement("div");
1859
+ card.className = "fp-docx-page-card";
1860
+ card.style.backgroundColor = "#ffffff";
1861
+ card.style.borderRadius = "6px";
1862
+ card.style.boxShadow = "0 4px 24px rgba(0,0,0,0.08)";
1863
+ card.style.padding = "48px 56px";
1864
+ card.style.fontFamily = 'Calibri, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
1865
+ card.style.color = "#1e293b";
1866
+ card.style.lineHeight = "1.6";
1867
+ const body = doc.getElementsByTagNameNS("*", "body")[0] || doc.documentElement;
1868
+ let html = "";
1869
+ for (const child of Array.from(body.children)) {
1870
+ const tag = child.localName || child.nodeName.split(":").pop();
1871
+ if (tag === "p") {
1872
+ const pStyle = child.getElementsByTagNameNS("*", "pStyle")[0];
1873
+ const styleVal = pStyle?.getAttribute("w:val") || pStyle?.getAttribute("val") || "";
1874
+ const numPr = child.getElementsByTagNameNS("*", "numPr")[0];
1875
+ const textContent = this.extractParagraphHtml(child, imageMap);
1876
+ if (!textContent.trim()) {
1877
+ html += '<div style="height: 10px;"></div>';
1878
+ continue;
1879
+ }
1880
+ const lowerStyle = styleVal.toLowerCase();
1881
+ if (lowerStyle.includes("title")) {
1882
+ 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>`;
1883
+ } else if (lowerStyle.includes("heading1") || styleVal === "1") {
1884
+ html += `<h2 style="font-size: 22px; font-weight: 700; color: #1e40af; margin: 20px 0 10px;">${textContent}</h2>`;
1885
+ } else if (lowerStyle.includes("heading2") || styleVal === "2") {
1886
+ html += `<h3 style="font-size: 18px; font-weight: 600; color: #2563eb; margin: 16px 0 8px;">${textContent}</h3>`;
1887
+ } else if (lowerStyle.includes("heading3") || styleVal === "3") {
1888
+ html += `<h4 style="font-size: 15px; font-weight: 600; color: #334155; margin: 12px 0 6px;">${textContent}</h4>`;
1889
+ } else if (numPr) {
1890
+ 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>`;
1891
+ } else {
1892
+ html += `<p style="margin: 8px 0; font-size: 14px;">${textContent}</p>`;
1893
+ }
1894
+ } else if (tag === "tbl") {
1895
+ html += '<table style="width: 100%; border-collapse: collapse; margin: 20px 0; border: 1px solid #cbd5e1;">';
1896
+ const rows = Array.from(child.getElementsByTagNameNS("*", "tr"));
1897
+ rows.forEach((tr, rIdx) => {
1898
+ html += `<tr style="${rIdx === 0 ? "background-color: #f8fafc; font-weight: 600;" : ""}">`;
1899
+ const cells = Array.from(tr.getElementsByTagNameNS("*", "tc"));
1900
+ cells.forEach((tc) => {
1901
+ const cellText = this.extractParagraphHtml(tc, imageMap);
1902
+ html += `<td style="border: 1px solid #cbd5e1; padding: 8px 12px; font-size: 13px;">${cellText || "&nbsp;"}</td>`;
1903
+ });
1904
+ html += "</tr>";
1905
+ });
1906
+ html += "</table>";
1907
+ }
1908
+ }
1909
+ card.innerHTML = DOMPurify2__default.default.sanitize(html, {
1910
+ ADD_TAGS: ["h1", "h2", "h3", "h4", "p", "table", "tr", "td", "span", "b", "i", "u", "s", "strike", "img", "div", "br"],
1911
+ ADD_ATTR: ["style", "src", "alt", "colspan", "rowspan"]
1912
+ });
1913
+ wrapper.appendChild(card);
1914
+ }
1915
+ extractParagraphHtml(pElement, imageMap = {}) {
1916
+ let result = "";
1917
+ const drawings = Array.from(pElement.getElementsByTagNameNS("*", "drawing"));
1918
+ for (const drawing of drawings) {
1919
+ const blip = drawing.getElementsByTagNameNS("*", "blip")[0];
1920
+ const rId = blip?.getAttribute("r:embed") || blip?.getAttribute("r:id");
1921
+ if (rId && imageMap[rId]) {
1922
+ result += `<div style="text-align:center; margin: 12px 0;"><img src="${imageMap[rId]}" style="max-width: 100%; height: auto; border-radius: 4px;" /></div>`;
1923
+ }
1924
+ }
1925
+ const runs = Array.from(pElement.getElementsByTagNameNS("*", "r"));
1926
+ if (runs.length === 0 && !result) {
1927
+ return DOMPurify2__default.default.sanitize(pElement.textContent || "");
1928
+ }
1929
+ for (const r of runs) {
1930
+ const blip = r.getElementsByTagNameNS("*", "blip")[0] || r.getElementsByTagNameNS("*", "imagedata")[0];
1931
+ const rId = blip?.getAttribute("r:embed") || blip?.getAttribute("r:id");
1932
+ if (rId && imageMap[rId]) {
1933
+ result += `<img src="${imageMap[rId]}" style="max-width: 100%; height: auto; display: inline-block; margin: 4px;" />`;
1934
+ }
1935
+ const brs = r.getElementsByTagNameNS("*", "br");
1936
+ for (let i = 0; i < brs.length; i++) {
1937
+ result += "<br/>";
1938
+ }
1939
+ const tabs = r.getElementsByTagNameNS("*", "tab");
1940
+ if (tabs.length > 0) {
1941
+ result += "&emsp;";
1942
+ }
1943
+ const rPr = r.getElementsByTagNameNS("*", "rPr")[0];
1944
+ const isBold = !!rPr?.getElementsByTagNameNS("*", "b")[0];
1945
+ const isItalic = !!rPr?.getElementsByTagNameNS("*", "i")[0];
1946
+ const isUnderline = !!rPr?.getElementsByTagNameNS("*", "u")[0];
1947
+ const isStrike = !!rPr?.getElementsByTagNameNS("*", "strike")[0];
1948
+ const colorNode = rPr?.getElementsByTagNameNS("*", "color")[0];
1949
+ const colorVal = colorNode?.getAttribute("w:val") || colorNode?.getAttribute("val");
1950
+ const szNode = rPr?.getElementsByTagNameNS("*", "sz")[0];
1951
+ const szVal = szNode?.getAttribute("w:val") || szNode?.getAttribute("val");
1952
+ const texts = Array.from(r.getElementsByTagNameNS("*", "t"));
1953
+ let text = texts.map((t) => t.textContent || "").join("");
1954
+ if (!text) continue;
1955
+ text = DOMPurify2__default.default.sanitize(text);
1956
+ let styles = "";
1957
+ if (isBold) styles += "font-weight: bold; ";
1958
+ if (isItalic) styles += "font-style: italic; ";
1959
+ if (isUnderline) styles += "text-decoration: underline; ";
1960
+ if (isStrike) styles += "text-decoration: line-through; ";
1961
+ if (colorVal && colorVal !== "auto") styles += `color: #${colorVal}; `;
1962
+ if (szVal) {
1963
+ const pt = parseInt(szVal, 10) / 2;
1964
+ if (!isNaN(pt) && pt > 0) styles += `font-size: ${pt}pt; `;
1965
+ }
1966
+ if (styles) {
1967
+ result += `<span style="${styles}">${text}</span>`;
1968
+ } else {
1969
+ result += text;
1970
+ }
1971
+ }
1972
+ return result;
1973
+ }
1974
+ renderBinaryDocFallback(ctx, wrapper) {
1975
+ const card = document.createElement("div");
1976
+ card.className = "fp-docx-page-card";
1977
+ card.style.backgroundColor = "#ffffff";
1978
+ card.style.borderRadius = "6px";
1979
+ card.style.boxShadow = "0 4px 24px rgba(0,0,0,0.08)";
1980
+ card.style.padding = "48px 56px";
1981
+ card.style.fontFamily = 'Calibri, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
1982
+ card.style.color = "#1e293b";
1983
+ card.style.lineHeight = "1.6";
1984
+ try {
1985
+ const cfbf = new CfbfReader(ctx.buffer);
1986
+ const wordDocStream = cfbf.readStream("WordDocument");
1987
+ if (wordDocStream && wordDocStream.length >= 512) {
1988
+ const text2 = this.extractReadableStrings(wordDocStream);
1989
+ card.innerHTML = `<div style="white-space: pre-wrap;">${DOMPurify2__default.default.sanitize(text2)}</div>`;
1990
+ wrapper.appendChild(card);
1991
+ return;
1992
+ }
1993
+ } catch {
1994
+ }
1995
+ const text = this.extractReadableStrings(new Uint8Array(ctx.buffer));
1996
+ card.innerHTML = `<div style="white-space: pre-wrap;">${DOMPurify2__default.default.sanitize(text)}</div>`;
1997
+ wrapper.appendChild(card);
1998
+ }
1999
+ extractReadableStrings(bytes) {
2000
+ let result = "";
2001
+ let current = "";
2002
+ for (let i = 0; i < bytes.length; i++) {
2003
+ const b = bytes[i];
2004
+ if (b >= 32 && b <= 126 || b === 10 || b === 13 || b === 9) {
2005
+ current += String.fromCharCode(b);
2006
+ } else if (b === 0 && i + 1 < bytes.length && bytes[i + 1] >= 32 && bytes[i + 1] <= 126) {
2007
+ continue;
2008
+ } else {
2009
+ if (current.trim().length > 3) {
2010
+ result += current.trim() + "\n\n";
2011
+ }
2012
+ current = "";
2013
+ }
2014
+ }
2015
+ if (current.trim().length > 3) {
2016
+ result += current.trim();
2017
+ }
2018
+ return result;
2019
+ }
1667
2020
  };
1668
2021
  function docxPlugin() {
1669
2022
  return new DocxPlugin();
@@ -2475,7 +2828,7 @@ var MarkdownPlugin = class {
2475
2828
  gfm: true,
2476
2829
  breaks: true
2477
2830
  });
2478
- const cleanHtml = DOMPurify5__default.default.sanitize(rawHtml, {
2831
+ const cleanHtml = DOMPurify2__default.default.sanitize(rawHtml, {
2479
2832
  USE_PROFILES: { html: true }
2480
2833
  });
2481
2834
  const wrapper = document.createElement("div");
@@ -2639,35 +2992,27 @@ var PptxPlugin = class {
2639
2992
  group: "navigation",
2640
2993
  execute: () => instance.toggleThumbnails?.()
2641
2994
  },
2642
- {
2643
- id: "page-prev",
2644
- icon: "page-prev",
2645
- label: "Previous Slide",
2646
- type: "button",
2647
- group: "navigation",
2648
- execute: () => {
2649
- const cur = instance.getCurrentPage?.() ?? 1;
2650
- if (cur > 1) instance.goToPage?.(cur - 1);
2651
- }
2652
- },
2653
2995
  {
2654
2996
  id: "page-nav",
2655
2997
  icon: "",
2656
- label: "Slide Number",
2998
+ label: "Slide Navigation",
2657
2999
  type: "page-nav",
2658
3000
  group: "navigation",
2659
- execute: (page) => instance.goToPage?.(Number(page))
2660
- },
2661
- {
2662
- id: "page-next",
2663
- icon: "page-next",
2664
- label: "Next Slide",
2665
- type: "button",
2666
- group: "navigation",
2667
- execute: () => {
2668
- const cur = instance.getCurrentPage?.() ?? 1;
2669
- const total = instance.getPageCount?.() ?? 1;
2670
- if (cur < total) instance.goToPage?.(cur + 1);
3001
+ value: instance.getCurrentPage?.() ?? 1,
3002
+ max: instance.getPageCount?.() ?? 1,
3003
+ execute: (action, page) => {
3004
+ if (action === "prev") {
3005
+ const cur = instance.getCurrentPage?.() ?? 1;
3006
+ if (cur > 1) instance.goToPage?.(cur - 1);
3007
+ } else if (action === "next") {
3008
+ const cur = instance.getCurrentPage?.() ?? 1;
3009
+ const total = instance.getPageCount?.() ?? 1;
3010
+ if (cur < total) instance.goToPage?.(cur + 1);
3011
+ } else if (typeof page === "number") {
3012
+ instance.goToPage?.(page);
3013
+ } else if (typeof action === "number") {
3014
+ instance.goToPage?.(action);
3015
+ }
2671
3016
  }
2672
3017
  },
2673
3018
  {
@@ -3198,7 +3543,7 @@ var HtmlPreviewPlugin = class {
3198
3543
  }
3199
3544
  async render(ctx) {
3200
3545
  const rawHtml = new TextDecoder("utf-8").decode(ctx.buffer);
3201
- const sanitized = DOMPurify5__default.default.sanitize(rawHtml, {
3546
+ const sanitized = DOMPurify2__default.default.sanitize(rawHtml, {
3202
3547
  WHOLE_DOCUMENT: true,
3203
3548
  ADD_TAGS: ["style", "link"],
3204
3549
  ADD_ATTR: ["target", "rel"]
@@ -3293,35 +3638,27 @@ var OpenDocumentPlugin = class {
3293
3638
  group: "navigation",
3294
3639
  execute: () => instance.toggleThumbnails?.()
3295
3640
  },
3296
- {
3297
- id: "page-prev",
3298
- icon: "page-prev",
3299
- label: "Previous Slide",
3300
- type: "button",
3301
- group: "navigation",
3302
- execute: () => {
3303
- const cur = instance.getCurrentPage?.() ?? 1;
3304
- if (cur > 1) instance.goToPage?.(cur - 1);
3305
- }
3306
- },
3307
3641
  {
3308
3642
  id: "page-nav",
3309
3643
  icon: "",
3310
- label: "Slide Number",
3644
+ label: "Slide Navigation",
3311
3645
  type: "page-nav",
3312
3646
  group: "navigation",
3313
- execute: (p) => instance.goToPage?.(Number(p))
3314
- },
3315
- {
3316
- id: "page-next",
3317
- icon: "page-next",
3318
- label: "Next Slide",
3319
- type: "button",
3320
- group: "navigation",
3321
- execute: () => {
3322
- const cur = instance.getCurrentPage?.() ?? 1;
3323
- const total = instance.getPageCount?.() ?? 1;
3324
- if (cur < total) instance.goToPage?.(cur + 1);
3647
+ value: instance.getCurrentPage?.() ?? 1,
3648
+ max: instance.getPageCount?.() ?? 1,
3649
+ execute: (action, page) => {
3650
+ if (action === "prev") {
3651
+ const cur = instance.getCurrentPage?.() ?? 1;
3652
+ if (cur > 1) instance.goToPage?.(cur - 1);
3653
+ } else if (action === "next") {
3654
+ const cur = instance.getCurrentPage?.() ?? 1;
3655
+ const total = instance.getPageCount?.() ?? 1;
3656
+ if (cur < total) instance.goToPage?.(cur + 1);
3657
+ } else if (typeof page === "number") {
3658
+ instance.goToPage?.(page);
3659
+ } else if (typeof action === "number") {
3660
+ instance.goToPage?.(action);
3661
+ }
3325
3662
  }
3326
3663
  }
3327
3664
  );
@@ -3449,7 +3786,7 @@ var OpenDocumentPlugin = class {
3449
3786
  wrapper.style.padding = "48px";
3450
3787
  wrapper.style.minHeight = "100%";
3451
3788
  const bodyHtml = this.renderOdfBody(contentDoc, styleMap, imageUrls);
3452
- wrapper.innerHTML = DOMPurify5__default.default.sanitize(bodyHtml, {
3789
+ wrapper.innerHTML = DOMPurify2__default.default.sanitize(bodyHtml, {
3453
3790
  ADD_TAGS: ["math", "semantics", "mrow", "mi", "mo", "mn", "msup", "msub"],
3454
3791
  ADD_ATTR: ["style", "colspan", "rowspan"]
3455
3792
  });
@@ -3969,24 +4306,24 @@ var DocPlugin = class {
3969
4306
  html += "</ul>";
3970
4307
  inList = false;
3971
4308
  }
3972
- 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>`;
4309
+ 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>`;
3973
4310
  } else if (line.startsWith("\u2022") || line.startsWith("-") || line.startsWith("*")) {
3974
4311
  if (!inList) {
3975
4312
  html += '<ul style="margin: 8px 0; padding-left: 24px;">';
3976
4313
  inList = true;
3977
4314
  }
3978
4315
  const bulletText = line.replace(/^[•\-\*]\s*/, "");
3979
- html += `<li style="margin: 4px 0; line-height: 1.6;">${DOMPurify5__default.default.sanitize(bulletText)}</li>`;
4316
+ html += `<li style="margin: 4px 0; line-height: 1.6;">${DOMPurify2__default.default.sanitize(bulletText)}</li>`;
3980
4317
  } else {
3981
4318
  if (inList) {
3982
4319
  html += "</ul>";
3983
4320
  inList = false;
3984
4321
  }
3985
- html += `<p style="line-height: 1.7; margin: 10px 0; font-size: 14px; text-align: justify;">${DOMPurify5__default.default.sanitize(line)}</p>`;
4322
+ html += `<p style="line-height: 1.7; margin: 10px 0; font-size: 14px; text-align: justify;">${DOMPurify2__default.default.sanitize(line)}</p>`;
3986
4323
  }
3987
4324
  }
3988
4325
  if (inList) html += "</ul>";
3989
- return html || `<p style="color: #64748b; font-style: italic;">(No readable text found in ${DOMPurify5__default.default.sanitize(filename)})</p>`;
4326
+ return html || `<p style="color: #64748b; font-style: italic;">(No readable text found in ${DOMPurify2__default.default.sanitize(filename)})</p>`;
3990
4327
  }
3991
4328
  };
3992
4329
  function docPlugin() {
@@ -4013,35 +4350,27 @@ var PptPlugin = class {
4013
4350
  group: "navigation",
4014
4351
  execute: () => instance.toggleThumbnails?.()
4015
4352
  },
4016
- {
4017
- id: "page-prev",
4018
- icon: "page-prev",
4019
- label: "Previous Slide",
4020
- type: "button",
4021
- group: "navigation",
4022
- execute: () => {
4023
- const cur = instance.getCurrentPage?.() ?? 1;
4024
- if (cur > 1) instance.goToPage?.(cur - 1);
4025
- }
4026
- },
4027
4353
  {
4028
4354
  id: "page-nav",
4029
4355
  icon: "",
4030
- label: "Slide Number",
4356
+ label: "Slide Navigation",
4031
4357
  type: "page-nav",
4032
4358
  group: "navigation",
4033
- execute: (p) => instance.goToPage?.(Number(p))
4034
- },
4035
- {
4036
- id: "page-next",
4037
- icon: "page-next",
4038
- label: "Next Slide",
4039
- type: "button",
4040
- group: "navigation",
4041
- execute: () => {
4042
- const cur = instance.getCurrentPage?.() ?? 1;
4043
- const total = instance.getPageCount?.() ?? 1;
4044
- if (cur < total) instance.goToPage?.(cur + 1);
4359
+ value: instance.getCurrentPage?.() ?? 1,
4360
+ max: instance.getPageCount?.() ?? 1,
4361
+ execute: (action, page) => {
4362
+ if (action === "prev") {
4363
+ const cur = instance.getCurrentPage?.() ?? 1;
4364
+ if (cur > 1) instance.goToPage?.(cur - 1);
4365
+ } else if (action === "next") {
4366
+ const cur = instance.getCurrentPage?.() ?? 1;
4367
+ const total = instance.getPageCount?.() ?? 1;
4368
+ if (cur < total) instance.goToPage?.(cur + 1);
4369
+ } else if (typeof page === "number") {
4370
+ instance.goToPage?.(page);
4371
+ } else if (typeof action === "number") {
4372
+ instance.goToPage?.(action);
4373
+ }
4045
4374
  }
4046
4375
  },
4047
4376
  {
@@ -4149,10 +4478,10 @@ var PptPlugin = class {
4149
4478
  </div>
4150
4479
  <div style="text-align: center; width: 100%;">
4151
4480
  <h1 style="font-size: ${idx === 1 ? "36px" : "28px"}; color: #1e3a8a; margin: 0 0 24px; font-family: -apple-system, BlinkMacSystemFont, sans-serif; font-weight: 700;">
4152
- ${DOMPurify5__default.default.sanitize(s.title || `Slide ${idx}`)}
4481
+ ${DOMPurify2__default.default.sanitize(s.title || `Slide ${idx}`)}
4153
4482
  </h1>
4154
4483
  <div style="display: flex; flex-direction: column; gap: 12px; max-width: 80%; margin: 0 auto; text-align: ${idx === 1 ? "center" : "left"};">
4155
- ${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("")}
4484
+ ${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("")}
4156
4485
  </div>
4157
4486
  </div>
4158
4487
  `;