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