@files-preview-app/preview-file 1.2.6 → 1.2.8

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.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var DOMPurify2 = require('dompurify');
3
+ var DOMPurify6 = require('dompurify');
4
4
  var pdfjsLib = require('pdfjs-dist');
5
5
  var docx = require('docx-preview');
6
6
  var fflate = require('fflate');
@@ -34,7 +34,7 @@ function _interopNamespace(e) {
34
34
  return Object.freeze(n);
35
35
  }
36
36
 
37
- var DOMPurify2__default = /*#__PURE__*/_interopDefault(DOMPurify2);
37
+ var DOMPurify6__default = /*#__PURE__*/_interopDefault(DOMPurify6);
38
38
  var pdfjsLib__namespace = /*#__PURE__*/_interopNamespace(pdfjsLib);
39
39
  var docx__namespace = /*#__PURE__*/_interopNamespace(docx);
40
40
  var XLSX__namespace = /*#__PURE__*/_interopNamespace(XLSX);
@@ -386,7 +386,7 @@ async function sourceToArrayBuffer(source, signal) {
386
386
  if (magicMime === "application/zip") {
387
387
  const ooxmlMime = detectOoxmlType(buffer);
388
388
  if (ooxmlMime && ooxmlMime !== "application/zip") {
389
- metadata.mimeType = ooxmlMime;
389
+ metadata.mimeType = metadata.mimeType ?? ooxmlMime;
390
390
  if (ooxmlMime.includes("wordprocessing")) metadata.extension = metadata.extension ?? ".docx";
391
391
  else if (ooxmlMime.includes("spreadsheet")) metadata.extension = metadata.extension ?? ".xlsx";
392
392
  else if (ooxmlMime.includes("presentation")) metadata.extension = metadata.extension ?? ".pptx";
@@ -415,12 +415,12 @@ async function sourceToArrayBuffer(source, signal) {
415
415
  return { buffer, metadata };
416
416
  }
417
417
  function sanitizeHTML(html) {
418
- return DOMPurify2__default.default.sanitize(html, {
418
+ return DOMPurify6__default.default.sanitize(html, {
419
419
  USE_PROFILES: { html: true }
420
420
  });
421
421
  }
422
422
  function sanitizeSVG(svg) {
423
- return DOMPurify2__default.default.sanitize(svg, {
423
+ return DOMPurify6__default.default.sanitize(svg, {
424
424
  USE_PROFILES: { svg: true, svgFilters: true }
425
425
  });
426
426
  }
@@ -506,7 +506,8 @@ function createElement(tag, attrs, ...children) {
506
506
  }
507
507
  var ICON_ZOOM_IN = `<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="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line><line x1="11" y1="8" x2="11" y2="14"></line><line x1="8" y1="11" x2="14" y2="11"></line></svg>`;
508
508
  var ICON_ZOOM_OUT = `<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="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line><line x1="8" y1="11" x2="14" y2="11"></line></svg>`;
509
- var ICON_FIT_PAGE = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" y1="3" x2="14" y2="10"></line><line x1="3" y1="21" x2="10" y2="14"></line></svg>`;
509
+ var ICON_FIT_PAGE = `<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="3" y="3" width="18" height="18" rx="2"></rect><line x1="8" y1="12" x2="16" y2="12"></line><polyline points="11 9 8 12 11 15"></polyline><polyline points="13 9 16 12 13 15"></polyline></svg>`;
510
+ var ICON_FIT_WIDTH = `<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="2" y1="12" x2="22" y2="12"></line><polyline points="5 9 2 12 5 15"></polyline><polyline points="19 9 22 12 19 15"></polyline><line x1="2" y1="5" x2="2" y2="19"></line><line x1="22" y1="5" x2="22" y2="19"></line></svg>`;
510
511
  var ICON_ROTATE_CW = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="23 4 23 10 17 10"></polyline><path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"></path></svg>`;
511
512
  var ICON_ROTATE_CCW = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="1 4 1 10 7 10"></polyline><path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"></path></svg>`;
512
513
  var ICON_DOWNLOAD = `<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="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg>`;
@@ -526,6 +527,7 @@ var ICON_MAP = {
526
527
  "zoom-in": ICON_ZOOM_IN,
527
528
  "zoom-out": ICON_ZOOM_OUT,
528
529
  "fit-page": ICON_FIT_PAGE,
530
+ "fit-width": ICON_FIT_WIDTH,
529
531
  "fit-slide": ICON_FIT_PAGE,
530
532
  "rotate-cw": ICON_ROTATE_CW,
531
533
  "rotate-ccw": ICON_ROTATE_CCW,
@@ -1472,6 +1474,7 @@ var PdfPlugin = class {
1472
1474
  let currentPage = 1;
1473
1475
  let zoomScale = 1;
1474
1476
  let rotation = 0;
1477
+ let fitMode = "width";
1475
1478
  let currentRenderTask = null;
1476
1479
  const renderPage = async (pageNum) => {
1477
1480
  if (currentRenderTask) {
@@ -1491,11 +1494,16 @@ var PdfPlugin = class {
1491
1494
  const containerWidth = container.clientWidth || 900;
1492
1495
  const containerHeight = container.clientHeight || 700;
1493
1496
  const unscaledVp = page.getViewport({ scale: 1, rotation });
1494
- const availWidth = Math.max(100, containerWidth - 48);
1495
- const availHeight = Math.max(100, containerHeight - 88);
1497
+ const availWidth = Math.max(280, containerWidth - 48);
1498
+ const availHeight = Math.max(280, containerHeight - 88);
1496
1499
  const scaleW = availWidth / unscaledVp.width;
1497
1500
  const scaleH = availHeight / unscaledVp.height;
1498
- const fitScale = Math.min(scaleW, scaleH);
1501
+ let fitScale;
1502
+ if (fitMode === "page") {
1503
+ fitScale = Math.max(0.5, Math.min(scaleW, scaleH));
1504
+ } else {
1505
+ fitScale = Math.max(0.65, Math.min(1.15, scaleW));
1506
+ }
1499
1507
  const effectiveScale = (fitScale > 0 ? fitScale : 1) * zoomScale;
1500
1508
  const pixelRatio = window.devicePixelRatio || 1;
1501
1509
  const viewport = page.getViewport({ scale: effectiveScale, rotation });
@@ -1521,7 +1529,19 @@ var PdfPlugin = class {
1521
1529
  }
1522
1530
  };
1523
1531
  await renderPage(1);
1532
+ let resizeTimer = null;
1533
+ const resizeObserver = new ResizeObserver(() => {
1534
+ if (resizeTimer) clearTimeout(resizeTimer);
1535
+ resizeTimer = setTimeout(() => {
1536
+ if (Math.abs(zoomScale - 1) < 0.05) {
1537
+ renderPage(currentPage);
1538
+ }
1539
+ }, 150);
1540
+ });
1541
+ resizeObserver.observe(container);
1524
1542
  const cleanup = () => {
1543
+ resizeObserver.disconnect();
1544
+ if (resizeTimer) clearTimeout(resizeTimer);
1525
1545
  if (currentRenderTask) {
1526
1546
  try {
1527
1547
  currentRenderTask.cancel();
@@ -1552,6 +1572,7 @@ var PdfPlugin = class {
1552
1572
  renderPage(currentPage);
1553
1573
  },
1554
1574
  fitToPage: () => {
1575
+ fitMode = fitMode === "width" ? "page" : "width";
1555
1576
  zoomScale = 1;
1556
1577
  rotation = 0;
1557
1578
  renderPage(currentPage);
@@ -2105,8 +2126,53 @@ var DocxPlugin = class {
2105
2126
  }
2106
2127
  }
2107
2128
  }
2108
- const sections = wrapper.querySelectorAll("section.docx");
2109
- const cards = wrapper.querySelectorAll(".fp-docx-page-card");
2129
+ let sections = Array.from(wrapper.querySelectorAll("section.docx"));
2130
+ const cards = Array.from(wrapper.querySelectorAll(".fp-docx-page-card"));
2131
+ if (sections.length === 1 && cards.length === 0) {
2132
+ const singleSec = sections[0];
2133
+ const pageH = 1056;
2134
+ const secH = singleSec.offsetHeight || singleSec.scrollHeight;
2135
+ if (secH > pageH * 1.25) {
2136
+ const children = Array.from(singleSec.children);
2137
+ if (children.length > 1) {
2138
+ const parent = singleSec.parentElement || wrapper;
2139
+ const newSections = [singleSec];
2140
+ singleSec.innerHTML = "";
2141
+ singleSec.style.minHeight = `${pageH}px`;
2142
+ singleSec.style.maxHeight = `${pageH}px`;
2143
+ singleSec.style.overflow = "hidden";
2144
+ singleSec.style.boxSizing = "border-box";
2145
+ let curSec = singleSec;
2146
+ let curH = 0;
2147
+ const maxH = pageH - 96;
2148
+ for (let i = 0; i < children.length; i++) {
2149
+ const child = children[i];
2150
+ curSec.appendChild(child);
2151
+ const chH = child.offsetHeight || 28;
2152
+ curH += chH;
2153
+ if (curH >= maxH && i < children.length - 1) {
2154
+ const nextSec = document.createElement("section");
2155
+ nextSec.className = singleSec.className;
2156
+ nextSec.style.cssText = singleSec.style.cssText;
2157
+ nextSec.style.width = singleSec.style.width || "816px";
2158
+ nextSec.style.minHeight = `${pageH}px`;
2159
+ nextSec.style.maxHeight = `${pageH}px`;
2160
+ nextSec.style.overflow = "hidden";
2161
+ nextSec.style.boxSizing = "border-box";
2162
+ nextSec.style.backgroundColor = "#ffffff";
2163
+ nextSec.style.boxShadow = "0 4px 24px rgba(0, 0, 0, 0.08)";
2164
+ nextSec.style.borderRadius = "4px";
2165
+ nextSec.style.marginBottom = "24px";
2166
+ parent.appendChild(nextSec);
2167
+ newSections.push(nextSec);
2168
+ curSec = nextSec;
2169
+ curH = 0;
2170
+ }
2171
+ }
2172
+ sections = newSections;
2173
+ }
2174
+ }
2175
+ }
2110
2176
  const pageElements = sections.length > 0 ? sections : cards;
2111
2177
  const totalPages = Math.max(1, pageElements.length);
2112
2178
  let currentPage = 1;
@@ -2150,22 +2216,27 @@ var DocxPlugin = class {
2150
2216
  }
2151
2217
  scale = 1;
2152
2218
  let rotation = 0;
2153
- const calculateFitScale = () => {
2219
+ let fitMode = "width";
2220
+ const calculateFitScale = (mode = fitMode) => {
2154
2221
  const activeEl = pageElements[currentPage - 1] || wrapper.firstElementChild || wrapper;
2155
2222
  const elW = activeEl.offsetWidth || 816;
2156
- const elH = activeEl.offsetHeight || 1056;
2157
- const availW = Math.max(200, ctx.container.clientWidth - 48);
2158
- const availH = Math.max(200, ctx.container.clientHeight - 80);
2223
+ const singlePageH = Math.min(activeEl.offsetHeight || 1056, Math.round(elW * 1.32));
2224
+ const availW = Math.max(280, ctx.container.clientWidth - 48);
2225
+ const availH = Math.max(280, ctx.container.clientHeight - 80);
2159
2226
  const sW = availW / elW;
2160
- const sH = availH / elH;
2161
- return Math.min(1.1, Math.min(sW, sH));
2227
+ const sH = availH / singlePageH;
2228
+ if (mode === "page") {
2229
+ return Math.max(0.55, Math.min(1.15, Math.min(sW, sH)));
2230
+ }
2231
+ return Math.max(0.65, Math.min(1.05, sW));
2162
2232
  };
2163
2233
  const applyTransform = () => {
2164
2234
  wrapper.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
2165
2235
  wrapper.style.transformOrigin = "top center";
2166
2236
  };
2167
2237
  setTimeout(() => {
2168
- scale = calculateFitScale();
2238
+ fitMode = "width";
2239
+ scale = calculateFitScale("width");
2169
2240
  applyTransform();
2170
2241
  }, 60);
2171
2242
  const cleanup = () => {
@@ -2199,7 +2270,8 @@ var DocxPlugin = class {
2199
2270
  applyTransform();
2200
2271
  },
2201
2272
  fitToPage: () => {
2202
- scale = calculateFitScale();
2273
+ fitMode = fitMode === "width" ? "page" : "width";
2274
+ scale = calculateFitScale(fitMode);
2203
2275
  rotation = 0;
2204
2276
  applyTransform();
2205
2277
  },
@@ -2269,90 +2341,149 @@ var DocxPlugin = class {
2269
2341
  console.warn("[DocxPlugin] Error parsing relationships:", relsErr);
2270
2342
  }
2271
2343
  }
2272
- const card = document.createElement("div");
2273
- card.className = "fp-docx-page-card";
2274
- card.style.backgroundColor = "#ffffff";
2275
- card.style.borderRadius = "6px";
2276
- card.style.boxShadow = "0 4px 24px rgba(0,0,0,0.08)";
2277
- card.style.padding = "48px 56px";
2278
- card.style.fontFamily = 'Calibri, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
2279
- card.style.color = "#1e293b";
2280
- card.style.lineHeight = "1.6";
2344
+ const sectPrs = Array.from(doc.getElementsByTagNameNS("*", "sectPr"));
2345
+ const lastSectPr = sectPrs[sectPrs.length - 1];
2346
+ let defaultW = 12240;
2347
+ let defaultH = 15840;
2348
+ let margins = { top: 1440, right: 1440, bottom: 1440, left: 1440 };
2349
+ if (lastSectPr) {
2350
+ const pgSz = lastSectPr.getElementsByTagNameNS("*", "pgSz")[0];
2351
+ if (pgSz) {
2352
+ defaultW = parseInt(pgSz.getAttribute("w:w") || pgSz.getAttribute("w") || "12240", 10);
2353
+ defaultH = parseInt(pgSz.getAttribute("w:h") || pgSz.getAttribute("h") || "15840", 10);
2354
+ }
2355
+ const pgMar = lastSectPr.getElementsByTagNameNS("*", "pgMar")[0];
2356
+ if (pgMar) {
2357
+ margins.top = parseInt(pgMar.getAttribute("w:top") || pgMar.getAttribute("top") || "1440", 10);
2358
+ margins.bottom = parseInt(pgMar.getAttribute("w:bottom") || pgMar.getAttribute("bottom") || "1440", 10);
2359
+ margins.left = parseInt(pgMar.getAttribute("w:left") || pgMar.getAttribute("left") || "1440", 10);
2360
+ margins.right = parseInt(pgMar.getAttribute("w:right") || pgMar.getAttribute("right") || "1440", 10);
2361
+ }
2362
+ }
2363
+ const createPageCard = () => {
2364
+ const card = document.createElement("div");
2365
+ card.className = "fp-docx-page-card";
2366
+ card.style.backgroundColor = "#ffffff";
2367
+ card.style.borderRadius = "4px";
2368
+ card.style.boxShadow = "0 4px 24px rgba(0,0,0,0.08)";
2369
+ card.style.fontFamily = 'Calibri, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
2370
+ card.style.color = "#1e293b";
2371
+ card.style.lineHeight = "1.6";
2372
+ card.style.boxSizing = "border-box";
2373
+ card.style.overflow = "hidden";
2374
+ card.style.position = "relative";
2375
+ card.style.marginBottom = "24px";
2376
+ card.style.width = `${defaultW / 15}px`;
2377
+ card.style.height = `${defaultH / 15}px`;
2378
+ card.style.padding = `${margins.top / 15}px ${margins.right / 15}px ${margins.bottom / 15}px ${margins.left / 15}px`;
2379
+ return card;
2380
+ };
2381
+ let currentCard = createPageCard();
2382
+ let currentHtml = "";
2383
+ const pages = [{ card: currentCard, html: "" }];
2384
+ const flushHtml = () => {
2385
+ pages[pages.length - 1].html += currentHtml;
2386
+ currentHtml = "";
2387
+ };
2388
+ const newPage = () => {
2389
+ flushHtml();
2390
+ currentCard = createPageCard();
2391
+ pages.push({ card: currentCard, html: "" });
2392
+ };
2281
2393
  const body = doc.getElementsByTagNameNS("*", "body")[0] || doc.documentElement;
2282
- let html = "";
2283
2394
  for (const child of Array.from(body.children)) {
2284
2395
  const tag = child.localName || child.nodeName.split(":").pop();
2285
2396
  if (tag === "p") {
2286
2397
  const pStyle = child.getElementsByTagNameNS("*", "pStyle")[0];
2287
2398
  const styleVal = pStyle?.getAttribute("w:val") || pStyle?.getAttribute("val") || "";
2288
2399
  const numPr = child.getElementsByTagNameNS("*", "numPr")[0];
2289
- const textContent = this.extractParagraphHtml(child, imageMap);
2290
- if (!textContent.trim()) {
2291
- html += '<div style="height: 10px;"></div>';
2292
- continue;
2293
- }
2294
- const lowerStyle = styleVal.toLowerCase();
2295
- if (lowerStyle.includes("title")) {
2296
- 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>`;
2297
- } else if (lowerStyle.includes("heading1") || styleVal === "1") {
2298
- html += `<h2 style="font-size: 22px; font-weight: 700; color: #1e40af; margin: 20px 0 10px;">${textContent}</h2>`;
2299
- } else if (lowerStyle.includes("heading2") || styleVal === "2") {
2300
- html += `<h3 style="font-size: 18px; font-weight: 600; color: #2563eb; margin: 16px 0 8px;">${textContent}</h3>`;
2301
- } else if (lowerStyle.includes("heading3") || styleVal === "3") {
2302
- html += `<h4 style="font-size: 15px; font-weight: 600; color: #334155; margin: 12px 0 6px;">${textContent}</h4>`;
2303
- } else if (numPr) {
2304
- 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>`;
2305
- } else {
2306
- html += `<p style="margin: 8px 0; font-size: 14px;">${textContent}</p>`;
2400
+ const chunks = this.extractParagraphChunks(child, imageMap);
2401
+ for (let i = 0; i < chunks.length; i++) {
2402
+ if (i > 0) {
2403
+ newPage();
2404
+ }
2405
+ const textContent = chunks[i];
2406
+ if (!textContent.trim() && !textContent.includes("<img")) {
2407
+ currentHtml += '<div style="height: 10px;"></div>';
2408
+ continue;
2409
+ }
2410
+ const lowerStyle = styleVal.toLowerCase();
2411
+ if (lowerStyle.includes("title")) {
2412
+ currentHtml += `<h1 style="font-size: 28px; font-weight: 700; color: #1e3a8a; margin: 24px 0 12px; border-bottom: 2px solid #e2e8f0; padding-bottom: 8px;">${textContent}</h1>`;
2413
+ } else if (lowerStyle.includes("heading1") || styleVal === "1") {
2414
+ currentHtml += `<h2 style="font-size: 22px; font-weight: 700; color: #1e40af; margin: 20px 0 10px;">${textContent}</h2>`;
2415
+ } else if (lowerStyle.includes("heading2") || styleVal === "2") {
2416
+ currentHtml += `<h3 style="font-size: 18px; font-weight: 600; color: #2563eb; margin: 16px 0 8px;">${textContent}</h3>`;
2417
+ } else if (lowerStyle.includes("heading3") || styleVal === "3") {
2418
+ currentHtml += `<h4 style="font-size: 15px; font-weight: 600; color: #334155; margin: 12px 0 6px;">${textContent}</h4>`;
2419
+ } else if (numPr) {
2420
+ currentHtml += `<div style="display: flex; gap: 8px; margin: 4px 0 4px 20px;"><span style="color: #2563eb; font-weight: bold;">\u2022</span><span>${textContent}</span></div>`;
2421
+ } else {
2422
+ currentHtml += `<p style="margin: 8px 0; font-size: 14px;">${textContent}</p>`;
2423
+ }
2307
2424
  }
2308
2425
  } else if (tag === "tbl") {
2309
- html += '<table style="width: 100%; border-collapse: collapse; margin: 20px 0; border: 1px solid #cbd5e1;">';
2426
+ currentHtml += '<table style="width: 100%; border-collapse: collapse; margin: 20px 0; border: 1px solid #cbd5e1;">';
2310
2427
  const rows = Array.from(child.getElementsByTagNameNS("*", "tr"));
2311
2428
  rows.forEach((tr, rIdx) => {
2312
- html += `<tr style="${rIdx === 0 ? "background-color: #f8fafc; font-weight: 600;" : ""}">`;
2429
+ currentHtml += `<tr style="${rIdx === 0 ? "background-color: #f8fafc; font-weight: 600;" : ""}">`;
2313
2430
  const cells = Array.from(tr.getElementsByTagNameNS("*", "tc"));
2314
2431
  cells.forEach((tc) => {
2315
2432
  const cellText = this.extractParagraphHtml(tc, imageMap);
2316
- html += `<td style="border: 1px solid #cbd5e1; padding: 8px 12px; font-size: 13px;">${cellText || "&nbsp;"}</td>`;
2433
+ currentHtml += `<td style="border: 1px solid #cbd5e1; padding: 8px 12px; font-size: 13px;">${cellText || "&nbsp;"}</td>`;
2317
2434
  });
2318
- html += "</tr>";
2435
+ currentHtml += "</tr>";
2319
2436
  });
2320
- html += "</table>";
2437
+ currentHtml += "</table>";
2321
2438
  }
2322
2439
  }
2323
- card.innerHTML = DOMPurify2__default.default.sanitize(html, {
2324
- ADD_TAGS: ["h1", "h2", "h3", "h4", "p", "table", "tr", "td", "span", "b", "i", "u", "s", "strike", "img", "div", "br"],
2325
- ADD_ATTR: ["style", "src", "alt", "colspan", "rowspan"]
2326
- });
2327
- wrapper.appendChild(card);
2440
+ flushHtml();
2441
+ for (const page of pages) {
2442
+ page.card.innerHTML = DOMPurify6__default.default.sanitize(page.html, {
2443
+ ADD_TAGS: ["h1", "h2", "h3", "h4", "p", "table", "tr", "td", "span", "b", "i", "u", "s", "strike", "img", "div", "br"],
2444
+ ADD_ATTR: ["style", "src", "alt", "colspan", "rowspan"]
2445
+ });
2446
+ wrapper.appendChild(page.card);
2447
+ }
2328
2448
  }
2329
2449
  extractParagraphHtml(pElement, imageMap = {}) {
2330
- let result = "";
2450
+ return this.extractParagraphChunks(pElement, imageMap).join("<br/>");
2451
+ }
2452
+ extractParagraphChunks(pElement, imageMap = {}) {
2453
+ const chunks = [""];
2454
+ let currentChunkIndex = 0;
2331
2455
  const drawings = Array.from(pElement.getElementsByTagNameNS("*", "drawing"));
2332
2456
  for (const drawing of drawings) {
2333
2457
  const blip = drawing.getElementsByTagNameNS("*", "blip")[0];
2334
2458
  const rId = blip?.getAttribute("r:embed") || blip?.getAttribute("r:id");
2335
2459
  if (rId && imageMap[rId]) {
2336
- result += `<div style="text-align:center; margin: 12px 0;"><img src="${imageMap[rId]}" style="max-width: 100%; height: auto; border-radius: 4px;" /></div>`;
2460
+ chunks[currentChunkIndex] += `<div style="text-align:center; margin: 12px 0;"><img src="${imageMap[rId]}" style="max-width: 100%; height: auto; border-radius: 4px;" /></div>`;
2337
2461
  }
2338
2462
  }
2339
2463
  const runs = Array.from(pElement.getElementsByTagNameNS("*", "r"));
2340
- if (runs.length === 0 && !result) {
2341
- return DOMPurify2__default.default.sanitize(pElement.textContent || "");
2464
+ if (runs.length === 0 && !chunks[currentChunkIndex]) {
2465
+ chunks[currentChunkIndex] = DOMPurify6__default.default.sanitize(pElement.textContent || "");
2466
+ return chunks;
2342
2467
  }
2343
2468
  for (const r of runs) {
2344
2469
  const blip = r.getElementsByTagNameNS("*", "blip")[0] || r.getElementsByTagNameNS("*", "imagedata")[0];
2345
2470
  const rId = blip?.getAttribute("r:embed") || blip?.getAttribute("r:id");
2346
2471
  if (rId && imageMap[rId]) {
2347
- result += `<img src="${imageMap[rId]}" style="max-width: 100%; height: auto; display: inline-block; margin: 4px;" />`;
2472
+ chunks[currentChunkIndex] += `<img src="${imageMap[rId]}" style="max-width: 100%; height: auto; display: inline-block; margin: 4px;" />`;
2348
2473
  }
2349
- const brs = r.getElementsByTagNameNS("*", "br");
2350
- for (let i = 0; i < brs.length; i++) {
2351
- result += "<br/>";
2474
+ const brs = Array.from(r.getElementsByTagNameNS("*", "br"));
2475
+ for (const br of brs) {
2476
+ const type = br.getAttribute("w:type") || br.getAttribute("type");
2477
+ if (type === "page") {
2478
+ chunks.push("");
2479
+ currentChunkIndex++;
2480
+ } else {
2481
+ chunks[currentChunkIndex] += "<br/>";
2482
+ }
2352
2483
  }
2353
2484
  const tabs = r.getElementsByTagNameNS("*", "tab");
2354
2485
  if (tabs.length > 0) {
2355
- result += "&emsp;";
2486
+ chunks[currentChunkIndex] += "&emsp;";
2356
2487
  }
2357
2488
  const rPr = r.getElementsByTagNameNS("*", "rPr")[0];
2358
2489
  const isBold = !!rPr?.getElementsByTagNameNS("*", "b")[0];
@@ -2366,7 +2497,7 @@ var DocxPlugin = class {
2366
2497
  const texts = Array.from(r.getElementsByTagNameNS("*", "t"));
2367
2498
  let text = texts.map((t) => t.textContent || "").join("");
2368
2499
  if (!text) continue;
2369
- text = DOMPurify2__default.default.sanitize(text);
2500
+ text = DOMPurify6__default.default.sanitize(text);
2370
2501
  let styles = "";
2371
2502
  if (isBold) styles += "font-weight: bold; ";
2372
2503
  if (isItalic) styles += "font-style: italic; ";
@@ -2378,12 +2509,12 @@ var DocxPlugin = class {
2378
2509
  if (!isNaN(pt) && pt > 0) styles += `font-size: ${pt}pt; `;
2379
2510
  }
2380
2511
  if (styles) {
2381
- result += `<span style="${styles}">${text}</span>`;
2512
+ chunks[currentChunkIndex] += `<span style="${styles}">${text}</span>`;
2382
2513
  } else {
2383
- result += text;
2514
+ chunks[currentChunkIndex] += text;
2384
2515
  }
2385
2516
  }
2386
- return result;
2517
+ return chunks;
2387
2518
  }
2388
2519
  renderBinaryDocFallback(ctx, wrapper) {
2389
2520
  const card = document.createElement("div");
@@ -2400,14 +2531,14 @@ var DocxPlugin = class {
2400
2531
  const wordDocStream = cfbf.readStream("WordDocument");
2401
2532
  if (wordDocStream && wordDocStream.length >= 512) {
2402
2533
  const text2 = this.extractReadableStrings(wordDocStream);
2403
- card.innerHTML = `<div style="white-space: pre-wrap;">${DOMPurify2__default.default.sanitize(text2)}</div>`;
2534
+ card.innerHTML = `<div style="white-space: pre-wrap;">${DOMPurify6__default.default.sanitize(text2)}</div>`;
2404
2535
  wrapper.appendChild(card);
2405
2536
  return;
2406
2537
  }
2407
2538
  } catch {
2408
2539
  }
2409
2540
  const text = this.extractReadableStrings(new Uint8Array(ctx.buffer));
2410
- card.innerHTML = `<div style="white-space: pre-wrap;">${DOMPurify2__default.default.sanitize(text)}</div>`;
2541
+ card.innerHTML = `<div style="white-space: pre-wrap;">${DOMPurify6__default.default.sanitize(text)}</div>`;
2411
2542
  wrapper.appendChild(card);
2412
2543
  }
2413
2544
  extractReadableStrings(bytes) {
@@ -2584,11 +2715,9 @@ var ExcelPlugin = class {
2584
2715
  const calculateFitScale = () => {
2585
2716
  const table = contentArea.querySelector("table");
2586
2717
  if (!table) return 1;
2587
- const availW = Math.max(200, container.clientWidth - 48);
2588
- const availH = Math.max(200, container.clientHeight - 80);
2718
+ const availW = Math.max(280, container.clientWidth - 48);
2589
2719
  const tW = table.offsetWidth || 800;
2590
- const tH = table.offsetHeight || 600;
2591
- return Math.min(1, Math.min(availW / tW, availH / tH));
2720
+ return Math.max(0.65, Math.min(1, availW / tW));
2592
2721
  };
2593
2722
  try {
2594
2723
  wb = XLSX__namespace.read(new Uint8Array(ctx.buffer), { type: "array", cellDates: true });
@@ -3006,41 +3135,94 @@ var CodePlugin = class {
3006
3135
  async render(ctx) {
3007
3136
  const decoder = new TextDecoder("utf-8");
3008
3137
  const fullText = decoder.decode(ctx.buffer);
3009
- const pageSplitRegex = /(?:\f|\x0C|(?:\r?\n|^)\s*[-=_]{3,}\s*(?:PAGE|Page|page break|Page Break)[\s\d\w-]*[-=_]{3,}\s*(?:\r?\n|$))/i;
3010
- const rawPages = fullText.split(pageSplitRegex).map((p) => p.trim()).filter((p) => p.length > 0);
3138
+ const extRaw = ctx.metadata.extension?.toLowerCase();
3139
+ const mimeRaw = ctx.metadata.mimeType?.toLowerCase();
3140
+ const isTxt = extRaw === ".txt" || mimeRaw === "text/plain" && (!extRaw || !this.extensions.filter((e) => e !== ".txt").includes(extRaw));
3141
+ let rawPages = [];
3142
+ if (isTxt) {
3143
+ const explicitPages = fullText.split(/(?:\f|\x0C)/);
3144
+ for (const ep of explicitPages) {
3145
+ const lines = ep.split(/\r?\n/);
3146
+ let currentChunk = [];
3147
+ for (let i = 0; i < lines.length; i++) {
3148
+ currentChunk.push(lines[i]);
3149
+ if (currentChunk.length >= 46) {
3150
+ rawPages.push(currentChunk.join("\n"));
3151
+ currentChunk = [];
3152
+ }
3153
+ }
3154
+ if (currentChunk.length > 0 || lines.length === 0) {
3155
+ rawPages.push(currentChunk.join("\n"));
3156
+ }
3157
+ }
3158
+ if (rawPages.length === 0) rawPages = [""];
3159
+ } else {
3160
+ const pageSplitRegex = /(?:\f|\x0C|(?:\r?\n|^)\s*[-=_]{3,}\s*(?:PAGE|Page|page break|Page Break)[\s\d\w-]*[-=_]{3,}\s*(?:\r?\n|$))/i;
3161
+ rawPages = fullText.split(pageSplitRegex).map((p) => p.trim()).filter((p) => p.length > 0);
3162
+ }
3011
3163
  const totalPages = Math.max(1, rawPages.length);
3012
3164
  let currentPage = 1;
3013
3165
  const container = document.createElement("div");
3014
3166
  container.style.width = "100%";
3015
3167
  container.style.height = "100%";
3016
3168
  container.style.overflow = "auto";
3017
- container.style.backgroundColor = "#1e1e1e";
3018
- container.style.color = "#d4d4d4";
3019
- container.style.padding = "16px";
3020
- container.style.boxSizing = "border-box";
3021
3169
  let fontSize = 13;
3022
3170
  let rotation = 0;
3171
+ let zoomLevel = 1;
3023
3172
  const pre = document.createElement("pre");
3024
- pre.style.margin = "0";
3025
- pre.style.fontFamily = "Consolas, Menlo, Monaco, monospace";
3026
- pre.style.fontSize = `${fontSize}px`;
3027
- pre.style.lineHeight = "1.5";
3028
- pre.style.whiteSpace = "pre-wrap";
3029
- pre.style.wordBreak = "break-all";
3030
3173
  const code = document.createElement("code");
3174
+ if (isTxt) {
3175
+ container.style.backgroundColor = "#f1f5f9";
3176
+ container.style.padding = "32px";
3177
+ container.style.boxSizing = "border-box";
3178
+ container.style.display = "flex";
3179
+ container.style.flexDirection = "column";
3180
+ container.style.alignItems = "center";
3181
+ pre.style.margin = "0";
3182
+ pre.style.fontFamily = "Consolas, 'Courier New', monospace";
3183
+ pre.style.fontSize = "13px";
3184
+ pre.style.color = "#1e293b";
3185
+ pre.style.lineHeight = "1.5";
3186
+ pre.style.whiteSpace = "pre-wrap";
3187
+ pre.style.wordBreak = "break-word";
3188
+ pre.style.backgroundColor = "#ffffff";
3189
+ pre.style.width = "816px";
3190
+ pre.style.minHeight = "1056px";
3191
+ pre.style.padding = "72px 56px";
3192
+ pre.style.boxSizing = "border-box";
3193
+ pre.style.boxShadow = "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)";
3194
+ pre.style.borderRadius = "4px";
3195
+ pre.style.transformOrigin = "top center";
3196
+ } else {
3197
+ container.style.backgroundColor = "#1e1e1e";
3198
+ container.style.color = "#d4d4d4";
3199
+ container.style.padding = "16px";
3200
+ container.style.boxSizing = "border-box";
3201
+ pre.style.margin = "0";
3202
+ pre.style.fontFamily = "Consolas, Menlo, Monaco, monospace";
3203
+ pre.style.fontSize = `${fontSize}px`;
3204
+ pre.style.lineHeight = "1.5";
3205
+ pre.style.whiteSpace = "pre-wrap";
3206
+ pre.style.wordBreak = "break-all";
3207
+ pre.style.transformOrigin = "top left";
3208
+ }
3031
3209
  const ext = (ctx.metadata.extension || "").replace(".", "");
3032
3210
  const renderCodePage = (text) => {
3033
- try {
3034
- if (ext && hljs__default.default.getLanguage(ext)) {
3035
- code.innerHTML = hljs__default.default.highlight(text, { language: ext }).value;
3036
- } else {
3037
- code.innerHTML = hljs__default.default.highlightAuto(text).value;
3038
- }
3039
- } catch {
3211
+ if (isTxt) {
3040
3212
  code.textContent = text;
3213
+ } else {
3214
+ try {
3215
+ if (ext && hljs__default.default.getLanguage(ext)) {
3216
+ code.innerHTML = hljs__default.default.highlight(text, { language: ext }).value;
3217
+ } else {
3218
+ code.innerHTML = hljs__default.default.highlightAuto(text).value;
3219
+ }
3220
+ } catch {
3221
+ code.textContent = text;
3222
+ }
3041
3223
  }
3042
3224
  };
3043
- renderCodePage(rawPages[0] || fullText);
3225
+ renderCodePage(rawPages[0] || (isTxt ? "" : fullText));
3044
3226
  pre.appendChild(code);
3045
3227
  container.appendChild(pre);
3046
3228
  let indicator = null;
@@ -3049,15 +3231,22 @@ var CodePlugin = class {
3049
3231
  indicator.className = "fp-code-page-indicator";
3050
3232
  indicator.style.position = "sticky";
3051
3233
  indicator.style.bottom = "16px";
3052
- indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
3053
- indicator.style.backdropFilter = "blur(8px)";
3054
- indicator.style.color = "#f8fafc";
3234
+ if (isTxt) {
3235
+ indicator.style.backgroundColor = "rgba(255, 255, 255, 0.9)";
3236
+ indicator.style.color = "#334155";
3237
+ indicator.style.border = "1px solid #e2e8f0";
3238
+ indicator.style.boxShadow = "0 1px 3px rgba(0,0,0,0.1)";
3239
+ } else {
3240
+ indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
3241
+ indicator.style.color = "#f8fafc";
3242
+ indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
3243
+ indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
3244
+ indicator.style.backdropFilter = "blur(8px)";
3245
+ }
3055
3246
  indicator.style.fontSize = "12px";
3056
3247
  indicator.style.fontWeight = "600";
3057
3248
  indicator.style.padding = "5px 14px";
3058
3249
  indicator.style.borderRadius = "20px";
3059
- indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
3060
- indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
3061
3250
  indicator.style.zIndex = "10";
3062
3251
  indicator.style.userSelect = "none";
3063
3252
  indicator.style.pointerEvents = "none";
@@ -3068,7 +3257,7 @@ var CodePlugin = class {
3068
3257
  }
3069
3258
  const showPage = (pageNum) => {
3070
3259
  currentPage = Math.max(1, Math.min(totalPages, pageNum));
3071
- renderCodePage(rawPages[currentPage - 1] || fullText);
3260
+ renderCodePage(rawPages[currentPage - 1] || (isTxt ? "" : fullText));
3072
3261
  if (indicator) {
3073
3262
  indicator.textContent = `Page ${currentPage} of ${totalPages}`;
3074
3263
  }
@@ -3084,39 +3273,57 @@ var CodePlugin = class {
3084
3273
  ctx.container.innerHTML = "";
3085
3274
  };
3086
3275
  ctx.signal.addEventListener("abort", cleanup);
3276
+ const updateTransform = () => {
3277
+ if (isTxt) {
3278
+ pre.style.transform = `scale(${zoomLevel}) rotate(${rotation}deg)`;
3279
+ } else {
3280
+ pre.style.transform = `rotate(${rotation}deg)`;
3281
+ pre.style.fontSize = `${fontSize}px`;
3282
+ }
3283
+ };
3087
3284
  return {
3088
3285
  destroy: cleanup,
3089
3286
  getPageCount: () => totalPages,
3090
3287
  getCurrentPage: () => currentPage,
3091
3288
  goToPage: (page) => showPage(page),
3092
3289
  zoomIn: () => {
3093
- fontSize = Math.min(32, fontSize + 2);
3094
- pre.style.fontSize = `${fontSize}px`;
3290
+ if (isTxt) {
3291
+ zoomLevel = Math.min(3, zoomLevel + 0.1);
3292
+ } else {
3293
+ fontSize = Math.min(32, fontSize + 2);
3294
+ }
3295
+ updateTransform();
3095
3296
  },
3096
3297
  zoomOut: () => {
3097
- fontSize = Math.max(8, fontSize - 2);
3098
- pre.style.fontSize = `${fontSize}px`;
3298
+ if (isTxt) {
3299
+ zoomLevel = Math.max(0.1, zoomLevel - 0.1);
3300
+ } else {
3301
+ fontSize = Math.max(8, fontSize - 2);
3302
+ }
3303
+ updateTransform();
3099
3304
  },
3100
- getZoom: () => fontSize / 13,
3305
+ getZoom: () => isTxt ? zoomLevel : fontSize / 13,
3101
3306
  setZoom: (level) => {
3102
- fontSize = Math.round(13 * level);
3103
- pre.style.fontSize = `${fontSize}px`;
3307
+ if (isTxt) {
3308
+ zoomLevel = level;
3309
+ } else {
3310
+ fontSize = Math.round(13 * level);
3311
+ }
3312
+ updateTransform();
3104
3313
  },
3105
3314
  fitToPage: () => {
3106
3315
  fontSize = 13;
3107
3316
  rotation = 0;
3108
- pre.style.fontSize = "13px";
3109
- pre.style.transform = "none";
3317
+ zoomLevel = isTxt ? Math.max(0.65, Math.min(1.05, (container.clientWidth - 48) / 816)) : 1;
3318
+ updateTransform();
3110
3319
  },
3111
3320
  rotateCW: () => {
3112
3321
  rotation = (rotation + 90) % 360;
3113
- pre.style.transform = `rotate(${rotation}deg)`;
3114
- pre.style.transformOrigin = "top left";
3322
+ updateTransform();
3115
3323
  },
3116
3324
  rotateCCW: () => {
3117
3325
  rotation = (rotation - 90 + 360) % 360;
3118
- pre.style.transform = `rotate(${rotation}deg)`;
3119
- pre.style.transformOrigin = "top left";
3326
+ updateTransform();
3120
3327
  },
3121
3328
  download: () => {
3122
3329
  const mimeType = ctx.metadata.mimeType || "text/plain";
@@ -3415,7 +3622,7 @@ var MarkdownPlugin = class {
3415
3622
  gfm: true,
3416
3623
  breaks: true
3417
3624
  });
3418
- const cleanHtml = DOMPurify2__default.default.sanitize(rawHtml, {
3625
+ const cleanHtml = DOMPurify6__default.default.sanitize(rawHtml, {
3419
3626
  USE_PROFILES: { html: true }
3420
3627
  });
3421
3628
  const wrapper = document.createElement("div");
@@ -4051,6 +4258,14 @@ var RtfPlugin = class {
4051
4258
  group: "actions",
4052
4259
  execute: () => instance.download?.()
4053
4260
  },
4261
+ {
4262
+ id: "copy",
4263
+ icon: "copy",
4264
+ label: "Copy Text",
4265
+ type: "button",
4266
+ group: "actions",
4267
+ execute: () => instance.copy?.()
4268
+ },
4054
4269
  {
4055
4270
  id: "print",
4056
4271
  icon: "print",
@@ -4081,20 +4296,27 @@ var RtfPlugin = class {
4081
4296
  let scale = 1;
4082
4297
  let rotation = 0;
4083
4298
  let pageElements = [];
4084
- const calculateFitScale = () => {
4299
+ let fitMode = "width";
4300
+ const calculateFitScale = (mode = fitMode) => {
4085
4301
  const activeEl = pageElements[currentPage - 1] || wrapper;
4086
4302
  const elW = activeEl.offsetWidth || 850;
4087
- const elH = activeEl.offsetHeight || 1e3;
4088
- const availW = Math.max(200, ctx.container.clientWidth - 48);
4089
- const availH = Math.max(200, ctx.container.clientHeight - 80);
4090
- return Math.min(1.1, Math.min(availW / elW, availH / elH));
4303
+ const singlePageH = Math.min(activeEl.offsetHeight || 1056, Math.round(elW * 1.32));
4304
+ const availW = Math.max(280, ctx.container.clientWidth - 48);
4305
+ const availH = Math.max(280, ctx.container.clientHeight - 80);
4306
+ const sW = availW / elW;
4307
+ const sH = availH / singlePageH;
4308
+ if (mode === "page") {
4309
+ return Math.max(0.55, Math.min(1.15, Math.min(sW, sH)));
4310
+ }
4311
+ return Math.max(0.65, Math.min(1.05, sW));
4091
4312
  };
4092
4313
  const applyTransform = () => {
4093
4314
  wrapper.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
4094
4315
  wrapper.style.transformOrigin = "top center";
4095
4316
  };
4096
4317
  setTimeout(() => {
4097
- scale = calculateFitScale();
4318
+ fitMode = "width";
4319
+ scale = calculateFitScale("width");
4098
4320
  applyTransform();
4099
4321
  }, 60);
4100
4322
  try {
@@ -4112,14 +4334,31 @@ var RtfPlugin = class {
4112
4334
  } catch (err) {
4113
4335
  console.warn("[RtfPlugin] RTF render error, fallback text:", err);
4114
4336
  const text = new TextDecoder("latin1").decode(ctx.buffer);
4115
- const clean = text.replace(/\\par[d]?/g, "\n").replace(/\\[a-zA-Z0-9\-]+/g, "").replace(/[{}]/g, "");
4116
- const pre = document.createElement("pre");
4117
- pre.style.whiteSpace = "pre-wrap";
4118
- pre.style.fontFamily = "serif";
4119
- pre.style.color = "#333";
4120
- pre.textContent = clean;
4121
- wrapper.appendChild(pre);
4122
- pageElements = [pre];
4337
+ const rawPages = text.split(/\\page\b/).map((segment) => {
4338
+ return segment.replace(/\\par[d]?/g, "\n").replace(/\\[a-zA-Z0-9\-]+/g, "").replace(/[{}]/g, "").trim();
4339
+ }).filter((p) => p.length > 0);
4340
+ const pages = rawPages.length > 0 ? rawPages : [text.replace(/\\par[d]?/g, "\n").replace(/\\[a-zA-Z0-9\-]+/g, "").replace(/[{}]/g, "").trim()];
4341
+ for (let i = 0; i < pages.length; i++) {
4342
+ const pageCard = document.createElement("div");
4343
+ pageCard.className = "fp-rtf-page-card";
4344
+ pageCard.style.backgroundColor = "#ffffff";
4345
+ pageCard.style.boxShadow = "0 2px 12px rgba(0,0,0,0.08)";
4346
+ pageCard.style.borderRadius = "4px";
4347
+ pageCard.style.padding = "72px 56px";
4348
+ pageCard.style.width = "816px";
4349
+ pageCard.style.minHeight = "1056px";
4350
+ pageCard.style.maxWidth = "100%";
4351
+ pageCard.style.boxSizing = "border-box";
4352
+ pageCard.style.fontFamily = "serif";
4353
+ pageCard.style.fontSize = "12pt";
4354
+ pageCard.style.lineHeight = "1.6";
4355
+ pageCard.style.color = "#1e293b";
4356
+ pageCard.style.whiteSpace = "pre-wrap";
4357
+ pageCard.style.display = i === 0 ? "block" : "none";
4358
+ pageCard.textContent = pages[i];
4359
+ wrapper.appendChild(pageCard);
4360
+ pageElements.push(pageCard);
4361
+ }
4123
4362
  }
4124
4363
  const totalPages = Math.max(1, pageElements.length);
4125
4364
  let currentPage = 1;
@@ -4186,7 +4425,8 @@ var RtfPlugin = class {
4186
4425
  applyTransform();
4187
4426
  },
4188
4427
  fitToPage: () => {
4189
- scale = calculateFitScale();
4428
+ fitMode = fitMode === "width" ? "page" : "width";
4429
+ scale = calculateFitScale(fitMode);
4190
4430
  rotation = 0;
4191
4431
  applyTransform();
4192
4432
  },
@@ -4207,6 +4447,10 @@ var RtfPlugin = class {
4207
4447
  a.click();
4208
4448
  URL.revokeObjectURL(url);
4209
4449
  },
4450
+ copy: () => {
4451
+ const text = wrapper.textContent || "";
4452
+ navigator.clipboard?.writeText(text);
4453
+ },
4210
4454
  print: () => {
4211
4455
  window.print();
4212
4456
  }
@@ -4281,7 +4525,7 @@ var HtmlPreviewPlugin = class {
4281
4525
  }
4282
4526
  async render(ctx) {
4283
4527
  const rawHtml = new TextDecoder("utf-8").decode(ctx.buffer);
4284
- const sanitized = DOMPurify2__default.default.sanitize(rawHtml, {
4528
+ const sanitized = DOMPurify6__default.default.sanitize(rawHtml, {
4285
4529
  WHOLE_DOCUMENT: true,
4286
4530
  ADD_TAGS: ["style", "link"],
4287
4531
  ADD_ATTR: ["target", "rel"]
@@ -4501,19 +4745,29 @@ var OpenDocumentPlugin = class {
4501
4745
  ctx.container.appendChild(container);
4502
4746
  let scale = 1;
4503
4747
  let rotation = 0;
4504
- const calculateFitScale = () => {
4748
+ let fitMode = "width";
4749
+ const calculateFitScale = (mode = fitMode) => {
4505
4750
  const elW = wrapper.offsetWidth || 850;
4506
- const elH = wrapper.offsetHeight || (isPresentation ? 540 : 1e3);
4507
- const availW = Math.max(200, ctx.container.clientWidth - 48);
4508
- const availH = Math.max(200, ctx.container.clientHeight - 80);
4509
- return Math.min(1, Math.min(availW / elW, availH / elH));
4751
+ const singlePageH = Math.min(wrapper.offsetHeight || 1056, Math.round(elW * 1.32));
4752
+ const availW = Math.max(280, ctx.container.clientWidth - 48);
4753
+ const availH = Math.max(280, ctx.container.clientHeight - 80);
4754
+ const sW = availW / elW;
4755
+ const sH = availH / (isPresentation ? 540 : singlePageH);
4756
+ if (isPresentation) {
4757
+ return Math.min(1, Math.min(sW, sH));
4758
+ }
4759
+ if (mode === "page") {
4760
+ return Math.max(0.55, Math.min(1.15, Math.min(sW, sH)));
4761
+ }
4762
+ return Math.max(0.65, Math.min(1.05, sW));
4510
4763
  };
4511
4764
  const applyTransform = () => {
4512
4765
  wrapper.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
4513
4766
  wrapper.style.transformOrigin = "top center";
4514
4767
  };
4515
4768
  setTimeout(() => {
4516
- scale = calculateFitScale();
4769
+ fitMode = "width";
4770
+ scale = calculateFitScale("width");
4517
4771
  applyTransform();
4518
4772
  }, 60);
4519
4773
  let currentPage = 1;
@@ -4547,14 +4801,97 @@ var OpenDocumentPlugin = class {
4547
4801
  wrapper.textContent = contentDoc.documentElement.textContent || "Formula content";
4548
4802
  }
4549
4803
  } else {
4550
- wrapper.style.maxWidth = "850px";
4551
- wrapper.style.padding = "48px";
4804
+ wrapper.style.maxWidth = "none";
4805
+ wrapper.style.padding = "0";
4552
4806
  wrapper.style.minHeight = "100%";
4807
+ wrapper.style.backgroundColor = "transparent";
4808
+ wrapper.style.boxShadow = "none";
4809
+ wrapper.style.position = "relative";
4810
+ const dims = this.getPageDimensions(stylesDoc, contentDoc);
4553
4811
  const bodyHtml = this.renderOdfBody(contentDoc, styleMap, imageUrls);
4554
- wrapper.innerHTML = DOMPurify2__default.default.sanitize(bodyHtml, {
4555
- ADD_TAGS: ["math", "semantics", "mrow", "mi", "mo", "mn", "msup", "msub"],
4812
+ const tempDiv = document.createElement("div");
4813
+ tempDiv.style.width = `${dims.width - dims.marginLeft - dims.marginRight}px`;
4814
+ tempDiv.style.position = "absolute";
4815
+ tempDiv.style.visibility = "hidden";
4816
+ tempDiv.innerHTML = DOMPurify6__default.default.sanitize(bodyHtml, {
4817
+ ADD_TAGS: ["math", "semantics", "mrow", "mi", "mo", "mn", "msup", "msub", "hr"],
4556
4818
  ADD_ATTR: ["style", "colspan", "rowspan"]
4557
4819
  });
4820
+ document.body.appendChild(tempDiv);
4821
+ const contentHeight = dims.height - dims.marginTop - dims.marginBottom;
4822
+ const pageElements = [[]];
4823
+ let currentHeight = 0;
4824
+ let currentPageIdx = 0;
4825
+ Array.from(tempDiv.children).forEach((child) => {
4826
+ const el = child;
4827
+ const style = el.getAttribute("style") || "";
4828
+ const isBreakBefore = style.includes("page-break-before: always");
4829
+ const isBreakAfter = style.includes("page-break-after: always");
4830
+ const isSoftBreak = el.classList.contains("odf-page-break");
4831
+ if (isBreakBefore) {
4832
+ if (pageElements[currentPageIdx].length > 0) {
4833
+ currentPageIdx++;
4834
+ pageElements.push([]);
4835
+ currentHeight = 0;
4836
+ }
4837
+ }
4838
+ const h = el.offsetHeight || 0;
4839
+ if (currentHeight + h > contentHeight && pageElements[currentPageIdx].length > 0 && !isSoftBreak) {
4840
+ currentPageIdx++;
4841
+ pageElements.push([]);
4842
+ currentHeight = 0;
4843
+ }
4844
+ if (!isSoftBreak) {
4845
+ pageElements[currentPageIdx].push(el.cloneNode(true));
4846
+ currentHeight += h;
4847
+ }
4848
+ if (isBreakAfter || isSoftBreak) {
4849
+ currentPageIdx++;
4850
+ pageElements.push([]);
4851
+ currentHeight = 0;
4852
+ }
4853
+ });
4854
+ document.body.removeChild(tempDiv);
4855
+ if (pageElements.length > 1 && pageElements[pageElements.length - 1].length === 0) {
4856
+ pageElements.pop();
4857
+ }
4858
+ totalPages = Math.max(1, pageElements.length);
4859
+ pageElements.forEach((elements, idx) => {
4860
+ const page = document.createElement("div");
4861
+ page.className = `fp-odt-page fp-odt-page-${idx + 1}`;
4862
+ page.style.width = `${dims.width}px`;
4863
+ page.style.minHeight = `${dims.height}px`;
4864
+ page.style.padding = `${dims.marginTop}px ${dims.marginRight}px ${dims.marginBottom}px ${dims.marginLeft}px`;
4865
+ page.style.margin = "0 auto";
4866
+ page.style.backgroundColor = "#ffffff";
4867
+ page.style.boxShadow = "0 2px 10px rgba(0,0,0,0.08)";
4868
+ page.style.borderRadius = "4px";
4869
+ page.style.boxSizing = "border-box";
4870
+ page.style.display = idx === 0 ? "block" : "none";
4871
+ page.style.position = "absolute";
4872
+ page.style.top = "0";
4873
+ page.style.left = "50%";
4874
+ page.style.transform = "translateX(-50%)";
4875
+ elements.forEach((el) => page.appendChild(el));
4876
+ wrapper.appendChild(page);
4877
+ slides.push(page);
4878
+ });
4879
+ const pageIndicator = document.createElement("div");
4880
+ pageIndicator.className = "fp-odt-page-indicator";
4881
+ pageIndicator.style.position = "sticky";
4882
+ pageIndicator.style.bottom = "16px";
4883
+ pageIndicator.style.left = "50%";
4884
+ pageIndicator.style.transform = "translateX(-50%)";
4885
+ pageIndicator.style.backgroundColor = "rgba(0, 0, 0, 0.6)";
4886
+ pageIndicator.style.color = "#fff";
4887
+ pageIndicator.style.padding = "6px 12px";
4888
+ pageIndicator.style.borderRadius = "16px";
4889
+ pageIndicator.style.fontSize = "12px";
4890
+ pageIndicator.style.zIndex = "100";
4891
+ pageIndicator.style.display = "inline-block";
4892
+ pageIndicator.style.width = "fit-content";
4893
+ pageIndicator.textContent = `Page 1 of ${totalPages}`;
4894
+ container.appendChild(pageIndicator);
4558
4895
  }
4559
4896
  const cleanup = () => {
4560
4897
  imageUrls.forEach((url) => URL.revokeObjectURL(url));
@@ -4563,11 +4900,15 @@ var OpenDocumentPlugin = class {
4563
4900
  };
4564
4901
  ctx.signal.addEventListener("abort", cleanup);
4565
4902
  const goToPage = (page) => {
4566
- if (!isPresentation || page < 1 || page > totalPages) return;
4903
+ if (page < 1 || page > totalPages) return;
4567
4904
  currentPage = page;
4568
4905
  slides.forEach((s, idx) => {
4569
4906
  s.style.display = idx === page - 1 ? "block" : "none";
4570
4907
  });
4908
+ const indicator = container.querySelector(".fp-odt-page-indicator");
4909
+ if (indicator) {
4910
+ indicator.textContent = `Page ${currentPage} of ${totalPages}`;
4911
+ }
4571
4912
  ctx.emit("page-change", { page: currentPage, total: totalPages });
4572
4913
  };
4573
4914
  return {
@@ -4587,7 +4928,8 @@ var OpenDocumentPlugin = class {
4587
4928
  applyTransform();
4588
4929
  },
4589
4930
  fitToPage: () => {
4590
- scale = calculateFitScale();
4931
+ fitMode = fitMode === "width" ? "page" : "width";
4932
+ scale = calculateFitScale(fitMode);
4591
4933
  rotation = 0;
4592
4934
  applyTransform();
4593
4935
  },
@@ -4636,6 +4978,43 @@ var OpenDocumentPlugin = class {
4636
4978
  }
4637
4979
  };
4638
4980
  }
4981
+ getPageDimensions(stylesDoc, contentDoc) {
4982
+ let width = 850;
4983
+ let height = 1123;
4984
+ let marginTop = 48;
4985
+ let marginBottom = 48;
4986
+ let marginLeft = 48;
4987
+ let marginRight = 48;
4988
+ const parseUnit = (val) => {
4989
+ if (!val) return null;
4990
+ if (val.endsWith("cm")) return parseFloat(val) * 37.8;
4991
+ if (val.endsWith("mm")) return parseFloat(val) * 3.78;
4992
+ if (val.endsWith("in")) return parseFloat(val) * 96;
4993
+ if (val.endsWith("pt")) return parseFloat(val) * 1.33;
4994
+ if (val.endsWith("px")) return parseFloat(val);
4995
+ return parseFloat(val);
4996
+ };
4997
+ const docs = [stylesDoc, contentDoc].filter(Boolean);
4998
+ for (const doc of docs) {
4999
+ const pageLayout = doc.querySelector("page-layout-properties, [page-width]");
5000
+ if (pageLayout) {
5001
+ const w = parseUnit(pageLayout.getAttribute("fo:page-width") || pageLayout.getAttribute("page-width"));
5002
+ const h = parseUnit(pageLayout.getAttribute("fo:page-height") || pageLayout.getAttribute("page-height"));
5003
+ const mt = parseUnit(pageLayout.getAttribute("fo:margin-top") || pageLayout.getAttribute("margin-top"));
5004
+ const mb = parseUnit(pageLayout.getAttribute("fo:margin-bottom") || pageLayout.getAttribute("margin-bottom"));
5005
+ const ml = parseUnit(pageLayout.getAttribute("fo:margin-left") || pageLayout.getAttribute("margin-left"));
5006
+ const mr = parseUnit(pageLayout.getAttribute("fo:margin-right") || pageLayout.getAttribute("margin-right"));
5007
+ if (w !== null) width = w;
5008
+ if (h !== null) height = h;
5009
+ if (mt !== null) marginTop = mt;
5010
+ if (mb !== null) marginBottom = mb;
5011
+ if (ml !== null) marginLeft = ml;
5012
+ if (mr !== null) marginRight = mr;
5013
+ break;
5014
+ }
5015
+ }
5016
+ return { width, height, marginTop, marginBottom, marginLeft, marginRight };
5017
+ }
4639
5018
  extractStyles(stylesDoc, contentDoc) {
4640
5019
  const map = /* @__PURE__ */ new Map();
4641
5020
  const styleNodes = [];
@@ -4658,14 +5037,21 @@ var OpenDocumentPlugin = class {
4658
5037
  if (color) css += `color: ${color}; `;
4659
5038
  if (size) css += `font-size: ${size}; `;
4660
5039
  }
4661
- const paraProp = node.querySelector("paragraph-properties, [text-align]");
5040
+ let paraProp = node.querySelector("paragraph-properties, [text-align]");
5041
+ if (!paraProp) {
5042
+ paraProp = Array.from(node.children).find((c) => c.tagName.includes("paragraph-properties")) || null;
5043
+ }
4662
5044
  if (paraProp) {
4663
5045
  const align = paraProp.getAttribute("fo:text-align") || paraProp.getAttribute("text-align");
4664
5046
  const mt = paraProp.getAttribute("fo:margin-top") || paraProp.getAttribute("margin-top");
4665
5047
  const mb = paraProp.getAttribute("fo:margin-bottom") || paraProp.getAttribute("margin-bottom");
5048
+ const breakBefore = paraProp.getAttribute("fo:break-before") || paraProp.getAttribute("break-before");
5049
+ const breakAfter = paraProp.getAttribute("fo:break-after") || paraProp.getAttribute("break-after");
4666
5050
  if (align) css += `text-align: ${align}; `;
4667
5051
  if (mt) css += `margin-top: ${mt}; `;
4668
5052
  if (mb) css += `margin-bottom: ${mb}; `;
5053
+ if (breakBefore === "page") css += "page-break-before: always; ";
5054
+ if (breakAfter === "page") css += "page-break-after: always; ";
4669
5055
  }
4670
5056
  if (css) map.set(name, css);
4671
5057
  }
@@ -4750,6 +5136,8 @@ var OpenDocumentPlugin = class {
4750
5136
  result += "&emsp;";
4751
5137
  } else if (tag === "line-break") {
4752
5138
  result += "<br/>";
5139
+ } else if (tag === "soft-page-break") {
5140
+ result += '<hr class="odf-page-break" style="page-break-after: always; border: none; margin: 0; padding: 0; height: 0;" />';
4753
5141
  } else {
4754
5142
  result += el.textContent || "";
4755
5143
  }
@@ -4910,20 +5298,9 @@ var DocPlugin = class {
4910
5298
  container.style.overflow = "auto";
4911
5299
  container.style.padding = "32px 16px";
4912
5300
  container.style.backgroundColor = "#f1f5f9";
4913
- const wrapper = document.createElement("div");
4914
- wrapper.className = "fp-doc-wrapper";
4915
- wrapper.style.maxWidth = "850px";
4916
- wrapper.style.margin = "0 auto";
4917
- wrapper.style.backgroundColor = "#ffffff";
4918
- wrapper.style.boxShadow = "0 2px 12px rgba(0,0,0,0.08)";
4919
- wrapper.style.borderRadius = "4px";
4920
- wrapper.style.padding = "56px 48px";
4921
- wrapper.style.minHeight = "100%";
4922
- wrapper.style.transformOrigin = "top center";
4923
- wrapper.style.transition = "transform 0.2s ease";
4924
- wrapper.style.fontFamily = 'Calibri, "Segoe UI", Arial, sans-serif';
4925
- wrapper.style.color = "#1e293b";
4926
- container.appendChild(wrapper);
5301
+ container.style.display = "flex";
5302
+ container.style.justifyContent = "center";
5303
+ container.style.alignItems = "flex-start";
4927
5304
  ctx.container.appendChild(container);
4928
5305
  let scale = 1;
4929
5306
  let extractedRawText = "";
@@ -4950,21 +5327,27 @@ var DocPlugin = class {
4950
5327
  const rawPages = this.splitIntoPages(extractedRawText);
4951
5328
  const totalPages = Math.max(1, rawPages.length);
4952
5329
  let currentPage = 1;
4953
- wrapper.innerHTML = "";
4954
5330
  const pageCards = [];
4955
5331
  for (let i = 0; i < totalPages; i++) {
4956
5332
  const pageCard = document.createElement("div");
4957
5333
  pageCard.className = "fp-doc-page-card";
5334
+ pageCard.style.width = "816px";
5335
+ pageCard.style.height = "1056px";
4958
5336
  pageCard.style.backgroundColor = "#ffffff";
4959
5337
  pageCard.style.boxShadow = "0 2px 12px rgba(0,0,0,0.08)";
4960
5338
  pageCard.style.borderRadius = "4px";
4961
- pageCard.style.padding = "56px 48px";
4962
- pageCard.style.minHeight = "100%";
5339
+ pageCard.style.padding = "96px 72px";
5340
+ pageCard.style.boxSizing = "border-box";
5341
+ pageCard.style.overflow = "hidden";
4963
5342
  pageCard.style.display = i === 0 ? "block" : "none";
5343
+ pageCard.style.transformOrigin = "top center";
5344
+ pageCard.style.transition = "transform 0.2s ease";
5345
+ pageCard.style.fontFamily = 'Calibri, "Segoe UI", Arial, sans-serif';
5346
+ pageCard.style.color = "#1e293b";
4964
5347
  if (isFallback && i === 0) {
4965
5348
  pageCard.innerHTML = `
4966
5349
  <div style="border-bottom: 1px solid #e2e8f0; padding-bottom: 12px; margin-bottom: 24px;">
4967
- <h2 style="margin: 0 0 6px; font-size: 20px; color: #334155;">${DOMPurify2__default.default.sanitize(ctx.metadata.name || "Word Document (.doc)")}</h2>
5350
+ <h2 style="margin: 0 0 6px; font-size: 20px; color: #334155;">${DOMPurify6__default.default.sanitize(ctx.metadata.name || "Word Document (.doc)")}</h2>
4968
5351
  <span style="font-size: 12px; color: #64748b; background: #f1f5f9; padding: 2px 8px; border-radius: 4px;">Legacy Word 97-2003 Binary Preview</span>
4969
5352
  </div>
4970
5353
  ${this.formatDocToHtml(rawPages[i], ctx.metadata.name || "Document")}
@@ -4972,15 +5355,17 @@ var DocPlugin = class {
4972
5355
  } else {
4973
5356
  pageCard.innerHTML = this.formatDocToHtml(rawPages[i], ctx.metadata.name || "Document");
4974
5357
  }
4975
- wrapper.appendChild(pageCard);
5358
+ container.appendChild(pageCard);
4976
5359
  pageCards.push(pageCard);
4977
5360
  }
4978
5361
  let indicator = null;
4979
5362
  if (totalPages > 1) {
4980
5363
  indicator = document.createElement("div");
4981
5364
  indicator.className = "fp-doc-page-indicator";
4982
- indicator.style.position = "sticky";
5365
+ indicator.style.position = "fixed";
4983
5366
  indicator.style.bottom = "16px";
5367
+ indicator.style.left = "50%";
5368
+ indicator.style.transform = "translateX(-50%)";
4984
5369
  indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
4985
5370
  indicator.style.backdropFilter = "blur(8px)";
4986
5371
  indicator.style.color = "#f8fafc";
@@ -4994,17 +5379,25 @@ var DocPlugin = class {
4994
5379
  indicator.style.userSelect = "none";
4995
5380
  indicator.style.pointerEvents = "none";
4996
5381
  indicator.style.textAlign = "center";
4997
- indicator.style.width = "fit-content";
4998
- indicator.style.margin = "16px auto 0";
4999
5382
  container.appendChild(indicator);
5000
5383
  }
5384
+ let rotation = 0;
5385
+ const applyTransform = () => {
5386
+ const activeCard = pageCards[currentPage - 1];
5387
+ if (activeCard) {
5388
+ activeCard.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
5389
+ }
5390
+ };
5001
5391
  const showPage = (pageNum) => {
5002
5392
  currentPage = Math.max(1, Math.min(totalPages, pageNum));
5003
- if (totalPages > 1) {
5004
- pageCards.forEach((card, idx) => {
5005
- card.style.display = idx + 1 === currentPage ? "block" : "none";
5006
- });
5007
- }
5393
+ pageCards.forEach((card, idx) => {
5394
+ if (idx + 1 === currentPage) {
5395
+ card.style.display = "block";
5396
+ card.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
5397
+ } else {
5398
+ card.style.display = "none";
5399
+ }
5400
+ });
5008
5401
  if (indicator) {
5009
5402
  indicator.textContent = `Page ${currentPage} of ${totalPages}`;
5010
5403
  }
@@ -5013,21 +5406,22 @@ var DocPlugin = class {
5013
5406
  if (totalPages > 1) {
5014
5407
  showPage(1);
5015
5408
  }
5016
- let rotation = 0;
5017
- const calculateFitScale = () => {
5018
- const activeCard = pageCards[currentPage - 1] || wrapper;
5019
- const elW = activeCard.offsetWidth || 850;
5020
- const elH = activeCard.offsetHeight || 1e3;
5021
- const availW = Math.max(200, ctx.container.clientWidth - 48);
5022
- const availH = Math.max(200, ctx.container.clientHeight - 80);
5023
- return Math.min(1.1, Math.min(availW / elW, availH / elH));
5024
- };
5025
- const applyTransform = () => {
5026
- wrapper.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
5027
- wrapper.style.transformOrigin = "top center";
5409
+ let fitMode = "width";
5410
+ const calculateFitScale = (mode = fitMode) => {
5411
+ const elW = 816;
5412
+ const elH = 1056;
5413
+ const availW = Math.max(280, ctx.container.clientWidth - 48);
5414
+ const availH = Math.max(280, ctx.container.clientHeight - 80);
5415
+ const sW = availW / elW;
5416
+ const sH = availH / elH;
5417
+ if (mode === "page") {
5418
+ return Math.max(0.55, Math.min(1.15, Math.min(sW, sH)));
5419
+ }
5420
+ return Math.max(0.65, Math.min(1.05, sW));
5028
5421
  };
5029
5422
  setTimeout(() => {
5030
- scale = calculateFitScale();
5423
+ fitMode = "width";
5424
+ scale = calculateFitScale("width");
5031
5425
  applyTransform();
5032
5426
  }, 60);
5033
5427
  const cleanup = () => {
@@ -5055,7 +5449,8 @@ var DocPlugin = class {
5055
5449
  applyTransform();
5056
5450
  },
5057
5451
  fitToPage: () => {
5058
- scale = calculateFitScale();
5452
+ fitMode = fitMode === "width" ? "page" : "width";
5453
+ scale = calculateFitScale(fitMode);
5059
5454
  rotation = 0;
5060
5455
  applyTransform();
5061
5456
  },
@@ -5183,8 +5578,28 @@ var DocPlugin = class {
5183
5578
  }
5184
5579
  splitIntoPages(text) {
5185
5580
  if (!text) return [""];
5186
- const parts = text.split(/[\x0C\f]|\r?\n\s*[-=_]{3,}\s*(?:PAGE|Page|page break)[\s\d\w-]*[-=_]{3,}\s*\r?\n/i).map((p) => p.trim()).filter((p) => p.length > 0);
5187
- return parts.length > 0 ? parts : [text];
5581
+ const explicitParts = text.split(/[\x0C\f]|\r?\n\s*[-=_]{3,}\s*(?:PAGE|Page|page break)[\s\d\w-]*[-=_]{3,}\s*\r?\n/i).map((p) => p.trim()).filter((p) => p.length > 0);
5582
+ if (explicitParts.length === 0) explicitParts.push(text);
5583
+ const maxLinesPerPage = 48;
5584
+ const finalPages = [];
5585
+ for (const part of explicitParts) {
5586
+ const lines = part.split(/\r?\n/);
5587
+ let currentLines = [];
5588
+ let count = 0;
5589
+ for (const line of lines) {
5590
+ currentLines.push(line);
5591
+ count++;
5592
+ if (count >= maxLinesPerPage) {
5593
+ finalPages.push(currentLines.join("\n"));
5594
+ currentLines = [];
5595
+ count = 0;
5596
+ }
5597
+ }
5598
+ if (currentLines.length > 0) {
5599
+ finalPages.push(currentLines.join("\n"));
5600
+ }
5601
+ }
5602
+ return finalPages.length > 0 ? finalPages : [text];
5188
5603
  }
5189
5604
  /**
5190
5605
  * Format cleaned extracted text into attractive HTML paragraphs, headings, and lists
@@ -5193,17 +5608,65 @@ var DocPlugin = class {
5193
5608
  const lines = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\x0B/g, "\n").split("\n");
5194
5609
  let html = "";
5195
5610
  let inList = false;
5196
- for (const rawLine of lines) {
5197
- const line = rawLine.trim();
5611
+ let tableLines = [];
5612
+ const flushTable = () => {
5613
+ if (tableLines.length > 0) {
5614
+ html += '<table style="width: 100%; border-collapse: collapse; margin: 16px 0; font-size: 12px;">';
5615
+ for (const tLine of tableLines) {
5616
+ html += "<tr>";
5617
+ const cols = tLine.split(" ");
5618
+ for (const col of cols) {
5619
+ html += `<td style="border: 1px solid #cbd5e1; padding: 6px 8px;">${DOMPurify6__default.default.sanitize(col.trim())}</td>`;
5620
+ }
5621
+ html += "</tr>";
5622
+ }
5623
+ html += "</table>";
5624
+ tableLines = [];
5625
+ }
5626
+ };
5627
+ let i = 0;
5628
+ while (i < lines.length) {
5629
+ let line = lines[i];
5630
+ let tabCount = (line.match(/\t/g) || []).length;
5631
+ if (tabCount > 0) {
5632
+ let consecutiveTableLines = 1;
5633
+ let j = i + 1;
5634
+ while (j < lines.length) {
5635
+ const nextTabCount = (lines[j].match(/\t/g) || []).length;
5636
+ if (nextTabCount === tabCount) {
5637
+ consecutiveTableLines++;
5638
+ j++;
5639
+ } else {
5640
+ break;
5641
+ }
5642
+ }
5643
+ if (consecutiveTableLines >= 3) {
5644
+ if (inList) {
5645
+ html += "</ul>";
5646
+ inList = false;
5647
+ }
5648
+ tableLines = lines.slice(i, j);
5649
+ flushTable();
5650
+ i = j;
5651
+ continue;
5652
+ }
5653
+ }
5654
+ line = line.trim();
5198
5655
  if (!line) {
5199
5656
  if (inList) {
5200
5657
  html += "</ul>";
5201
5658
  inList = false;
5202
5659
  }
5660
+ html += '<div style="height: 1.15em;"></div>';
5661
+ i++;
5662
+ continue;
5663
+ }
5664
+ if (/^[\x00-\x1F\x7F-\x9F]+$/.test(line)) {
5665
+ i++;
5203
5666
  continue;
5204
5667
  }
5205
- if (/^[\x00-\x1F\x7F-\x9F]+$/.test(line)) continue;
5206
- if (line.includes("Normal.dot") || line.includes("Microsoft Word") || line.includes("Times New Roman") && line.length < 30) {
5668
+ if ((line.includes("Normal.dot") || line.includes("Microsoft Word") || line.includes("Times New Roman")) && line.length < 30) {
5669
+ i++;
5207
5670
  continue;
5208
5671
  }
5209
5672
  if (line.length < 60 && !line.endsWith(".") && (/^[A-Z0-9\s:_-]+$/.test(line) || line.startsWith("#"))) {
@@ -5211,24 +5674,26 @@ var DocPlugin = class {
5211
5674
  html += "</ul>";
5212
5675
  inList = false;
5213
5676
  }
5214
- 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>`;
5677
+ html += `<h2 style="font-size: 16px; font-weight: 700; color: #1e3a8a; margin: 16px 0 8px; border-bottom: 1px solid #e2e8f0; padding-bottom: 4px;">${DOMPurify6__default.default.sanitize(line)}</h2>`;
5215
5678
  } else if (line.startsWith("\u2022") || line.startsWith("-") || line.startsWith("*")) {
5216
5679
  if (!inList) {
5217
5680
  html += '<ul style="margin: 8px 0; padding-left: 24px;">';
5218
5681
  inList = true;
5219
5682
  }
5220
5683
  const bulletText = line.replace(/^[•\-\*]\s*/, "");
5221
- html += `<li style="margin: 4px 0; line-height: 1.6;">${DOMPurify2__default.default.sanitize(bulletText)}</li>`;
5684
+ html += `<li style="margin: 4px 0; line-height: 1.15; font-size: 12pt;">${DOMPurify6__default.default.sanitize(bulletText)}</li>`;
5222
5685
  } else {
5223
5686
  if (inList) {
5224
5687
  html += "</ul>";
5225
5688
  inList = false;
5226
5689
  }
5227
- html += `<p style="line-height: 1.7; margin: 10px 0; font-size: 14px; text-align: justify;">${DOMPurify2__default.default.sanitize(line)}</p>`;
5690
+ html += `<p style="line-height: 1.15; margin: 0; font-size: 12pt; text-align: justify;">${DOMPurify6__default.default.sanitize(line)}</p>`;
5228
5691
  }
5692
+ i++;
5229
5693
  }
5230
5694
  if (inList) html += "</ul>";
5231
- return html || `<p style="color: #64748b; font-style: italic;">(No readable text found in ${DOMPurify2__default.default.sanitize(filename)})</p>`;
5695
+ flushTable();
5696
+ return html || `<p style="color: #64748b; font-style: italic;">(No readable text found in ${DOMPurify6__default.default.sanitize(filename)})</p>`;
5232
5697
  }
5233
5698
  };
5234
5699
  function docPlugin() {
@@ -5406,7 +5871,7 @@ var PptPlugin = class {
5406
5871
  if (s.pictureUrl) {
5407
5872
  contentHtml = `
5408
5873
  <div style="flex: 1; display: flex; justify-content: center; align-items: center; padding: 12px; overflow: hidden;">
5409
- <img src="${s.pictureUrl}" alt="${DOMPurify2__default.default.sanitize(s.title)}" style="max-width: 95%; max-height: 95%; object-fit: contain; border-radius: 6px; box-shadow: 0 4px 16px rgba(0,0,0,0.1); background: #ffffff;" />
5874
+ <img src="${s.pictureUrl}" alt="${DOMPurify6__default.default.sanitize(s.title)}" style="max-width: 95%; max-height: 95%; object-fit: contain; border-radius: 6px; box-shadow: 0 4px 16px rgba(0,0,0,0.1); background: #ffffff;" />
5410
5875
  </div>
5411
5876
  `;
5412
5877
  } else if (s.tableColumns.length > 0) {
@@ -5416,7 +5881,7 @@ var PptPlugin = class {
5416
5881
  <table style="width: 100%; border-collapse: collapse; border: 1px solid #cbd5e1; border-radius: 6px; overflow: hidden; background: #ffffff;">
5417
5882
  <thead>
5418
5883
  <tr style="background: #e2e8f0; color: #1e293b; font-weight: 600; font-size: 14px;">
5419
- ${cols.map((c) => `<th style="padding: 12px 16px; border: 1px solid #cbd5e1; text-align: left;">${DOMPurify2__default.default.sanitize(c)}</th>`).join("")}
5884
+ ${cols.map((c) => `<th style="padding: 12px 16px; border: 1px solid #cbd5e1; text-align: left;">${DOMPurify6__default.default.sanitize(c)}</th>`).join("")}
5420
5885
  </tr>
5421
5886
  </thead>
5422
5887
  <tbody>
@@ -5432,7 +5897,7 @@ var PptPlugin = class {
5432
5897
  } else {
5433
5898
  const pTags = s.paragraphs.map((p) => {
5434
5899
  const lines = p.split(/[\r\n]+/).map((l) => l.trim()).filter(Boolean);
5435
- return lines.map((l) => `<p style="margin: 0 0 14px; font-size: 14px; line-height: 1.65; color: #334155; text-align: justify;">${DOMPurify2__default.default.sanitize(l)}</p>`).join("");
5900
+ return lines.map((l) => `<p style="margin: 0 0 14px; font-size: 14px; line-height: 1.65; color: #334155; text-align: justify;">${DOMPurify6__default.default.sanitize(l)}</p>`).join("");
5436
5901
  }).join("");
5437
5902
  contentHtml = `
5438
5903
  <div style="flex: 1; overflow: auto; padding: 4px 8px; display: flex; flex-direction: column; justify-content: flex-start;">
@@ -5445,11 +5910,11 @@ var PptPlugin = class {
5445
5910
  <!-- Header Banner matching PowerPoint design -->
5446
5911
  <div style="background: linear-gradient(90deg, #a3e635 0%, #84cc16 100%); padding: 12px 24px; border-radius: 4px; display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; box-shadow: 0 2px 8px rgba(0,0,0,0.08); flex-shrink: 0;">
5447
5912
  <h1 style="margin: 0; font-size: 26px; font-weight: 700; color: #1e293b; letter-spacing: -0.3px;">
5448
- ${DOMPurify2__default.default.sanitize(s.title)}
5913
+ ${DOMPurify6__default.default.sanitize(s.title)}
5449
5914
  </h1>
5450
5915
  ${s.subtitle ? `
5451
5916
  <span style="background: #38bdf8; color: #ffffff; padding: 4px 14px; border-radius: 4px; font-weight: 700; font-size: 13px; letter-spacing: 0.5px; box-shadow: 0 1px 4px rgba(0,0,0,0.15);">
5452
- ${DOMPurify2__default.default.sanitize(s.subtitle)}
5917
+ ${DOMPurify6__default.default.sanitize(s.subtitle)}
5453
5918
  </span>
5454
5919
  ` : `
5455
5920
  <span style="font-size: 12px; color: #365314; font-weight: 600;">