@files-preview-app/preview-file 1.2.0 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
- import DOMPurify5 from 'dompurify';
1
+ import DOMPurify6 from 'dompurify';
2
2
  import * as docx from 'docx-preview';
3
+ import { unzipSync, strFromU8, unzip } from 'fflate';
3
4
  import * as XLSX from 'xlsx';
4
5
  import hljs from 'highlight.js';
5
- import { unzip, unzipSync, strFromU8 } from 'fflate';
6
6
  import { marked } from 'marked';
7
7
  import { PptxRenderer } from 'pptx-browser';
8
8
  import * as THREE from 'three';
@@ -339,12 +339,12 @@ async function sourceToArrayBuffer(source, signal) {
339
339
  return { buffer, metadata };
340
340
  }
341
341
  function sanitizeHTML(html) {
342
- return DOMPurify5.sanitize(html, {
342
+ return DOMPurify6.sanitize(html, {
343
343
  USE_PROFILES: { html: true }
344
344
  });
345
345
  }
346
346
  function sanitizeSVG(svg) {
347
- return DOMPurify5.sanitize(svg, {
347
+ return DOMPurify6.sanitize(svg, {
348
348
  USE_PROFILES: { svg: true, svgFilters: true }
349
349
  });
350
350
  }
@@ -459,7 +459,11 @@ var ICON_MAP = {
459
459
  "close": ICON_CLOSE,
460
460
  "copy": ICON_COPY,
461
461
  "prev": ICON_PAGE_PREV,
462
- "next": ICON_PAGE_NEXT
462
+ "next": ICON_PAGE_NEXT,
463
+ "page-prev": ICON_PAGE_PREV,
464
+ "page-next": ICON_PAGE_NEXT,
465
+ "chevron-left": ICON_PAGE_PREV,
466
+ "chevron-right": ICON_PAGE_NEXT
463
467
  };
464
468
  var ToolbarController = class {
465
469
  el;
@@ -491,7 +495,9 @@ var ToolbarController = class {
491
495
  view: [],
492
496
  actions: []
493
497
  };
494
- for (const action of this.actions) {
498
+ const hasPageNav = this.actions.some((a) => a.type === "page-nav");
499
+ const effectiveActions = hasPageNav ? this.actions.filter((a) => a.id !== "page-prev" && a.id !== "page-next" && a.id !== "prev" && a.id !== "next") : this.actions;
500
+ for (const action of effectiveActions) {
495
501
  if (groups[action.group]) {
496
502
  groups[action.group].push(action);
497
503
  }
@@ -514,13 +520,26 @@ var ToolbarController = class {
514
520
  "prev",
515
521
  ICON_PAGE_PREV,
516
522
  "Previous Page",
517
- () => action.execute("prev")
523
+ () => {
524
+ const cur = parseInt(input.value, 10) || 1;
525
+ if (cur > 1) {
526
+ input.value = (cur - 1).toString();
527
+ action.execute("prev", cur - 1);
528
+ }
529
+ }
518
530
  );
519
531
  const nextBtn = this.createButton(
520
532
  "next",
521
533
  ICON_PAGE_NEXT,
522
534
  "Next Page",
523
- () => action.execute("next")
535
+ () => {
536
+ const cur = parseInt(input.value, 10) || 1;
537
+ const max = action.max ?? 1;
538
+ if (cur < max) {
539
+ input.value = (cur + 1).toString();
540
+ action.execute("next", cur + 1);
541
+ }
542
+ }
524
543
  );
525
544
  const input = createElement("input", {
526
545
  className: "fp-toolbar-input",
@@ -530,7 +549,10 @@ var ToolbarController = class {
530
549
  max: (action.max ?? 1).toString()
531
550
  });
532
551
  input.addEventListener("change", () => {
533
- action.execute("go", parseInt(input.value, 10));
552
+ const val = parseInt(input.value, 10);
553
+ if (!isNaN(val)) {
554
+ action.execute("go", val);
555
+ }
534
556
  });
535
557
  const label = createElement("span", { className: "fp-toolbar-label" }, ` / ${action.max ?? 1}`);
536
558
  groupEl.appendChild(prevBtn);
@@ -571,11 +593,11 @@ var ToolbarController = class {
571
593
  type: "button",
572
594
  "data-action-id": id
573
595
  });
574
- const svg = ICON_MAP[iconHtml] || iconHtml;
575
- if (svg && svg.startsWith("<svg")) {
596
+ const svg = ICON_MAP[iconHtml] || ICON_MAP[id] || (iconHtml && iconHtml.startsWith("<svg") ? iconHtml : null);
597
+ if (svg) {
576
598
  btn.innerHTML = sanitizeSVG(svg);
577
599
  } else {
578
- btn.textContent = svg || title;
600
+ btn.textContent = title || id;
579
601
  }
580
602
  btn.addEventListener("click", onClick);
581
603
  return btn;
@@ -1318,12 +1340,12 @@ function pdfPlugin() {
1318
1340
  }
1319
1341
 
1320
1342
  // ../plugins/media/dist/index.js
1321
- var IMAGE_EXTS = [".jpg", ".jpeg", ".png", ".gif", ".webp", ".svg"];
1322
- var VIDEO_EXTS = [".mp4", ".webm", ".ogg"];
1323
- var AUDIO_EXTS = [".mp3", ".wav", ".ogg"];
1343
+ var IMAGE_EXTS = [".jpg", ".jpeg", ".png", ".gif", ".webp", ".svg", ".bmp", ".ico", ".tiff", ".tif", ".avif"];
1344
+ var VIDEO_EXTS = [".mp4", ".m4v", ".webm", ".ogv", ".ogg", ".mov", ".avi", ".mkv", ".flv", ".wmv", ".3gp", ".mpg", ".mpeg"];
1345
+ var AUDIO_EXTS = [".mp3", ".wav", ".ogg", ".flac", ".aac", ".m4a", ".wma", ".opus", ".weba"];
1324
1346
  var MediaPlugin = class {
1325
1347
  id = "media";
1326
- name = "Media Preview";
1348
+ name = "Media Preview (Image, Video, Audio)";
1327
1349
  extensions = [...IMAGE_EXTS, ...VIDEO_EXTS, ...AUDIO_EXTS];
1328
1350
  mimeTypes = [
1329
1351
  "image/jpeg",
@@ -1331,12 +1353,29 @@ var MediaPlugin = class {
1331
1353
  "image/gif",
1332
1354
  "image/webp",
1333
1355
  "image/svg+xml",
1356
+ "image/bmp",
1357
+ "image/x-icon",
1358
+ "image/tiff",
1359
+ "image/avif",
1334
1360
  "video/mp4",
1335
1361
  "video/webm",
1336
1362
  "video/ogg",
1363
+ "video/quicktime",
1364
+ "video/x-msvideo",
1365
+ "video/x-matroska",
1366
+ "video/x-flv",
1367
+ "video/x-ms-wmv",
1368
+ "video/3gpp",
1369
+ "video/mpeg",
1337
1370
  "audio/mpeg",
1338
1371
  "audio/wav",
1339
- "audio/ogg"
1372
+ "audio/ogg",
1373
+ "audio/flac",
1374
+ "audio/aac",
1375
+ "audio/mp4",
1376
+ "audio/x-ms-wma",
1377
+ "audio/opus",
1378
+ "audio/webm"
1340
1379
  ];
1341
1380
  weight = 90;
1342
1381
  supports(file) {
@@ -1469,8 +1508,12 @@ var MediaPlugin = class {
1469
1508
  const video = document.createElement("video");
1470
1509
  video.src = url;
1471
1510
  video.controls = true;
1472
- video.style.maxWidth = "100%";
1473
- video.style.maxHeight = "100%";
1511
+ video.playsInline = true;
1512
+ video.style.maxWidth = "90%";
1513
+ video.style.maxHeight = "90%";
1514
+ video.style.borderRadius = "8px";
1515
+ video.style.boxShadow = "0 8px 30px rgba(0,0,0,0.3)";
1516
+ video.style.backgroundColor = "#000000";
1474
1517
  element = video;
1475
1518
  mediaElement = video;
1476
1519
  } else if (isAudio) {
@@ -1546,7 +1589,7 @@ function mediaPlugin() {
1546
1589
  }
1547
1590
  var DocxPlugin = class {
1548
1591
  id = "docx";
1549
- name = "Word Document Preview";
1592
+ name = "Word Document Preview (.docx, .docm, .dotx, .dotm)";
1550
1593
  extensions = [".docx", ".docm", ".dotx", ".dotm"];
1551
1594
  mimeTypes = [
1552
1595
  "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
@@ -1568,9 +1611,7 @@ var DocxPlugin = class {
1568
1611
  label: "Zoom Out",
1569
1612
  type: "button",
1570
1613
  group: "zoom",
1571
- execute: () => {
1572
- instance.zoomOut?.();
1573
- }
1614
+ execute: () => instance.zoomOut?.()
1574
1615
  },
1575
1616
  {
1576
1617
  id: "zoom-in",
@@ -1578,9 +1619,7 @@ var DocxPlugin = class {
1578
1619
  label: "Zoom In",
1579
1620
  type: "button",
1580
1621
  group: "zoom",
1581
- execute: () => {
1582
- instance.zoomIn?.();
1583
- }
1622
+ execute: () => instance.zoomIn?.()
1584
1623
  },
1585
1624
  {
1586
1625
  id: "fit-page",
@@ -1588,9 +1627,7 @@ var DocxPlugin = class {
1588
1627
  label: "Fit to Page",
1589
1628
  type: "button",
1590
1629
  group: "zoom",
1591
- execute: () => {
1592
- instance.fitToPage?.();
1593
- }
1630
+ execute: () => instance.fitToPage?.()
1594
1631
  },
1595
1632
  {
1596
1633
  id: "download",
@@ -1598,9 +1635,7 @@ var DocxPlugin = class {
1598
1635
  label: "Download",
1599
1636
  type: "button",
1600
1637
  group: "actions",
1601
- execute: () => {
1602
- instance.download?.();
1603
- }
1638
+ execute: () => instance.download?.()
1604
1639
  },
1605
1640
  {
1606
1641
  id: "print",
@@ -1608,9 +1643,7 @@ var DocxPlugin = class {
1608
1643
  label: "Print",
1609
1644
  type: "button",
1610
1645
  group: "actions",
1611
- execute: () => {
1612
- instance.print?.();
1613
- }
1646
+ execute: () => instance.print?.()
1614
1647
  }
1615
1648
  ];
1616
1649
  }
@@ -1619,24 +1652,46 @@ var DocxPlugin = class {
1619
1652
  wrapper.className = "fp-docx-wrapper";
1620
1653
  wrapper.style.transformOrigin = "top center";
1621
1654
  wrapper.style.transition = "transform 0.2s ease";
1622
- wrapper.style.padding = "16px";
1655
+ wrapper.style.padding = "24px 16px";
1656
+ wrapper.style.maxWidth = "900px";
1657
+ wrapper.style.margin = "0 auto";
1658
+ wrapper.style.boxSizing = "border-box";
1623
1659
  ctx.container.style.overflow = "auto";
1660
+ ctx.container.style.backgroundColor = "#f1f5f9";
1624
1661
  ctx.container.appendChild(wrapper);
1625
1662
  let scale = 1;
1663
+ let renderedSuccessfully = false;
1626
1664
  try {
1627
1665
  await docx.renderAsync(ctx.buffer, wrapper, ctx.container, {
1628
1666
  inWrapper: true,
1629
1667
  ignoreWidth: false,
1630
- ignoreHeight: false
1668
+ ignoreHeight: false,
1669
+ ignoreFonts: true,
1670
+ // Avoid crashes on embedded obfuscated fonts
1671
+ breakPages: true,
1672
+ experimental: true
1631
1673
  });
1632
- } catch {
1633
- wrapper.innerHTML = `
1634
- <div style="text-align:center; padding: 40px; color: #666;">
1635
- <div style="font-size:48px; margin-bottom: 16px;">\u{1F4C4}</div>
1636
- <h3>${ctx.metadata.name || "Word Document"}</h3>
1637
- <p>DOCX render preview</p>
1638
- </div>
1639
- `;
1674
+ if (wrapper.children.length > 0 && (wrapper.textContent?.trim().length ?? 0) > 0) {
1675
+ renderedSuccessfully = true;
1676
+ }
1677
+ } catch (err) {
1678
+ console.warn("[DocxPlugin] docx-preview failed, triggering native XML fallback:", err);
1679
+ }
1680
+ if (!renderedSuccessfully) {
1681
+ try {
1682
+ wrapper.innerHTML = "";
1683
+ this.renderXmlFallback(ctx, wrapper);
1684
+ renderedSuccessfully = true;
1685
+ } catch (fallbackErr) {
1686
+ console.error("[DocxPlugin] XML fallback failed:", fallbackErr);
1687
+ wrapper.innerHTML = `
1688
+ <div style="text-align:center; padding: 48px; background: #fff; border-radius: 8px; box-shadow: 0 4px 20px rgba(0,0,0,0.06);">
1689
+ <div style="font-size:48px; margin-bottom: 16px;">\u{1F4C4}</div>
1690
+ <h3 style="margin: 0 0 8px; color: #1e293b;">${ctx.metadata.name || "Word Document"}</h3>
1691
+ <p style="color: #64748b; margin: 0;">Could not parse document content</p>
1692
+ </div>
1693
+ `;
1694
+ }
1640
1695
  }
1641
1696
  const cleanup = () => {
1642
1697
  wrapper.remove();
@@ -1660,7 +1715,7 @@ var DocxPlugin = class {
1660
1715
  },
1661
1716
  fitToPage: () => {
1662
1717
  scale = 1;
1663
- wrapper.style.transform = `scale(1)`;
1718
+ wrapper.style.transform = "scale(1)";
1664
1719
  },
1665
1720
  download: () => {
1666
1721
  const blob = new Blob([ctx.buffer], { type: this.mimeTypes[0] });
@@ -1676,6 +1731,100 @@ var DocxPlugin = class {
1676
1731
  }
1677
1732
  };
1678
1733
  }
1734
+ renderXmlFallback(ctx, wrapper) {
1735
+ const unzipped = unzipSync(new Uint8Array(ctx.buffer));
1736
+ const docXmlEntry = unzipped["word/document.xml"];
1737
+ if (!docXmlEntry) throw new Error("Missing word/document.xml in DOCX package");
1738
+ const xmlStr = strFromU8(docXmlEntry);
1739
+ const parser = new DOMParser();
1740
+ const doc = parser.parseFromString(xmlStr, "application/xml");
1741
+ const card = document.createElement("div");
1742
+ card.className = "fp-docx-page-card";
1743
+ card.style.backgroundColor = "#ffffff";
1744
+ card.style.borderRadius = "6px";
1745
+ card.style.boxShadow = "0 4px 24px rgba(0,0,0,0.08)";
1746
+ card.style.padding = "48px 56px";
1747
+ card.style.fontFamily = 'Calibri, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
1748
+ card.style.color = "#1e293b";
1749
+ card.style.lineHeight = "1.6";
1750
+ const body = doc.getElementsByTagNameNS("*", "body")[0] || doc.documentElement;
1751
+ let html = "";
1752
+ for (const child of Array.from(body.children)) {
1753
+ const tag = child.localName || child.nodeName.split(":").pop();
1754
+ if (tag === "p") {
1755
+ const pStyle = child.getElementsByTagNameNS("*", "pStyle")[0];
1756
+ const styleVal = pStyle?.getAttribute("w:val") || pStyle?.getAttribute("val") || "";
1757
+ const numPr = child.getElementsByTagNameNS("*", "numPr")[0];
1758
+ const textContent = this.extractParagraphHtml(child);
1759
+ if (!textContent.trim()) {
1760
+ html += '<div style="height: 10px;"></div>';
1761
+ continue;
1762
+ }
1763
+ const lowerStyle = styleVal.toLowerCase();
1764
+ if (lowerStyle.includes("title")) {
1765
+ 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>`;
1766
+ } else if (lowerStyle.includes("heading1") || styleVal === "1") {
1767
+ html += `<h2 style="font-size: 22px; font-weight: 700; color: #1e40af; margin: 20px 0 10px;">${textContent}</h2>`;
1768
+ } else if (lowerStyle.includes("heading2") || styleVal === "2") {
1769
+ html += `<h3 style="font-size: 18px; font-weight: 600; color: #2563eb; margin: 16px 0 8px;">${textContent}</h3>`;
1770
+ } else if (lowerStyle.includes("heading3") || styleVal === "3") {
1771
+ html += `<h4 style="font-size: 15px; font-weight: 600; color: #334155; margin: 12px 0 6px;">${textContent}</h4>`;
1772
+ } else if (numPr) {
1773
+ 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>`;
1774
+ } else {
1775
+ html += `<p style="margin: 8px 0; font-size: 14px;">${textContent}</p>`;
1776
+ }
1777
+ } else if (tag === "tbl") {
1778
+ html += '<table style="width: 100%; border-collapse: collapse; margin: 20px 0; border: 1px solid #cbd5e1;">';
1779
+ const rows = Array.from(child.getElementsByTagNameNS("*", "tr"));
1780
+ rows.forEach((tr, rIdx) => {
1781
+ html += `<tr style="${rIdx === 0 ? "background-color: #f8fafc; font-weight: 600;" : ""}">`;
1782
+ const cells = Array.from(tr.getElementsByTagNameNS("*", "tc"));
1783
+ cells.forEach((tc) => {
1784
+ const cellText = this.extractParagraphHtml(tc);
1785
+ html += `<td style="border: 1px solid #cbd5e1; padding: 8px 12px; font-size: 13px;">${cellText || "&nbsp;"}</td>`;
1786
+ });
1787
+ html += "</tr>";
1788
+ });
1789
+ html += "</table>";
1790
+ }
1791
+ }
1792
+ card.innerHTML = DOMPurify6.sanitize(html, {
1793
+ ADD_TAGS: ["h1", "h2", "h3", "h4", "p", "table", "tr", "td", "span", "b", "i", "u", "img", "div"],
1794
+ ADD_ATTR: ["style", "src", "alt", "colspan", "rowspan"]
1795
+ });
1796
+ wrapper.appendChild(card);
1797
+ }
1798
+ extractParagraphHtml(pElement) {
1799
+ let result = "";
1800
+ const runs = Array.from(pElement.getElementsByTagNameNS("*", "r"));
1801
+ if (runs.length === 0) {
1802
+ return DOMPurify6.sanitize(pElement.textContent || "");
1803
+ }
1804
+ for (const r of runs) {
1805
+ const rPr = r.getElementsByTagNameNS("*", "rPr")[0];
1806
+ const isBold = !!rPr?.getElementsByTagNameNS("*", "b")[0];
1807
+ const isItalic = !!rPr?.getElementsByTagNameNS("*", "i")[0];
1808
+ const isUnderline = !!rPr?.getElementsByTagNameNS("*", "u")[0];
1809
+ const colorNode = rPr?.getElementsByTagNameNS("*", "color")[0];
1810
+ const colorVal = colorNode?.getAttribute("w:val") || colorNode?.getAttribute("val");
1811
+ const texts = Array.from(r.getElementsByTagNameNS("*", "t"));
1812
+ let text = texts.map((t) => t.textContent || "").join("");
1813
+ if (!text) continue;
1814
+ text = DOMPurify6.sanitize(text);
1815
+ let styles = "";
1816
+ if (isBold) styles += "font-weight: bold; ";
1817
+ if (isItalic) styles += "font-style: italic; ";
1818
+ if (isUnderline) styles += "text-decoration: underline; ";
1819
+ if (colorVal && colorVal !== "auto") styles += `color: #${colorVal}; `;
1820
+ if (styles) {
1821
+ result += `<span style="${styles}">${text}</span>`;
1822
+ } else {
1823
+ result += text;
1824
+ }
1825
+ }
1826
+ return result;
1827
+ }
1679
1828
  };
1680
1829
  function docxPlugin() {
1681
1830
  return new DocxPlugin();
@@ -2487,7 +2636,7 @@ var MarkdownPlugin = class {
2487
2636
  gfm: true,
2488
2637
  breaks: true
2489
2638
  });
2490
- const cleanHtml = DOMPurify5.sanitize(rawHtml, {
2639
+ const cleanHtml = DOMPurify6.sanitize(rawHtml, {
2491
2640
  USE_PROFILES: { html: true }
2492
2641
  });
2493
2642
  const wrapper = document.createElement("div");
@@ -2651,35 +2800,27 @@ var PptxPlugin = class {
2651
2800
  group: "navigation",
2652
2801
  execute: () => instance.toggleThumbnails?.()
2653
2802
  },
2654
- {
2655
- id: "page-prev",
2656
- icon: "page-prev",
2657
- label: "Previous Slide",
2658
- type: "button",
2659
- group: "navigation",
2660
- execute: () => {
2661
- const cur = instance.getCurrentPage?.() ?? 1;
2662
- if (cur > 1) instance.goToPage?.(cur - 1);
2663
- }
2664
- },
2665
2803
  {
2666
2804
  id: "page-nav",
2667
2805
  icon: "",
2668
- label: "Slide Number",
2806
+ label: "Slide Navigation",
2669
2807
  type: "page-nav",
2670
2808
  group: "navigation",
2671
- execute: (page) => instance.goToPage?.(Number(page))
2672
- },
2673
- {
2674
- id: "page-next",
2675
- icon: "page-next",
2676
- label: "Next Slide",
2677
- type: "button",
2678
- group: "navigation",
2679
- execute: () => {
2680
- const cur = instance.getCurrentPage?.() ?? 1;
2681
- const total = instance.getPageCount?.() ?? 1;
2682
- if (cur < total) instance.goToPage?.(cur + 1);
2809
+ value: instance.getCurrentPage?.() ?? 1,
2810
+ max: instance.getPageCount?.() ?? 1,
2811
+ execute: (action, page) => {
2812
+ if (action === "prev") {
2813
+ const cur = instance.getCurrentPage?.() ?? 1;
2814
+ if (cur > 1) instance.goToPage?.(cur - 1);
2815
+ } else if (action === "next") {
2816
+ const cur = instance.getCurrentPage?.() ?? 1;
2817
+ const total = instance.getPageCount?.() ?? 1;
2818
+ if (cur < total) instance.goToPage?.(cur + 1);
2819
+ } else if (typeof page === "number") {
2820
+ instance.goToPage?.(page);
2821
+ } else if (typeof action === "number") {
2822
+ instance.goToPage?.(action);
2823
+ }
2683
2824
  }
2684
2825
  },
2685
2826
  {
@@ -3210,7 +3351,7 @@ var HtmlPreviewPlugin = class {
3210
3351
  }
3211
3352
  async render(ctx) {
3212
3353
  const rawHtml = new TextDecoder("utf-8").decode(ctx.buffer);
3213
- const sanitized = DOMPurify5.sanitize(rawHtml, {
3354
+ const sanitized = DOMPurify6.sanitize(rawHtml, {
3214
3355
  WHOLE_DOCUMENT: true,
3215
3356
  ADD_TAGS: ["style", "link"],
3216
3357
  ADD_ATTR: ["target", "rel"]
@@ -3305,35 +3446,27 @@ var OpenDocumentPlugin = class {
3305
3446
  group: "navigation",
3306
3447
  execute: () => instance.toggleThumbnails?.()
3307
3448
  },
3308
- {
3309
- id: "page-prev",
3310
- icon: "page-prev",
3311
- label: "Previous Slide",
3312
- type: "button",
3313
- group: "navigation",
3314
- execute: () => {
3315
- const cur = instance.getCurrentPage?.() ?? 1;
3316
- if (cur > 1) instance.goToPage?.(cur - 1);
3317
- }
3318
- },
3319
3449
  {
3320
3450
  id: "page-nav",
3321
3451
  icon: "",
3322
- label: "Slide Number",
3452
+ label: "Slide Navigation",
3323
3453
  type: "page-nav",
3324
3454
  group: "navigation",
3325
- execute: (p) => instance.goToPage?.(Number(p))
3326
- },
3327
- {
3328
- id: "page-next",
3329
- icon: "page-next",
3330
- label: "Next Slide",
3331
- type: "button",
3332
- group: "navigation",
3333
- execute: () => {
3334
- const cur = instance.getCurrentPage?.() ?? 1;
3335
- const total = instance.getPageCount?.() ?? 1;
3336
- if (cur < total) instance.goToPage?.(cur + 1);
3455
+ value: instance.getCurrentPage?.() ?? 1,
3456
+ max: instance.getPageCount?.() ?? 1,
3457
+ execute: (action, page) => {
3458
+ if (action === "prev") {
3459
+ const cur = instance.getCurrentPage?.() ?? 1;
3460
+ if (cur > 1) instance.goToPage?.(cur - 1);
3461
+ } else if (action === "next") {
3462
+ const cur = instance.getCurrentPage?.() ?? 1;
3463
+ const total = instance.getPageCount?.() ?? 1;
3464
+ if (cur < total) instance.goToPage?.(cur + 1);
3465
+ } else if (typeof page === "number") {
3466
+ instance.goToPage?.(page);
3467
+ } else if (typeof action === "number") {
3468
+ instance.goToPage?.(action);
3469
+ }
3337
3470
  }
3338
3471
  }
3339
3472
  );
@@ -3461,7 +3594,7 @@ var OpenDocumentPlugin = class {
3461
3594
  wrapper.style.padding = "48px";
3462
3595
  wrapper.style.minHeight = "100%";
3463
3596
  const bodyHtml = this.renderOdfBody(contentDoc, styleMap, imageUrls);
3464
- wrapper.innerHTML = DOMPurify5.sanitize(bodyHtml, {
3597
+ wrapper.innerHTML = DOMPurify6.sanitize(bodyHtml, {
3465
3598
  ADD_TAGS: ["math", "semantics", "mrow", "mi", "mo", "mn", "msup", "msub"],
3466
3599
  ADD_ATTR: ["style", "colspan", "rowspan"]
3467
3600
  });
@@ -3981,24 +4114,24 @@ var DocPlugin = class {
3981
4114
  html += "</ul>";
3982
4115
  inList = false;
3983
4116
  }
3984
- html += `<h2 style="font-size: 18px; font-weight: 700; color: #1e3a8a; margin: 20px 0 8px; border-bottom: 1px solid #e2e8f0; padding-bottom: 4px;">${DOMPurify5.sanitize(line)}</h2>`;
4117
+ html += `<h2 style="font-size: 18px; font-weight: 700; color: #1e3a8a; margin: 20px 0 8px; border-bottom: 1px solid #e2e8f0; padding-bottom: 4px;">${DOMPurify6.sanitize(line)}</h2>`;
3985
4118
  } else if (line.startsWith("\u2022") || line.startsWith("-") || line.startsWith("*")) {
3986
4119
  if (!inList) {
3987
4120
  html += '<ul style="margin: 8px 0; padding-left: 24px;">';
3988
4121
  inList = true;
3989
4122
  }
3990
4123
  const bulletText = line.replace(/^[•\-\*]\s*/, "");
3991
- html += `<li style="margin: 4px 0; line-height: 1.6;">${DOMPurify5.sanitize(bulletText)}</li>`;
4124
+ html += `<li style="margin: 4px 0; line-height: 1.6;">${DOMPurify6.sanitize(bulletText)}</li>`;
3992
4125
  } else {
3993
4126
  if (inList) {
3994
4127
  html += "</ul>";
3995
4128
  inList = false;
3996
4129
  }
3997
- html += `<p style="line-height: 1.7; margin: 10px 0; font-size: 14px; text-align: justify;">${DOMPurify5.sanitize(line)}</p>`;
4130
+ html += `<p style="line-height: 1.7; margin: 10px 0; font-size: 14px; text-align: justify;">${DOMPurify6.sanitize(line)}</p>`;
3998
4131
  }
3999
4132
  }
4000
4133
  if (inList) html += "</ul>";
4001
- return html || `<p style="color: #64748b; font-style: italic;">(No readable text found in ${DOMPurify5.sanitize(filename)})</p>`;
4134
+ return html || `<p style="color: #64748b; font-style: italic;">(No readable text found in ${DOMPurify6.sanitize(filename)})</p>`;
4002
4135
  }
4003
4136
  };
4004
4137
  function docPlugin() {
@@ -4025,35 +4158,27 @@ var PptPlugin = class {
4025
4158
  group: "navigation",
4026
4159
  execute: () => instance.toggleThumbnails?.()
4027
4160
  },
4028
- {
4029
- id: "page-prev",
4030
- icon: "page-prev",
4031
- label: "Previous Slide",
4032
- type: "button",
4033
- group: "navigation",
4034
- execute: () => {
4035
- const cur = instance.getCurrentPage?.() ?? 1;
4036
- if (cur > 1) instance.goToPage?.(cur - 1);
4037
- }
4038
- },
4039
4161
  {
4040
4162
  id: "page-nav",
4041
4163
  icon: "",
4042
- label: "Slide Number",
4164
+ label: "Slide Navigation",
4043
4165
  type: "page-nav",
4044
4166
  group: "navigation",
4045
- execute: (p) => instance.goToPage?.(Number(p))
4046
- },
4047
- {
4048
- id: "page-next",
4049
- icon: "page-next",
4050
- label: "Next Slide",
4051
- type: "button",
4052
- group: "navigation",
4053
- execute: () => {
4054
- const cur = instance.getCurrentPage?.() ?? 1;
4055
- const total = instance.getPageCount?.() ?? 1;
4056
- if (cur < total) instance.goToPage?.(cur + 1);
4167
+ value: instance.getCurrentPage?.() ?? 1,
4168
+ max: instance.getPageCount?.() ?? 1,
4169
+ execute: (action, page) => {
4170
+ if (action === "prev") {
4171
+ const cur = instance.getCurrentPage?.() ?? 1;
4172
+ if (cur > 1) instance.goToPage?.(cur - 1);
4173
+ } else if (action === "next") {
4174
+ const cur = instance.getCurrentPage?.() ?? 1;
4175
+ const total = instance.getPageCount?.() ?? 1;
4176
+ if (cur < total) instance.goToPage?.(cur + 1);
4177
+ } else if (typeof page === "number") {
4178
+ instance.goToPage?.(page);
4179
+ } else if (typeof action === "number") {
4180
+ instance.goToPage?.(action);
4181
+ }
4057
4182
  }
4058
4183
  },
4059
4184
  {
@@ -4161,10 +4286,10 @@ var PptPlugin = class {
4161
4286
  </div>
4162
4287
  <div style="text-align: center; width: 100%;">
4163
4288
  <h1 style="font-size: ${idx === 1 ? "36px" : "28px"}; color: #1e3a8a; margin: 0 0 24px; font-family: -apple-system, BlinkMacSystemFont, sans-serif; font-weight: 700;">
4164
- ${DOMPurify5.sanitize(s.title || `Slide ${idx}`)}
4289
+ ${DOMPurify6.sanitize(s.title || `Slide ${idx}`)}
4165
4290
  </h1>
4166
4291
  <div style="display: flex; flex-direction: column; gap: 12px; max-width: 80%; margin: 0 auto; text-align: ${idx === 1 ? "center" : "left"};">
4167
- ${s.texts.map((t) => `<div style="font-size: 18px; color: #334155; line-height: 1.5; font-family: -apple-system, BlinkMacSystemFont, sans-serif;">${DOMPurify5.sanitize(t)}</div>`).join("")}
4292
+ ${s.texts.map((t) => `<div style="font-size: 18px; color: #334155; line-height: 1.5; font-family: -apple-system, BlinkMacSystemFont, sans-serif;">${DOMPurify6.sanitize(t)}</div>`).join("")}
4168
4293
  </div>
4169
4294
  </div>
4170
4295
  `;