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

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,4 +1,4 @@
1
- import DOMPurify2 from 'dompurify';
1
+ import DOMPurify6 from 'dompurify';
2
2
  import * as pdfjsLib from 'pdfjs-dist';
3
3
  import * as docx from 'docx-preview';
4
4
  import { unzipSync, strFromU8, unzip } from 'fflate';
@@ -356,7 +356,7 @@ async function sourceToArrayBuffer(source, signal) {
356
356
  if (magicMime === "application/zip") {
357
357
  const ooxmlMime = detectOoxmlType(buffer);
358
358
  if (ooxmlMime && ooxmlMime !== "application/zip") {
359
- metadata.mimeType = ooxmlMime;
359
+ metadata.mimeType = metadata.mimeType ?? ooxmlMime;
360
360
  if (ooxmlMime.includes("wordprocessing")) metadata.extension = metadata.extension ?? ".docx";
361
361
  else if (ooxmlMime.includes("spreadsheet")) metadata.extension = metadata.extension ?? ".xlsx";
362
362
  else if (ooxmlMime.includes("presentation")) metadata.extension = metadata.extension ?? ".pptx";
@@ -385,12 +385,12 @@ async function sourceToArrayBuffer(source, signal) {
385
385
  return { buffer, metadata };
386
386
  }
387
387
  function sanitizeHTML(html) {
388
- return DOMPurify2.sanitize(html, {
388
+ return DOMPurify6.sanitize(html, {
389
389
  USE_PROFILES: { html: true }
390
390
  });
391
391
  }
392
392
  function sanitizeSVG(svg) {
393
- return DOMPurify2.sanitize(svg, {
393
+ return DOMPurify6.sanitize(svg, {
394
394
  USE_PROFILES: { svg: true, svgFilters: true }
395
395
  });
396
396
  }
@@ -1491,7 +1491,19 @@ var PdfPlugin = class {
1491
1491
  }
1492
1492
  };
1493
1493
  await renderPage(1);
1494
+ let resizeTimer = null;
1495
+ const resizeObserver = new ResizeObserver(() => {
1496
+ if (resizeTimer) clearTimeout(resizeTimer);
1497
+ resizeTimer = setTimeout(() => {
1498
+ if (Math.abs(zoomScale - 1) < 0.05) {
1499
+ renderPage(currentPage);
1500
+ }
1501
+ }, 150);
1502
+ });
1503
+ resizeObserver.observe(container);
1494
1504
  const cleanup = () => {
1505
+ resizeObserver.disconnect();
1506
+ if (resizeTimer) clearTimeout(resizeTimer);
1495
1507
  if (currentRenderTask) {
1496
1508
  try {
1497
1509
  currentRenderTask.cancel();
@@ -2239,90 +2251,149 @@ var DocxPlugin = class {
2239
2251
  console.warn("[DocxPlugin] Error parsing relationships:", relsErr);
2240
2252
  }
2241
2253
  }
2242
- const card = document.createElement("div");
2243
- card.className = "fp-docx-page-card";
2244
- card.style.backgroundColor = "#ffffff";
2245
- card.style.borderRadius = "6px";
2246
- card.style.boxShadow = "0 4px 24px rgba(0,0,0,0.08)";
2247
- card.style.padding = "48px 56px";
2248
- card.style.fontFamily = 'Calibri, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
2249
- card.style.color = "#1e293b";
2250
- card.style.lineHeight = "1.6";
2254
+ const sectPrs = Array.from(doc.getElementsByTagNameNS("*", "sectPr"));
2255
+ const lastSectPr = sectPrs[sectPrs.length - 1];
2256
+ let defaultW = 12240;
2257
+ let defaultH = 15840;
2258
+ let margins = { top: 1440, right: 1440, bottom: 1440, left: 1440 };
2259
+ if (lastSectPr) {
2260
+ const pgSz = lastSectPr.getElementsByTagNameNS("*", "pgSz")[0];
2261
+ if (pgSz) {
2262
+ defaultW = parseInt(pgSz.getAttribute("w:w") || pgSz.getAttribute("w") || "12240", 10);
2263
+ defaultH = parseInt(pgSz.getAttribute("w:h") || pgSz.getAttribute("h") || "15840", 10);
2264
+ }
2265
+ const pgMar = lastSectPr.getElementsByTagNameNS("*", "pgMar")[0];
2266
+ if (pgMar) {
2267
+ margins.top = parseInt(pgMar.getAttribute("w:top") || pgMar.getAttribute("top") || "1440", 10);
2268
+ margins.bottom = parseInt(pgMar.getAttribute("w:bottom") || pgMar.getAttribute("bottom") || "1440", 10);
2269
+ margins.left = parseInt(pgMar.getAttribute("w:left") || pgMar.getAttribute("left") || "1440", 10);
2270
+ margins.right = parseInt(pgMar.getAttribute("w:right") || pgMar.getAttribute("right") || "1440", 10);
2271
+ }
2272
+ }
2273
+ const createPageCard = () => {
2274
+ const card = document.createElement("div");
2275
+ card.className = "fp-docx-page-card";
2276
+ card.style.backgroundColor = "#ffffff";
2277
+ card.style.borderRadius = "4px";
2278
+ card.style.boxShadow = "0 4px 24px rgba(0,0,0,0.08)";
2279
+ card.style.fontFamily = 'Calibri, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
2280
+ card.style.color = "#1e293b";
2281
+ card.style.lineHeight = "1.6";
2282
+ card.style.boxSizing = "border-box";
2283
+ card.style.overflow = "hidden";
2284
+ card.style.position = "relative";
2285
+ card.style.marginBottom = "24px";
2286
+ card.style.width = `${defaultW / 15}px`;
2287
+ card.style.height = `${defaultH / 15}px`;
2288
+ card.style.padding = `${margins.top / 15}px ${margins.right / 15}px ${margins.bottom / 15}px ${margins.left / 15}px`;
2289
+ return card;
2290
+ };
2291
+ let currentCard = createPageCard();
2292
+ let currentHtml = "";
2293
+ const pages = [{ card: currentCard, html: "" }];
2294
+ const flushHtml = () => {
2295
+ pages[pages.length - 1].html += currentHtml;
2296
+ currentHtml = "";
2297
+ };
2298
+ const newPage = () => {
2299
+ flushHtml();
2300
+ currentCard = createPageCard();
2301
+ pages.push({ card: currentCard, html: "" });
2302
+ };
2251
2303
  const body = doc.getElementsByTagNameNS("*", "body")[0] || doc.documentElement;
2252
- let html = "";
2253
2304
  for (const child of Array.from(body.children)) {
2254
2305
  const tag = child.localName || child.nodeName.split(":").pop();
2255
2306
  if (tag === "p") {
2256
2307
  const pStyle = child.getElementsByTagNameNS("*", "pStyle")[0];
2257
2308
  const styleVal = pStyle?.getAttribute("w:val") || pStyle?.getAttribute("val") || "";
2258
2309
  const numPr = child.getElementsByTagNameNS("*", "numPr")[0];
2259
- const textContent = this.extractParagraphHtml(child, imageMap);
2260
- if (!textContent.trim()) {
2261
- html += '<div style="height: 10px;"></div>';
2262
- continue;
2263
- }
2264
- const lowerStyle = styleVal.toLowerCase();
2265
- if (lowerStyle.includes("title")) {
2266
- 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>`;
2267
- } else if (lowerStyle.includes("heading1") || styleVal === "1") {
2268
- html += `<h2 style="font-size: 22px; font-weight: 700; color: #1e40af; margin: 20px 0 10px;">${textContent}</h2>`;
2269
- } else if (lowerStyle.includes("heading2") || styleVal === "2") {
2270
- html += `<h3 style="font-size: 18px; font-weight: 600; color: #2563eb; margin: 16px 0 8px;">${textContent}</h3>`;
2271
- } else if (lowerStyle.includes("heading3") || styleVal === "3") {
2272
- html += `<h4 style="font-size: 15px; font-weight: 600; color: #334155; margin: 12px 0 6px;">${textContent}</h4>`;
2273
- } else if (numPr) {
2274
- 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>`;
2275
- } else {
2276
- html += `<p style="margin: 8px 0; font-size: 14px;">${textContent}</p>`;
2310
+ const chunks = this.extractParagraphChunks(child, imageMap);
2311
+ for (let i = 0; i < chunks.length; i++) {
2312
+ if (i > 0) {
2313
+ newPage();
2314
+ }
2315
+ const textContent = chunks[i];
2316
+ if (!textContent.trim() && !textContent.includes("<img")) {
2317
+ currentHtml += '<div style="height: 10px;"></div>';
2318
+ continue;
2319
+ }
2320
+ const lowerStyle = styleVal.toLowerCase();
2321
+ if (lowerStyle.includes("title")) {
2322
+ 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>`;
2323
+ } else if (lowerStyle.includes("heading1") || styleVal === "1") {
2324
+ currentHtml += `<h2 style="font-size: 22px; font-weight: 700; color: #1e40af; margin: 20px 0 10px;">${textContent}</h2>`;
2325
+ } else if (lowerStyle.includes("heading2") || styleVal === "2") {
2326
+ currentHtml += `<h3 style="font-size: 18px; font-weight: 600; color: #2563eb; margin: 16px 0 8px;">${textContent}</h3>`;
2327
+ } else if (lowerStyle.includes("heading3") || styleVal === "3") {
2328
+ currentHtml += `<h4 style="font-size: 15px; font-weight: 600; color: #334155; margin: 12px 0 6px;">${textContent}</h4>`;
2329
+ } else if (numPr) {
2330
+ 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>`;
2331
+ } else {
2332
+ currentHtml += `<p style="margin: 8px 0; font-size: 14px;">${textContent}</p>`;
2333
+ }
2277
2334
  }
2278
2335
  } else if (tag === "tbl") {
2279
- html += '<table style="width: 100%; border-collapse: collapse; margin: 20px 0; border: 1px solid #cbd5e1;">';
2336
+ currentHtml += '<table style="width: 100%; border-collapse: collapse; margin: 20px 0; border: 1px solid #cbd5e1;">';
2280
2337
  const rows = Array.from(child.getElementsByTagNameNS("*", "tr"));
2281
2338
  rows.forEach((tr, rIdx) => {
2282
- html += `<tr style="${rIdx === 0 ? "background-color: #f8fafc; font-weight: 600;" : ""}">`;
2339
+ currentHtml += `<tr style="${rIdx === 0 ? "background-color: #f8fafc; font-weight: 600;" : ""}">`;
2283
2340
  const cells = Array.from(tr.getElementsByTagNameNS("*", "tc"));
2284
2341
  cells.forEach((tc) => {
2285
2342
  const cellText = this.extractParagraphHtml(tc, imageMap);
2286
- html += `<td style="border: 1px solid #cbd5e1; padding: 8px 12px; font-size: 13px;">${cellText || "&nbsp;"}</td>`;
2343
+ currentHtml += `<td style="border: 1px solid #cbd5e1; padding: 8px 12px; font-size: 13px;">${cellText || "&nbsp;"}</td>`;
2287
2344
  });
2288
- html += "</tr>";
2345
+ currentHtml += "</tr>";
2289
2346
  });
2290
- html += "</table>";
2347
+ currentHtml += "</table>";
2291
2348
  }
2292
2349
  }
2293
- card.innerHTML = DOMPurify2.sanitize(html, {
2294
- ADD_TAGS: ["h1", "h2", "h3", "h4", "p", "table", "tr", "td", "span", "b", "i", "u", "s", "strike", "img", "div", "br"],
2295
- ADD_ATTR: ["style", "src", "alt", "colspan", "rowspan"]
2296
- });
2297
- wrapper.appendChild(card);
2350
+ flushHtml();
2351
+ for (const page of pages) {
2352
+ page.card.innerHTML = DOMPurify6.sanitize(page.html, {
2353
+ ADD_TAGS: ["h1", "h2", "h3", "h4", "p", "table", "tr", "td", "span", "b", "i", "u", "s", "strike", "img", "div", "br"],
2354
+ ADD_ATTR: ["style", "src", "alt", "colspan", "rowspan"]
2355
+ });
2356
+ wrapper.appendChild(page.card);
2357
+ }
2298
2358
  }
2299
2359
  extractParagraphHtml(pElement, imageMap = {}) {
2300
- let result = "";
2360
+ return this.extractParagraphChunks(pElement, imageMap).join("<br/>");
2361
+ }
2362
+ extractParagraphChunks(pElement, imageMap = {}) {
2363
+ const chunks = [""];
2364
+ let currentChunkIndex = 0;
2301
2365
  const drawings = Array.from(pElement.getElementsByTagNameNS("*", "drawing"));
2302
2366
  for (const drawing of drawings) {
2303
2367
  const blip = drawing.getElementsByTagNameNS("*", "blip")[0];
2304
2368
  const rId = blip?.getAttribute("r:embed") || blip?.getAttribute("r:id");
2305
2369
  if (rId && imageMap[rId]) {
2306
- result += `<div style="text-align:center; margin: 12px 0;"><img src="${imageMap[rId]}" style="max-width: 100%; height: auto; border-radius: 4px;" /></div>`;
2370
+ chunks[currentChunkIndex] += `<div style="text-align:center; margin: 12px 0;"><img src="${imageMap[rId]}" style="max-width: 100%; height: auto; border-radius: 4px;" /></div>`;
2307
2371
  }
2308
2372
  }
2309
2373
  const runs = Array.from(pElement.getElementsByTagNameNS("*", "r"));
2310
- if (runs.length === 0 && !result) {
2311
- return DOMPurify2.sanitize(pElement.textContent || "");
2374
+ if (runs.length === 0 && !chunks[currentChunkIndex]) {
2375
+ chunks[currentChunkIndex] = DOMPurify6.sanitize(pElement.textContent || "");
2376
+ return chunks;
2312
2377
  }
2313
2378
  for (const r of runs) {
2314
2379
  const blip = r.getElementsByTagNameNS("*", "blip")[0] || r.getElementsByTagNameNS("*", "imagedata")[0];
2315
2380
  const rId = blip?.getAttribute("r:embed") || blip?.getAttribute("r:id");
2316
2381
  if (rId && imageMap[rId]) {
2317
- result += `<img src="${imageMap[rId]}" style="max-width: 100%; height: auto; display: inline-block; margin: 4px;" />`;
2382
+ chunks[currentChunkIndex] += `<img src="${imageMap[rId]}" style="max-width: 100%; height: auto; display: inline-block; margin: 4px;" />`;
2318
2383
  }
2319
- const brs = r.getElementsByTagNameNS("*", "br");
2320
- for (let i = 0; i < brs.length; i++) {
2321
- result += "<br/>";
2384
+ const brs = Array.from(r.getElementsByTagNameNS("*", "br"));
2385
+ for (const br of brs) {
2386
+ const type = br.getAttribute("w:type") || br.getAttribute("type");
2387
+ if (type === "page") {
2388
+ chunks.push("");
2389
+ currentChunkIndex++;
2390
+ } else {
2391
+ chunks[currentChunkIndex] += "<br/>";
2392
+ }
2322
2393
  }
2323
2394
  const tabs = r.getElementsByTagNameNS("*", "tab");
2324
2395
  if (tabs.length > 0) {
2325
- result += "&emsp;";
2396
+ chunks[currentChunkIndex] += "&emsp;";
2326
2397
  }
2327
2398
  const rPr = r.getElementsByTagNameNS("*", "rPr")[0];
2328
2399
  const isBold = !!rPr?.getElementsByTagNameNS("*", "b")[0];
@@ -2336,7 +2407,7 @@ var DocxPlugin = class {
2336
2407
  const texts = Array.from(r.getElementsByTagNameNS("*", "t"));
2337
2408
  let text = texts.map((t) => t.textContent || "").join("");
2338
2409
  if (!text) continue;
2339
- text = DOMPurify2.sanitize(text);
2410
+ text = DOMPurify6.sanitize(text);
2340
2411
  let styles = "";
2341
2412
  if (isBold) styles += "font-weight: bold; ";
2342
2413
  if (isItalic) styles += "font-style: italic; ";
@@ -2348,12 +2419,12 @@ var DocxPlugin = class {
2348
2419
  if (!isNaN(pt) && pt > 0) styles += `font-size: ${pt}pt; `;
2349
2420
  }
2350
2421
  if (styles) {
2351
- result += `<span style="${styles}">${text}</span>`;
2422
+ chunks[currentChunkIndex] += `<span style="${styles}">${text}</span>`;
2352
2423
  } else {
2353
- result += text;
2424
+ chunks[currentChunkIndex] += text;
2354
2425
  }
2355
2426
  }
2356
- return result;
2427
+ return chunks;
2357
2428
  }
2358
2429
  renderBinaryDocFallback(ctx, wrapper) {
2359
2430
  const card = document.createElement("div");
@@ -2370,14 +2441,14 @@ var DocxPlugin = class {
2370
2441
  const wordDocStream = cfbf.readStream("WordDocument");
2371
2442
  if (wordDocStream && wordDocStream.length >= 512) {
2372
2443
  const text2 = this.extractReadableStrings(wordDocStream);
2373
- card.innerHTML = `<div style="white-space: pre-wrap;">${DOMPurify2.sanitize(text2)}</div>`;
2444
+ card.innerHTML = `<div style="white-space: pre-wrap;">${DOMPurify6.sanitize(text2)}</div>`;
2374
2445
  wrapper.appendChild(card);
2375
2446
  return;
2376
2447
  }
2377
2448
  } catch {
2378
2449
  }
2379
2450
  const text = this.extractReadableStrings(new Uint8Array(ctx.buffer));
2380
- card.innerHTML = `<div style="white-space: pre-wrap;">${DOMPurify2.sanitize(text)}</div>`;
2451
+ card.innerHTML = `<div style="white-space: pre-wrap;">${DOMPurify6.sanitize(text)}</div>`;
2381
2452
  wrapper.appendChild(card);
2382
2453
  }
2383
2454
  extractReadableStrings(bytes) {
@@ -2976,41 +3047,94 @@ var CodePlugin = class {
2976
3047
  async render(ctx) {
2977
3048
  const decoder = new TextDecoder("utf-8");
2978
3049
  const fullText = decoder.decode(ctx.buffer);
2979
- const pageSplitRegex = /(?:\f|\x0C|(?:\r?\n|^)\s*[-=_]{3,}\s*(?:PAGE|Page|page break|Page Break)[\s\d\w-]*[-=_]{3,}\s*(?:\r?\n|$))/i;
2980
- const rawPages = fullText.split(pageSplitRegex).map((p) => p.trim()).filter((p) => p.length > 0);
3050
+ const extRaw = ctx.metadata.extension?.toLowerCase();
3051
+ const mimeRaw = ctx.metadata.mimeType?.toLowerCase();
3052
+ const isTxt = extRaw === ".txt" || mimeRaw === "text/plain" && (!extRaw || !this.extensions.filter((e) => e !== ".txt").includes(extRaw));
3053
+ let rawPages = [];
3054
+ if (isTxt) {
3055
+ const explicitPages = fullText.split(/(?:\f|\x0C)/);
3056
+ for (const ep of explicitPages) {
3057
+ const lines = ep.split(/\r?\n/);
3058
+ let currentChunk = [];
3059
+ for (let i = 0; i < lines.length; i++) {
3060
+ currentChunk.push(lines[i]);
3061
+ if (currentChunk.length >= 46) {
3062
+ rawPages.push(currentChunk.join("\n"));
3063
+ currentChunk = [];
3064
+ }
3065
+ }
3066
+ if (currentChunk.length > 0 || lines.length === 0) {
3067
+ rawPages.push(currentChunk.join("\n"));
3068
+ }
3069
+ }
3070
+ if (rawPages.length === 0) rawPages = [""];
3071
+ } else {
3072
+ const pageSplitRegex = /(?:\f|\x0C|(?:\r?\n|^)\s*[-=_]{3,}\s*(?:PAGE|Page|page break|Page Break)[\s\d\w-]*[-=_]{3,}\s*(?:\r?\n|$))/i;
3073
+ rawPages = fullText.split(pageSplitRegex).map((p) => p.trim()).filter((p) => p.length > 0);
3074
+ }
2981
3075
  const totalPages = Math.max(1, rawPages.length);
2982
3076
  let currentPage = 1;
2983
3077
  const container = document.createElement("div");
2984
3078
  container.style.width = "100%";
2985
3079
  container.style.height = "100%";
2986
3080
  container.style.overflow = "auto";
2987
- container.style.backgroundColor = "#1e1e1e";
2988
- container.style.color = "#d4d4d4";
2989
- container.style.padding = "16px";
2990
- container.style.boxSizing = "border-box";
2991
3081
  let fontSize = 13;
2992
3082
  let rotation = 0;
3083
+ let zoomLevel = 1;
2993
3084
  const pre = document.createElement("pre");
2994
- pre.style.margin = "0";
2995
- pre.style.fontFamily = "Consolas, Menlo, Monaco, monospace";
2996
- pre.style.fontSize = `${fontSize}px`;
2997
- pre.style.lineHeight = "1.5";
2998
- pre.style.whiteSpace = "pre-wrap";
2999
- pre.style.wordBreak = "break-all";
3000
3085
  const code = document.createElement("code");
3086
+ if (isTxt) {
3087
+ container.style.backgroundColor = "#f1f5f9";
3088
+ container.style.padding = "32px";
3089
+ container.style.boxSizing = "border-box";
3090
+ container.style.display = "flex";
3091
+ container.style.flexDirection = "column";
3092
+ container.style.alignItems = "center";
3093
+ pre.style.margin = "0";
3094
+ pre.style.fontFamily = "Consolas, 'Courier New', monospace";
3095
+ pre.style.fontSize = "13px";
3096
+ pre.style.color = "#1e293b";
3097
+ pre.style.lineHeight = "1.5";
3098
+ pre.style.whiteSpace = "pre-wrap";
3099
+ pre.style.wordBreak = "break-word";
3100
+ pre.style.backgroundColor = "#ffffff";
3101
+ pre.style.width = "816px";
3102
+ pre.style.minHeight = "1056px";
3103
+ pre.style.padding = "72px 56px";
3104
+ pre.style.boxSizing = "border-box";
3105
+ pre.style.boxShadow = "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)";
3106
+ pre.style.borderRadius = "4px";
3107
+ pre.style.transformOrigin = "top center";
3108
+ } else {
3109
+ container.style.backgroundColor = "#1e1e1e";
3110
+ container.style.color = "#d4d4d4";
3111
+ container.style.padding = "16px";
3112
+ container.style.boxSizing = "border-box";
3113
+ pre.style.margin = "0";
3114
+ pre.style.fontFamily = "Consolas, Menlo, Monaco, monospace";
3115
+ pre.style.fontSize = `${fontSize}px`;
3116
+ pre.style.lineHeight = "1.5";
3117
+ pre.style.whiteSpace = "pre-wrap";
3118
+ pre.style.wordBreak = "break-all";
3119
+ pre.style.transformOrigin = "top left";
3120
+ }
3001
3121
  const ext = (ctx.metadata.extension || "").replace(".", "");
3002
3122
  const renderCodePage = (text) => {
3003
- try {
3004
- if (ext && hljs.getLanguage(ext)) {
3005
- code.innerHTML = hljs.highlight(text, { language: ext }).value;
3006
- } else {
3007
- code.innerHTML = hljs.highlightAuto(text).value;
3008
- }
3009
- } catch {
3123
+ if (isTxt) {
3010
3124
  code.textContent = text;
3125
+ } else {
3126
+ try {
3127
+ if (ext && hljs.getLanguage(ext)) {
3128
+ code.innerHTML = hljs.highlight(text, { language: ext }).value;
3129
+ } else {
3130
+ code.innerHTML = hljs.highlightAuto(text).value;
3131
+ }
3132
+ } catch {
3133
+ code.textContent = text;
3134
+ }
3011
3135
  }
3012
3136
  };
3013
- renderCodePage(rawPages[0] || fullText);
3137
+ renderCodePage(rawPages[0] || (isTxt ? "" : fullText));
3014
3138
  pre.appendChild(code);
3015
3139
  container.appendChild(pre);
3016
3140
  let indicator = null;
@@ -3019,15 +3143,22 @@ var CodePlugin = class {
3019
3143
  indicator.className = "fp-code-page-indicator";
3020
3144
  indicator.style.position = "sticky";
3021
3145
  indicator.style.bottom = "16px";
3022
- indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
3023
- indicator.style.backdropFilter = "blur(8px)";
3024
- indicator.style.color = "#f8fafc";
3146
+ if (isTxt) {
3147
+ indicator.style.backgroundColor = "rgba(255, 255, 255, 0.9)";
3148
+ indicator.style.color = "#334155";
3149
+ indicator.style.border = "1px solid #e2e8f0";
3150
+ indicator.style.boxShadow = "0 1px 3px rgba(0,0,0,0.1)";
3151
+ } else {
3152
+ indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
3153
+ indicator.style.color = "#f8fafc";
3154
+ indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
3155
+ indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
3156
+ indicator.style.backdropFilter = "blur(8px)";
3157
+ }
3025
3158
  indicator.style.fontSize = "12px";
3026
3159
  indicator.style.fontWeight = "600";
3027
3160
  indicator.style.padding = "5px 14px";
3028
3161
  indicator.style.borderRadius = "20px";
3029
- indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
3030
- indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
3031
3162
  indicator.style.zIndex = "10";
3032
3163
  indicator.style.userSelect = "none";
3033
3164
  indicator.style.pointerEvents = "none";
@@ -3038,7 +3169,7 @@ var CodePlugin = class {
3038
3169
  }
3039
3170
  const showPage = (pageNum) => {
3040
3171
  currentPage = Math.max(1, Math.min(totalPages, pageNum));
3041
- renderCodePage(rawPages[currentPage - 1] || fullText);
3172
+ renderCodePage(rawPages[currentPage - 1] || (isTxt ? "" : fullText));
3042
3173
  if (indicator) {
3043
3174
  indicator.textContent = `Page ${currentPage} of ${totalPages}`;
3044
3175
  }
@@ -3054,39 +3185,57 @@ var CodePlugin = class {
3054
3185
  ctx.container.innerHTML = "";
3055
3186
  };
3056
3187
  ctx.signal.addEventListener("abort", cleanup);
3188
+ const updateTransform = () => {
3189
+ if (isTxt) {
3190
+ pre.style.transform = `scale(${zoomLevel}) rotate(${rotation}deg)`;
3191
+ } else {
3192
+ pre.style.transform = `rotate(${rotation}deg)`;
3193
+ pre.style.fontSize = `${fontSize}px`;
3194
+ }
3195
+ };
3057
3196
  return {
3058
3197
  destroy: cleanup,
3059
3198
  getPageCount: () => totalPages,
3060
3199
  getCurrentPage: () => currentPage,
3061
3200
  goToPage: (page) => showPage(page),
3062
3201
  zoomIn: () => {
3063
- fontSize = Math.min(32, fontSize + 2);
3064
- pre.style.fontSize = `${fontSize}px`;
3202
+ if (isTxt) {
3203
+ zoomLevel = Math.min(3, zoomLevel + 0.1);
3204
+ } else {
3205
+ fontSize = Math.min(32, fontSize + 2);
3206
+ }
3207
+ updateTransform();
3065
3208
  },
3066
3209
  zoomOut: () => {
3067
- fontSize = Math.max(8, fontSize - 2);
3068
- pre.style.fontSize = `${fontSize}px`;
3210
+ if (isTxt) {
3211
+ zoomLevel = Math.max(0.1, zoomLevel - 0.1);
3212
+ } else {
3213
+ fontSize = Math.max(8, fontSize - 2);
3214
+ }
3215
+ updateTransform();
3069
3216
  },
3070
- getZoom: () => fontSize / 13,
3217
+ getZoom: () => isTxt ? zoomLevel : fontSize / 13,
3071
3218
  setZoom: (level) => {
3072
- fontSize = Math.round(13 * level);
3073
- pre.style.fontSize = `${fontSize}px`;
3219
+ if (isTxt) {
3220
+ zoomLevel = level;
3221
+ } else {
3222
+ fontSize = Math.round(13 * level);
3223
+ }
3224
+ updateTransform();
3074
3225
  },
3075
3226
  fitToPage: () => {
3076
3227
  fontSize = 13;
3077
3228
  rotation = 0;
3078
- pre.style.fontSize = "13px";
3079
- pre.style.transform = "none";
3229
+ zoomLevel = 1;
3230
+ updateTransform();
3080
3231
  },
3081
3232
  rotateCW: () => {
3082
3233
  rotation = (rotation + 90) % 360;
3083
- pre.style.transform = `rotate(${rotation}deg)`;
3084
- pre.style.transformOrigin = "top left";
3234
+ updateTransform();
3085
3235
  },
3086
3236
  rotateCCW: () => {
3087
3237
  rotation = (rotation - 90 + 360) % 360;
3088
- pre.style.transform = `rotate(${rotation}deg)`;
3089
- pre.style.transformOrigin = "top left";
3238
+ updateTransform();
3090
3239
  },
3091
3240
  download: () => {
3092
3241
  const mimeType = ctx.metadata.mimeType || "text/plain";
@@ -3385,7 +3534,7 @@ var MarkdownPlugin = class {
3385
3534
  gfm: true,
3386
3535
  breaks: true
3387
3536
  });
3388
- const cleanHtml = DOMPurify2.sanitize(rawHtml, {
3537
+ const cleanHtml = DOMPurify6.sanitize(rawHtml, {
3389
3538
  USE_PROFILES: { html: true }
3390
3539
  });
3391
3540
  const wrapper = document.createElement("div");
@@ -4021,6 +4170,14 @@ var RtfPlugin = class {
4021
4170
  group: "actions",
4022
4171
  execute: () => instance.download?.()
4023
4172
  },
4173
+ {
4174
+ id: "copy",
4175
+ icon: "copy",
4176
+ label: "Copy Text",
4177
+ type: "button",
4178
+ group: "actions",
4179
+ execute: () => instance.copy?.()
4180
+ },
4024
4181
  {
4025
4182
  id: "print",
4026
4183
  icon: "print",
@@ -4082,14 +4239,31 @@ var RtfPlugin = class {
4082
4239
  } catch (err) {
4083
4240
  console.warn("[RtfPlugin] RTF render error, fallback text:", err);
4084
4241
  const text = new TextDecoder("latin1").decode(ctx.buffer);
4085
- const clean = text.replace(/\\par[d]?/g, "\n").replace(/\\[a-zA-Z0-9\-]+/g, "").replace(/[{}]/g, "");
4086
- const pre = document.createElement("pre");
4087
- pre.style.whiteSpace = "pre-wrap";
4088
- pre.style.fontFamily = "serif";
4089
- pre.style.color = "#333";
4090
- pre.textContent = clean;
4091
- wrapper.appendChild(pre);
4092
- pageElements = [pre];
4242
+ const rawPages = text.split(/\\page\b/).map((segment) => {
4243
+ return segment.replace(/\\par[d]?/g, "\n").replace(/\\[a-zA-Z0-9\-]+/g, "").replace(/[{}]/g, "").trim();
4244
+ }).filter((p) => p.length > 0);
4245
+ const pages = rawPages.length > 0 ? rawPages : [text.replace(/\\par[d]?/g, "\n").replace(/\\[a-zA-Z0-9\-]+/g, "").replace(/[{}]/g, "").trim()];
4246
+ for (let i = 0; i < pages.length; i++) {
4247
+ const pageCard = document.createElement("div");
4248
+ pageCard.className = "fp-rtf-page-card";
4249
+ pageCard.style.backgroundColor = "#ffffff";
4250
+ pageCard.style.boxShadow = "0 2px 12px rgba(0,0,0,0.08)";
4251
+ pageCard.style.borderRadius = "4px";
4252
+ pageCard.style.padding = "72px 56px";
4253
+ pageCard.style.width = "816px";
4254
+ pageCard.style.minHeight = "1056px";
4255
+ pageCard.style.maxWidth = "100%";
4256
+ pageCard.style.boxSizing = "border-box";
4257
+ pageCard.style.fontFamily = "serif";
4258
+ pageCard.style.fontSize = "12pt";
4259
+ pageCard.style.lineHeight = "1.6";
4260
+ pageCard.style.color = "#1e293b";
4261
+ pageCard.style.whiteSpace = "pre-wrap";
4262
+ pageCard.style.display = i === 0 ? "block" : "none";
4263
+ pageCard.textContent = pages[i];
4264
+ wrapper.appendChild(pageCard);
4265
+ pageElements.push(pageCard);
4266
+ }
4093
4267
  }
4094
4268
  const totalPages = Math.max(1, pageElements.length);
4095
4269
  let currentPage = 1;
@@ -4177,6 +4351,10 @@ var RtfPlugin = class {
4177
4351
  a.click();
4178
4352
  URL.revokeObjectURL(url);
4179
4353
  },
4354
+ copy: () => {
4355
+ const text = wrapper.textContent || "";
4356
+ navigator.clipboard?.writeText(text);
4357
+ },
4180
4358
  print: () => {
4181
4359
  window.print();
4182
4360
  }
@@ -4251,7 +4429,7 @@ var HtmlPreviewPlugin = class {
4251
4429
  }
4252
4430
  async render(ctx) {
4253
4431
  const rawHtml = new TextDecoder("utf-8").decode(ctx.buffer);
4254
- const sanitized = DOMPurify2.sanitize(rawHtml, {
4432
+ const sanitized = DOMPurify6.sanitize(rawHtml, {
4255
4433
  WHOLE_DOCUMENT: true,
4256
4434
  ADD_TAGS: ["style", "link"],
4257
4435
  ADD_ATTR: ["target", "rel"]
@@ -4517,14 +4695,97 @@ var OpenDocumentPlugin = class {
4517
4695
  wrapper.textContent = contentDoc.documentElement.textContent || "Formula content";
4518
4696
  }
4519
4697
  } else {
4520
- wrapper.style.maxWidth = "850px";
4521
- wrapper.style.padding = "48px";
4698
+ wrapper.style.maxWidth = "none";
4699
+ wrapper.style.padding = "0";
4522
4700
  wrapper.style.minHeight = "100%";
4701
+ wrapper.style.backgroundColor = "transparent";
4702
+ wrapper.style.boxShadow = "none";
4703
+ wrapper.style.position = "relative";
4704
+ const dims = this.getPageDimensions(stylesDoc, contentDoc);
4523
4705
  const bodyHtml = this.renderOdfBody(contentDoc, styleMap, imageUrls);
4524
- wrapper.innerHTML = DOMPurify2.sanitize(bodyHtml, {
4525
- ADD_TAGS: ["math", "semantics", "mrow", "mi", "mo", "mn", "msup", "msub"],
4706
+ const tempDiv = document.createElement("div");
4707
+ tempDiv.style.width = `${dims.width - dims.marginLeft - dims.marginRight}px`;
4708
+ tempDiv.style.position = "absolute";
4709
+ tempDiv.style.visibility = "hidden";
4710
+ tempDiv.innerHTML = DOMPurify6.sanitize(bodyHtml, {
4711
+ ADD_TAGS: ["math", "semantics", "mrow", "mi", "mo", "mn", "msup", "msub", "hr"],
4526
4712
  ADD_ATTR: ["style", "colspan", "rowspan"]
4527
4713
  });
4714
+ document.body.appendChild(tempDiv);
4715
+ const contentHeight = dims.height - dims.marginTop - dims.marginBottom;
4716
+ const pageElements = [[]];
4717
+ let currentHeight = 0;
4718
+ let currentPageIdx = 0;
4719
+ Array.from(tempDiv.children).forEach((child) => {
4720
+ const el = child;
4721
+ const style = el.getAttribute("style") || "";
4722
+ const isBreakBefore = style.includes("page-break-before: always");
4723
+ const isBreakAfter = style.includes("page-break-after: always");
4724
+ const isSoftBreak = el.classList.contains("odf-page-break");
4725
+ if (isBreakBefore) {
4726
+ if (pageElements[currentPageIdx].length > 0) {
4727
+ currentPageIdx++;
4728
+ pageElements.push([]);
4729
+ currentHeight = 0;
4730
+ }
4731
+ }
4732
+ const h = el.offsetHeight || 0;
4733
+ if (currentHeight + h > contentHeight && pageElements[currentPageIdx].length > 0 && !isSoftBreak) {
4734
+ currentPageIdx++;
4735
+ pageElements.push([]);
4736
+ currentHeight = 0;
4737
+ }
4738
+ if (!isSoftBreak) {
4739
+ pageElements[currentPageIdx].push(el.cloneNode(true));
4740
+ currentHeight += h;
4741
+ }
4742
+ if (isBreakAfter || isSoftBreak) {
4743
+ currentPageIdx++;
4744
+ pageElements.push([]);
4745
+ currentHeight = 0;
4746
+ }
4747
+ });
4748
+ document.body.removeChild(tempDiv);
4749
+ if (pageElements.length > 1 && pageElements[pageElements.length - 1].length === 0) {
4750
+ pageElements.pop();
4751
+ }
4752
+ totalPages = Math.max(1, pageElements.length);
4753
+ pageElements.forEach((elements, idx) => {
4754
+ const page = document.createElement("div");
4755
+ page.className = `fp-odt-page fp-odt-page-${idx + 1}`;
4756
+ page.style.width = `${dims.width}px`;
4757
+ page.style.minHeight = `${dims.height}px`;
4758
+ page.style.padding = `${dims.marginTop}px ${dims.marginRight}px ${dims.marginBottom}px ${dims.marginLeft}px`;
4759
+ page.style.margin = "0 auto";
4760
+ page.style.backgroundColor = "#ffffff";
4761
+ page.style.boxShadow = "0 2px 10px rgba(0,0,0,0.08)";
4762
+ page.style.borderRadius = "4px";
4763
+ page.style.boxSizing = "border-box";
4764
+ page.style.display = idx === 0 ? "block" : "none";
4765
+ page.style.position = "absolute";
4766
+ page.style.top = "0";
4767
+ page.style.left = "50%";
4768
+ page.style.transform = "translateX(-50%)";
4769
+ elements.forEach((el) => page.appendChild(el));
4770
+ wrapper.appendChild(page);
4771
+ slides.push(page);
4772
+ });
4773
+ const pageIndicator = document.createElement("div");
4774
+ pageIndicator.className = "fp-odt-page-indicator";
4775
+ pageIndicator.style.position = "sticky";
4776
+ pageIndicator.style.bottom = "16px";
4777
+ pageIndicator.style.left = "50%";
4778
+ pageIndicator.style.transform = "translateX(-50%)";
4779
+ pageIndicator.style.backgroundColor = "rgba(0, 0, 0, 0.6)";
4780
+ pageIndicator.style.color = "#fff";
4781
+ pageIndicator.style.padding = "6px 12px";
4782
+ pageIndicator.style.borderRadius = "16px";
4783
+ pageIndicator.style.fontSize = "12px";
4784
+ pageIndicator.style.zIndex = "100";
4785
+ pageIndicator.style.display = "inline-block";
4786
+ pageIndicator.style.width = "fit-content";
4787
+ pageIndicator.textContent = `Page 1 of ${totalPages}`;
4788
+ container.appendChild(pageIndicator);
4528
4789
  }
4529
4790
  const cleanup = () => {
4530
4791
  imageUrls.forEach((url) => URL.revokeObjectURL(url));
@@ -4533,11 +4794,15 @@ var OpenDocumentPlugin = class {
4533
4794
  };
4534
4795
  ctx.signal.addEventListener("abort", cleanup);
4535
4796
  const goToPage = (page) => {
4536
- if (!isPresentation || page < 1 || page > totalPages) return;
4797
+ if (page < 1 || page > totalPages) return;
4537
4798
  currentPage = page;
4538
4799
  slides.forEach((s, idx) => {
4539
4800
  s.style.display = idx === page - 1 ? "block" : "none";
4540
4801
  });
4802
+ const indicator = container.querySelector(".fp-odt-page-indicator");
4803
+ if (indicator) {
4804
+ indicator.textContent = `Page ${currentPage} of ${totalPages}`;
4805
+ }
4541
4806
  ctx.emit("page-change", { page: currentPage, total: totalPages });
4542
4807
  };
4543
4808
  return {
@@ -4606,6 +4871,43 @@ var OpenDocumentPlugin = class {
4606
4871
  }
4607
4872
  };
4608
4873
  }
4874
+ getPageDimensions(stylesDoc, contentDoc) {
4875
+ let width = 850;
4876
+ let height = 1123;
4877
+ let marginTop = 48;
4878
+ let marginBottom = 48;
4879
+ let marginLeft = 48;
4880
+ let marginRight = 48;
4881
+ const parseUnit = (val) => {
4882
+ if (!val) return null;
4883
+ if (val.endsWith("cm")) return parseFloat(val) * 37.8;
4884
+ if (val.endsWith("mm")) return parseFloat(val) * 3.78;
4885
+ if (val.endsWith("in")) return parseFloat(val) * 96;
4886
+ if (val.endsWith("pt")) return parseFloat(val) * 1.33;
4887
+ if (val.endsWith("px")) return parseFloat(val);
4888
+ return parseFloat(val);
4889
+ };
4890
+ const docs = [stylesDoc, contentDoc].filter(Boolean);
4891
+ for (const doc of docs) {
4892
+ const pageLayout = doc.querySelector("page-layout-properties, [page-width]");
4893
+ if (pageLayout) {
4894
+ const w = parseUnit(pageLayout.getAttribute("fo:page-width") || pageLayout.getAttribute("page-width"));
4895
+ const h = parseUnit(pageLayout.getAttribute("fo:page-height") || pageLayout.getAttribute("page-height"));
4896
+ const mt = parseUnit(pageLayout.getAttribute("fo:margin-top") || pageLayout.getAttribute("margin-top"));
4897
+ const mb = parseUnit(pageLayout.getAttribute("fo:margin-bottom") || pageLayout.getAttribute("margin-bottom"));
4898
+ const ml = parseUnit(pageLayout.getAttribute("fo:margin-left") || pageLayout.getAttribute("margin-left"));
4899
+ const mr = parseUnit(pageLayout.getAttribute("fo:margin-right") || pageLayout.getAttribute("margin-right"));
4900
+ if (w !== null) width = w;
4901
+ if (h !== null) height = h;
4902
+ if (mt !== null) marginTop = mt;
4903
+ if (mb !== null) marginBottom = mb;
4904
+ if (ml !== null) marginLeft = ml;
4905
+ if (mr !== null) marginRight = mr;
4906
+ break;
4907
+ }
4908
+ }
4909
+ return { width, height, marginTop, marginBottom, marginLeft, marginRight };
4910
+ }
4609
4911
  extractStyles(stylesDoc, contentDoc) {
4610
4912
  const map = /* @__PURE__ */ new Map();
4611
4913
  const styleNodes = [];
@@ -4628,14 +4930,21 @@ var OpenDocumentPlugin = class {
4628
4930
  if (color) css += `color: ${color}; `;
4629
4931
  if (size) css += `font-size: ${size}; `;
4630
4932
  }
4631
- const paraProp = node.querySelector("paragraph-properties, [text-align]");
4933
+ let paraProp = node.querySelector("paragraph-properties, [text-align]");
4934
+ if (!paraProp) {
4935
+ paraProp = Array.from(node.children).find((c) => c.tagName.includes("paragraph-properties")) || null;
4936
+ }
4632
4937
  if (paraProp) {
4633
4938
  const align = paraProp.getAttribute("fo:text-align") || paraProp.getAttribute("text-align");
4634
4939
  const mt = paraProp.getAttribute("fo:margin-top") || paraProp.getAttribute("margin-top");
4635
4940
  const mb = paraProp.getAttribute("fo:margin-bottom") || paraProp.getAttribute("margin-bottom");
4941
+ const breakBefore = paraProp.getAttribute("fo:break-before") || paraProp.getAttribute("break-before");
4942
+ const breakAfter = paraProp.getAttribute("fo:break-after") || paraProp.getAttribute("break-after");
4636
4943
  if (align) css += `text-align: ${align}; `;
4637
4944
  if (mt) css += `margin-top: ${mt}; `;
4638
4945
  if (mb) css += `margin-bottom: ${mb}; `;
4946
+ if (breakBefore === "page") css += "page-break-before: always; ";
4947
+ if (breakAfter === "page") css += "page-break-after: always; ";
4639
4948
  }
4640
4949
  if (css) map.set(name, css);
4641
4950
  }
@@ -4720,6 +5029,8 @@ var OpenDocumentPlugin = class {
4720
5029
  result += "&emsp;";
4721
5030
  } else if (tag === "line-break") {
4722
5031
  result += "<br/>";
5032
+ } else if (tag === "soft-page-break") {
5033
+ result += '<hr class="odf-page-break" style="page-break-after: always; border: none; margin: 0; padding: 0; height: 0;" />';
4723
5034
  } else {
4724
5035
  result += el.textContent || "";
4725
5036
  }
@@ -4880,20 +5191,9 @@ var DocPlugin = class {
4880
5191
  container.style.overflow = "auto";
4881
5192
  container.style.padding = "32px 16px";
4882
5193
  container.style.backgroundColor = "#f1f5f9";
4883
- const wrapper = document.createElement("div");
4884
- wrapper.className = "fp-doc-wrapper";
4885
- wrapper.style.maxWidth = "850px";
4886
- wrapper.style.margin = "0 auto";
4887
- wrapper.style.backgroundColor = "#ffffff";
4888
- wrapper.style.boxShadow = "0 2px 12px rgba(0,0,0,0.08)";
4889
- wrapper.style.borderRadius = "4px";
4890
- wrapper.style.padding = "56px 48px";
4891
- wrapper.style.minHeight = "100%";
4892
- wrapper.style.transformOrigin = "top center";
4893
- wrapper.style.transition = "transform 0.2s ease";
4894
- wrapper.style.fontFamily = 'Calibri, "Segoe UI", Arial, sans-serif';
4895
- wrapper.style.color = "#1e293b";
4896
- container.appendChild(wrapper);
5194
+ container.style.display = "flex";
5195
+ container.style.justifyContent = "center";
5196
+ container.style.alignItems = "flex-start";
4897
5197
  ctx.container.appendChild(container);
4898
5198
  let scale = 1;
4899
5199
  let extractedRawText = "";
@@ -4920,21 +5220,27 @@ var DocPlugin = class {
4920
5220
  const rawPages = this.splitIntoPages(extractedRawText);
4921
5221
  const totalPages = Math.max(1, rawPages.length);
4922
5222
  let currentPage = 1;
4923
- wrapper.innerHTML = "";
4924
5223
  const pageCards = [];
4925
5224
  for (let i = 0; i < totalPages; i++) {
4926
5225
  const pageCard = document.createElement("div");
4927
5226
  pageCard.className = "fp-doc-page-card";
5227
+ pageCard.style.width = "816px";
5228
+ pageCard.style.height = "1056px";
4928
5229
  pageCard.style.backgroundColor = "#ffffff";
4929
5230
  pageCard.style.boxShadow = "0 2px 12px rgba(0,0,0,0.08)";
4930
5231
  pageCard.style.borderRadius = "4px";
4931
- pageCard.style.padding = "56px 48px";
4932
- pageCard.style.minHeight = "100%";
5232
+ pageCard.style.padding = "96px 72px";
5233
+ pageCard.style.boxSizing = "border-box";
5234
+ pageCard.style.overflow = "hidden";
4933
5235
  pageCard.style.display = i === 0 ? "block" : "none";
5236
+ pageCard.style.transformOrigin = "top center";
5237
+ pageCard.style.transition = "transform 0.2s ease";
5238
+ pageCard.style.fontFamily = 'Calibri, "Segoe UI", Arial, sans-serif';
5239
+ pageCard.style.color = "#1e293b";
4934
5240
  if (isFallback && i === 0) {
4935
5241
  pageCard.innerHTML = `
4936
5242
  <div style="border-bottom: 1px solid #e2e8f0; padding-bottom: 12px; margin-bottom: 24px;">
4937
- <h2 style="margin: 0 0 6px; font-size: 20px; color: #334155;">${DOMPurify2.sanitize(ctx.metadata.name || "Word Document (.doc)")}</h2>
5243
+ <h2 style="margin: 0 0 6px; font-size: 20px; color: #334155;">${DOMPurify6.sanitize(ctx.metadata.name || "Word Document (.doc)")}</h2>
4938
5244
  <span style="font-size: 12px; color: #64748b; background: #f1f5f9; padding: 2px 8px; border-radius: 4px;">Legacy Word 97-2003 Binary Preview</span>
4939
5245
  </div>
4940
5246
  ${this.formatDocToHtml(rawPages[i], ctx.metadata.name || "Document")}
@@ -4942,15 +5248,17 @@ var DocPlugin = class {
4942
5248
  } else {
4943
5249
  pageCard.innerHTML = this.formatDocToHtml(rawPages[i], ctx.metadata.name || "Document");
4944
5250
  }
4945
- wrapper.appendChild(pageCard);
5251
+ container.appendChild(pageCard);
4946
5252
  pageCards.push(pageCard);
4947
5253
  }
4948
5254
  let indicator = null;
4949
5255
  if (totalPages > 1) {
4950
5256
  indicator = document.createElement("div");
4951
5257
  indicator.className = "fp-doc-page-indicator";
4952
- indicator.style.position = "sticky";
5258
+ indicator.style.position = "fixed";
4953
5259
  indicator.style.bottom = "16px";
5260
+ indicator.style.left = "50%";
5261
+ indicator.style.transform = "translateX(-50%)";
4954
5262
  indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
4955
5263
  indicator.style.backdropFilter = "blur(8px)";
4956
5264
  indicator.style.color = "#f8fafc";
@@ -4964,17 +5272,25 @@ var DocPlugin = class {
4964
5272
  indicator.style.userSelect = "none";
4965
5273
  indicator.style.pointerEvents = "none";
4966
5274
  indicator.style.textAlign = "center";
4967
- indicator.style.width = "fit-content";
4968
- indicator.style.margin = "16px auto 0";
4969
5275
  container.appendChild(indicator);
4970
5276
  }
5277
+ let rotation = 0;
5278
+ const applyTransform = () => {
5279
+ const activeCard = pageCards[currentPage - 1];
5280
+ if (activeCard) {
5281
+ activeCard.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
5282
+ }
5283
+ };
4971
5284
  const showPage = (pageNum) => {
4972
5285
  currentPage = Math.max(1, Math.min(totalPages, pageNum));
4973
- if (totalPages > 1) {
4974
- pageCards.forEach((card, idx) => {
4975
- card.style.display = idx + 1 === currentPage ? "block" : "none";
4976
- });
4977
- }
5286
+ pageCards.forEach((card, idx) => {
5287
+ if (idx + 1 === currentPage) {
5288
+ card.style.display = "block";
5289
+ card.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
5290
+ } else {
5291
+ card.style.display = "none";
5292
+ }
5293
+ });
4978
5294
  if (indicator) {
4979
5295
  indicator.textContent = `Page ${currentPage} of ${totalPages}`;
4980
5296
  }
@@ -4983,19 +5299,13 @@ var DocPlugin = class {
4983
5299
  if (totalPages > 1) {
4984
5300
  showPage(1);
4985
5301
  }
4986
- let rotation = 0;
4987
5302
  const calculateFitScale = () => {
4988
- const activeCard = pageCards[currentPage - 1] || wrapper;
4989
- const elW = activeCard.offsetWidth || 850;
4990
- const elH = activeCard.offsetHeight || 1e3;
5303
+ const elW = 816;
5304
+ const elH = 1056;
4991
5305
  const availW = Math.max(200, ctx.container.clientWidth - 48);
4992
5306
  const availH = Math.max(200, ctx.container.clientHeight - 80);
4993
5307
  return Math.min(1.1, Math.min(availW / elW, availH / elH));
4994
5308
  };
4995
- const applyTransform = () => {
4996
- wrapper.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
4997
- wrapper.style.transformOrigin = "top center";
4998
- };
4999
5309
  setTimeout(() => {
5000
5310
  scale = calculateFitScale();
5001
5311
  applyTransform();
@@ -5153,8 +5463,28 @@ var DocPlugin = class {
5153
5463
  }
5154
5464
  splitIntoPages(text) {
5155
5465
  if (!text) return [""];
5156
- 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);
5157
- return parts.length > 0 ? parts : [text];
5466
+ 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);
5467
+ if (explicitParts.length === 0) explicitParts.push(text);
5468
+ const maxLinesPerPage = 48;
5469
+ const finalPages = [];
5470
+ for (const part of explicitParts) {
5471
+ const lines = part.split(/\r?\n/);
5472
+ let currentLines = [];
5473
+ let count = 0;
5474
+ for (const line of lines) {
5475
+ currentLines.push(line);
5476
+ count++;
5477
+ if (count >= maxLinesPerPage) {
5478
+ finalPages.push(currentLines.join("\n"));
5479
+ currentLines = [];
5480
+ count = 0;
5481
+ }
5482
+ }
5483
+ if (currentLines.length > 0) {
5484
+ finalPages.push(currentLines.join("\n"));
5485
+ }
5486
+ }
5487
+ return finalPages.length > 0 ? finalPages : [text];
5158
5488
  }
5159
5489
  /**
5160
5490
  * Format cleaned extracted text into attractive HTML paragraphs, headings, and lists
@@ -5163,17 +5493,65 @@ var DocPlugin = class {
5163
5493
  const lines = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\x0B/g, "\n").split("\n");
5164
5494
  let html = "";
5165
5495
  let inList = false;
5166
- for (const rawLine of lines) {
5167
- const line = rawLine.trim();
5496
+ let tableLines = [];
5497
+ const flushTable = () => {
5498
+ if (tableLines.length > 0) {
5499
+ html += '<table style="width: 100%; border-collapse: collapse; margin: 16px 0; font-size: 12px;">';
5500
+ for (const tLine of tableLines) {
5501
+ html += "<tr>";
5502
+ const cols = tLine.split(" ");
5503
+ for (const col of cols) {
5504
+ html += `<td style="border: 1px solid #cbd5e1; padding: 6px 8px;">${DOMPurify6.sanitize(col.trim())}</td>`;
5505
+ }
5506
+ html += "</tr>";
5507
+ }
5508
+ html += "</table>";
5509
+ tableLines = [];
5510
+ }
5511
+ };
5512
+ let i = 0;
5513
+ while (i < lines.length) {
5514
+ let line = lines[i];
5515
+ let tabCount = (line.match(/\t/g) || []).length;
5516
+ if (tabCount > 0) {
5517
+ let consecutiveTableLines = 1;
5518
+ let j = i + 1;
5519
+ while (j < lines.length) {
5520
+ const nextTabCount = (lines[j].match(/\t/g) || []).length;
5521
+ if (nextTabCount === tabCount) {
5522
+ consecutiveTableLines++;
5523
+ j++;
5524
+ } else {
5525
+ break;
5526
+ }
5527
+ }
5528
+ if (consecutiveTableLines >= 3) {
5529
+ if (inList) {
5530
+ html += "</ul>";
5531
+ inList = false;
5532
+ }
5533
+ tableLines = lines.slice(i, j);
5534
+ flushTable();
5535
+ i = j;
5536
+ continue;
5537
+ }
5538
+ }
5539
+ line = line.trim();
5168
5540
  if (!line) {
5169
5541
  if (inList) {
5170
5542
  html += "</ul>";
5171
5543
  inList = false;
5172
5544
  }
5545
+ html += '<div style="height: 1.15em;"></div>';
5546
+ i++;
5547
+ continue;
5548
+ }
5549
+ if (/^[\x00-\x1F\x7F-\x9F]+$/.test(line)) {
5550
+ i++;
5173
5551
  continue;
5174
5552
  }
5175
- if (/^[\x00-\x1F\x7F-\x9F]+$/.test(line)) continue;
5176
- if (line.includes("Normal.dot") || line.includes("Microsoft Word") || line.includes("Times New Roman") && line.length < 30) {
5553
+ if ((line.includes("Normal.dot") || line.includes("Microsoft Word") || line.includes("Times New Roman")) && line.length < 30) {
5554
+ i++;
5177
5555
  continue;
5178
5556
  }
5179
5557
  if (line.length < 60 && !line.endsWith(".") && (/^[A-Z0-9\s:_-]+$/.test(line) || line.startsWith("#"))) {
@@ -5181,24 +5559,26 @@ var DocPlugin = class {
5181
5559
  html += "</ul>";
5182
5560
  inList = false;
5183
5561
  }
5184
- html += `<h2 style="font-size: 18px; font-weight: 700; color: #1e3a8a; margin: 20px 0 8px; border-bottom: 1px solid #e2e8f0; padding-bottom: 4px;">${DOMPurify2.sanitize(line)}</h2>`;
5562
+ html += `<h2 style="font-size: 16px; font-weight: 700; color: #1e3a8a; margin: 16px 0 8px; border-bottom: 1px solid #e2e8f0; padding-bottom: 4px;">${DOMPurify6.sanitize(line)}</h2>`;
5185
5563
  } else if (line.startsWith("\u2022") || line.startsWith("-") || line.startsWith("*")) {
5186
5564
  if (!inList) {
5187
5565
  html += '<ul style="margin: 8px 0; padding-left: 24px;">';
5188
5566
  inList = true;
5189
5567
  }
5190
5568
  const bulletText = line.replace(/^[•\-\*]\s*/, "");
5191
- html += `<li style="margin: 4px 0; line-height: 1.6;">${DOMPurify2.sanitize(bulletText)}</li>`;
5569
+ html += `<li style="margin: 4px 0; line-height: 1.15; font-size: 12pt;">${DOMPurify6.sanitize(bulletText)}</li>`;
5192
5570
  } else {
5193
5571
  if (inList) {
5194
5572
  html += "</ul>";
5195
5573
  inList = false;
5196
5574
  }
5197
- html += `<p style="line-height: 1.7; margin: 10px 0; font-size: 14px; text-align: justify;">${DOMPurify2.sanitize(line)}</p>`;
5575
+ html += `<p style="line-height: 1.15; margin: 0; font-size: 12pt; text-align: justify;">${DOMPurify6.sanitize(line)}</p>`;
5198
5576
  }
5577
+ i++;
5199
5578
  }
5200
5579
  if (inList) html += "</ul>";
5201
- return html || `<p style="color: #64748b; font-style: italic;">(No readable text found in ${DOMPurify2.sanitize(filename)})</p>`;
5580
+ flushTable();
5581
+ return html || `<p style="color: #64748b; font-style: italic;">(No readable text found in ${DOMPurify6.sanitize(filename)})</p>`;
5202
5582
  }
5203
5583
  };
5204
5584
  function docPlugin() {
@@ -5376,7 +5756,7 @@ var PptPlugin = class {
5376
5756
  if (s.pictureUrl) {
5377
5757
  contentHtml = `
5378
5758
  <div style="flex: 1; display: flex; justify-content: center; align-items: center; padding: 12px; overflow: hidden;">
5379
- <img src="${s.pictureUrl}" alt="${DOMPurify2.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;" />
5759
+ <img src="${s.pictureUrl}" alt="${DOMPurify6.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;" />
5380
5760
  </div>
5381
5761
  `;
5382
5762
  } else if (s.tableColumns.length > 0) {
@@ -5386,7 +5766,7 @@ var PptPlugin = class {
5386
5766
  <table style="width: 100%; border-collapse: collapse; border: 1px solid #cbd5e1; border-radius: 6px; overflow: hidden; background: #ffffff;">
5387
5767
  <thead>
5388
5768
  <tr style="background: #e2e8f0; color: #1e293b; font-weight: 600; font-size: 14px;">
5389
- ${cols.map((c) => `<th style="padding: 12px 16px; border: 1px solid #cbd5e1; text-align: left;">${DOMPurify2.sanitize(c)}</th>`).join("")}
5769
+ ${cols.map((c) => `<th style="padding: 12px 16px; border: 1px solid #cbd5e1; text-align: left;">${DOMPurify6.sanitize(c)}</th>`).join("")}
5390
5770
  </tr>
5391
5771
  </thead>
5392
5772
  <tbody>
@@ -5402,7 +5782,7 @@ var PptPlugin = class {
5402
5782
  } else {
5403
5783
  const pTags = s.paragraphs.map((p) => {
5404
5784
  const lines = p.split(/[\r\n]+/).map((l) => l.trim()).filter(Boolean);
5405
- return lines.map((l) => `<p style="margin: 0 0 14px; font-size: 14px; line-height: 1.65; color: #334155; text-align: justify;">${DOMPurify2.sanitize(l)}</p>`).join("");
5785
+ return lines.map((l) => `<p style="margin: 0 0 14px; font-size: 14px; line-height: 1.65; color: #334155; text-align: justify;">${DOMPurify6.sanitize(l)}</p>`).join("");
5406
5786
  }).join("");
5407
5787
  contentHtml = `
5408
5788
  <div style="flex: 1; overflow: auto; padding: 4px 8px; display: flex; flex-direction: column; justify-content: flex-start;">
@@ -5415,11 +5795,11 @@ var PptPlugin = class {
5415
5795
  <!-- Header Banner matching PowerPoint design -->
5416
5796
  <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;">
5417
5797
  <h1 style="margin: 0; font-size: 26px; font-weight: 700; color: #1e293b; letter-spacing: -0.3px;">
5418
- ${DOMPurify2.sanitize(s.title)}
5798
+ ${DOMPurify6.sanitize(s.title)}
5419
5799
  </h1>
5420
5800
  ${s.subtitle ? `
5421
5801
  <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);">
5422
- ${DOMPurify2.sanitize(s.subtitle)}
5802
+ ${DOMPurify6.sanitize(s.subtitle)}
5423
5803
  </span>
5424
5804
  ` : `
5425
5805
  <span style="font-size: 12px; color: #365314; font-weight: 600;">