@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/vue.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { defineComponent, ref, onMounted, watch, onBeforeUnmount, h } from 'vue';
2
- import DOMPurify2 from 'dompurify';
2
+ import DOMPurify6 from 'dompurify';
3
3
  import * as pdfjsLib from 'pdfjs-dist';
4
4
  import * as docx from 'docx-preview';
5
5
  import { unzipSync, strFromU8, unzip } from 'fflate';
@@ -357,7 +357,7 @@ async function sourceToArrayBuffer(source, signal) {
357
357
  if (magicMime === "application/zip") {
358
358
  const ooxmlMime = detectOoxmlType(buffer);
359
359
  if (ooxmlMime && ooxmlMime !== "application/zip") {
360
- metadata.mimeType = ooxmlMime;
360
+ metadata.mimeType = metadata.mimeType ?? ooxmlMime;
361
361
  if (ooxmlMime.includes("wordprocessing")) metadata.extension = metadata.extension ?? ".docx";
362
362
  else if (ooxmlMime.includes("spreadsheet")) metadata.extension = metadata.extension ?? ".xlsx";
363
363
  else if (ooxmlMime.includes("presentation")) metadata.extension = metadata.extension ?? ".pptx";
@@ -386,7 +386,7 @@ async function sourceToArrayBuffer(source, signal) {
386
386
  return { buffer, metadata };
387
387
  }
388
388
  function sanitizeSVG(svg) {
389
- return DOMPurify2.sanitize(svg, {
389
+ return DOMPurify6.sanitize(svg, {
390
390
  USE_PROFILES: { svg: true, svgFilters: true }
391
391
  });
392
392
  }
@@ -1448,7 +1448,19 @@ var PdfPlugin = class {
1448
1448
  }
1449
1449
  };
1450
1450
  await renderPage(1);
1451
+ let resizeTimer = null;
1452
+ const resizeObserver = new ResizeObserver(() => {
1453
+ if (resizeTimer) clearTimeout(resizeTimer);
1454
+ resizeTimer = setTimeout(() => {
1455
+ if (Math.abs(zoomScale - 1) < 0.05) {
1456
+ renderPage(currentPage);
1457
+ }
1458
+ }, 150);
1459
+ });
1460
+ resizeObserver.observe(container);
1451
1461
  const cleanup = () => {
1462
+ resizeObserver.disconnect();
1463
+ if (resizeTimer) clearTimeout(resizeTimer);
1452
1464
  if (currentRenderTask) {
1453
1465
  try {
1454
1466
  currentRenderTask.cancel();
@@ -2196,90 +2208,149 @@ var DocxPlugin = class {
2196
2208
  console.warn("[DocxPlugin] Error parsing relationships:", relsErr);
2197
2209
  }
2198
2210
  }
2199
- const card = document.createElement("div");
2200
- card.className = "fp-docx-page-card";
2201
- card.style.backgroundColor = "#ffffff";
2202
- card.style.borderRadius = "6px";
2203
- card.style.boxShadow = "0 4px 24px rgba(0,0,0,0.08)";
2204
- card.style.padding = "48px 56px";
2205
- card.style.fontFamily = 'Calibri, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
2206
- card.style.color = "#1e293b";
2207
- card.style.lineHeight = "1.6";
2211
+ const sectPrs = Array.from(doc.getElementsByTagNameNS("*", "sectPr"));
2212
+ const lastSectPr = sectPrs[sectPrs.length - 1];
2213
+ let defaultW = 12240;
2214
+ let defaultH = 15840;
2215
+ let margins = { top: 1440, right: 1440, bottom: 1440, left: 1440 };
2216
+ if (lastSectPr) {
2217
+ const pgSz = lastSectPr.getElementsByTagNameNS("*", "pgSz")[0];
2218
+ if (pgSz) {
2219
+ defaultW = parseInt(pgSz.getAttribute("w:w") || pgSz.getAttribute("w") || "12240", 10);
2220
+ defaultH = parseInt(pgSz.getAttribute("w:h") || pgSz.getAttribute("h") || "15840", 10);
2221
+ }
2222
+ const pgMar = lastSectPr.getElementsByTagNameNS("*", "pgMar")[0];
2223
+ if (pgMar) {
2224
+ margins.top = parseInt(pgMar.getAttribute("w:top") || pgMar.getAttribute("top") || "1440", 10);
2225
+ margins.bottom = parseInt(pgMar.getAttribute("w:bottom") || pgMar.getAttribute("bottom") || "1440", 10);
2226
+ margins.left = parseInt(pgMar.getAttribute("w:left") || pgMar.getAttribute("left") || "1440", 10);
2227
+ margins.right = parseInt(pgMar.getAttribute("w:right") || pgMar.getAttribute("right") || "1440", 10);
2228
+ }
2229
+ }
2230
+ const createPageCard = () => {
2231
+ const card = document.createElement("div");
2232
+ card.className = "fp-docx-page-card";
2233
+ card.style.backgroundColor = "#ffffff";
2234
+ card.style.borderRadius = "4px";
2235
+ card.style.boxShadow = "0 4px 24px rgba(0,0,0,0.08)";
2236
+ card.style.fontFamily = 'Calibri, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
2237
+ card.style.color = "#1e293b";
2238
+ card.style.lineHeight = "1.6";
2239
+ card.style.boxSizing = "border-box";
2240
+ card.style.overflow = "hidden";
2241
+ card.style.position = "relative";
2242
+ card.style.marginBottom = "24px";
2243
+ card.style.width = `${defaultW / 15}px`;
2244
+ card.style.height = `${defaultH / 15}px`;
2245
+ card.style.padding = `${margins.top / 15}px ${margins.right / 15}px ${margins.bottom / 15}px ${margins.left / 15}px`;
2246
+ return card;
2247
+ };
2248
+ let currentCard = createPageCard();
2249
+ let currentHtml = "";
2250
+ const pages = [{ card: currentCard, html: "" }];
2251
+ const flushHtml = () => {
2252
+ pages[pages.length - 1].html += currentHtml;
2253
+ currentHtml = "";
2254
+ };
2255
+ const newPage = () => {
2256
+ flushHtml();
2257
+ currentCard = createPageCard();
2258
+ pages.push({ card: currentCard, html: "" });
2259
+ };
2208
2260
  const body = doc.getElementsByTagNameNS("*", "body")[0] || doc.documentElement;
2209
- let html = "";
2210
2261
  for (const child of Array.from(body.children)) {
2211
2262
  const tag = child.localName || child.nodeName.split(":").pop();
2212
2263
  if (tag === "p") {
2213
2264
  const pStyle = child.getElementsByTagNameNS("*", "pStyle")[0];
2214
2265
  const styleVal = pStyle?.getAttribute("w:val") || pStyle?.getAttribute("val") || "";
2215
2266
  const numPr = child.getElementsByTagNameNS("*", "numPr")[0];
2216
- const textContent = this.extractParagraphHtml(child, imageMap);
2217
- if (!textContent.trim()) {
2218
- html += '<div style="height: 10px;"></div>';
2219
- continue;
2220
- }
2221
- const lowerStyle = styleVal.toLowerCase();
2222
- if (lowerStyle.includes("title")) {
2223
- 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>`;
2224
- } else if (lowerStyle.includes("heading1") || styleVal === "1") {
2225
- html += `<h2 style="font-size: 22px; font-weight: 700; color: #1e40af; margin: 20px 0 10px;">${textContent}</h2>`;
2226
- } else if (lowerStyle.includes("heading2") || styleVal === "2") {
2227
- html += `<h3 style="font-size: 18px; font-weight: 600; color: #2563eb; margin: 16px 0 8px;">${textContent}</h3>`;
2228
- } else if (lowerStyle.includes("heading3") || styleVal === "3") {
2229
- html += `<h4 style="font-size: 15px; font-weight: 600; color: #334155; margin: 12px 0 6px;">${textContent}</h4>`;
2230
- } else if (numPr) {
2231
- 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>`;
2232
- } else {
2233
- html += `<p style="margin: 8px 0; font-size: 14px;">${textContent}</p>`;
2267
+ const chunks = this.extractParagraphChunks(child, imageMap);
2268
+ for (let i = 0; i < chunks.length; i++) {
2269
+ if (i > 0) {
2270
+ newPage();
2271
+ }
2272
+ const textContent = chunks[i];
2273
+ if (!textContent.trim() && !textContent.includes("<img")) {
2274
+ currentHtml += '<div style="height: 10px;"></div>';
2275
+ continue;
2276
+ }
2277
+ const lowerStyle = styleVal.toLowerCase();
2278
+ if (lowerStyle.includes("title")) {
2279
+ 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>`;
2280
+ } else if (lowerStyle.includes("heading1") || styleVal === "1") {
2281
+ currentHtml += `<h2 style="font-size: 22px; font-weight: 700; color: #1e40af; margin: 20px 0 10px;">${textContent}</h2>`;
2282
+ } else if (lowerStyle.includes("heading2") || styleVal === "2") {
2283
+ currentHtml += `<h3 style="font-size: 18px; font-weight: 600; color: #2563eb; margin: 16px 0 8px;">${textContent}</h3>`;
2284
+ } else if (lowerStyle.includes("heading3") || styleVal === "3") {
2285
+ currentHtml += `<h4 style="font-size: 15px; font-weight: 600; color: #334155; margin: 12px 0 6px;">${textContent}</h4>`;
2286
+ } else if (numPr) {
2287
+ 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>`;
2288
+ } else {
2289
+ currentHtml += `<p style="margin: 8px 0; font-size: 14px;">${textContent}</p>`;
2290
+ }
2234
2291
  }
2235
2292
  } else if (tag === "tbl") {
2236
- html += '<table style="width: 100%; border-collapse: collapse; margin: 20px 0; border: 1px solid #cbd5e1;">';
2293
+ currentHtml += '<table style="width: 100%; border-collapse: collapse; margin: 20px 0; border: 1px solid #cbd5e1;">';
2237
2294
  const rows = Array.from(child.getElementsByTagNameNS("*", "tr"));
2238
2295
  rows.forEach((tr, rIdx) => {
2239
- html += `<tr style="${rIdx === 0 ? "background-color: #f8fafc; font-weight: 600;" : ""}">`;
2296
+ currentHtml += `<tr style="${rIdx === 0 ? "background-color: #f8fafc; font-weight: 600;" : ""}">`;
2240
2297
  const cells = Array.from(tr.getElementsByTagNameNS("*", "tc"));
2241
2298
  cells.forEach((tc) => {
2242
2299
  const cellText = this.extractParagraphHtml(tc, imageMap);
2243
- html += `<td style="border: 1px solid #cbd5e1; padding: 8px 12px; font-size: 13px;">${cellText || "&nbsp;"}</td>`;
2300
+ currentHtml += `<td style="border: 1px solid #cbd5e1; padding: 8px 12px; font-size: 13px;">${cellText || "&nbsp;"}</td>`;
2244
2301
  });
2245
- html += "</tr>";
2302
+ currentHtml += "</tr>";
2246
2303
  });
2247
- html += "</table>";
2304
+ currentHtml += "</table>";
2248
2305
  }
2249
2306
  }
2250
- card.innerHTML = DOMPurify2.sanitize(html, {
2251
- ADD_TAGS: ["h1", "h2", "h3", "h4", "p", "table", "tr", "td", "span", "b", "i", "u", "s", "strike", "img", "div", "br"],
2252
- ADD_ATTR: ["style", "src", "alt", "colspan", "rowspan"]
2253
- });
2254
- wrapper.appendChild(card);
2307
+ flushHtml();
2308
+ for (const page of pages) {
2309
+ page.card.innerHTML = DOMPurify6.sanitize(page.html, {
2310
+ ADD_TAGS: ["h1", "h2", "h3", "h4", "p", "table", "tr", "td", "span", "b", "i", "u", "s", "strike", "img", "div", "br"],
2311
+ ADD_ATTR: ["style", "src", "alt", "colspan", "rowspan"]
2312
+ });
2313
+ wrapper.appendChild(page.card);
2314
+ }
2255
2315
  }
2256
2316
  extractParagraphHtml(pElement, imageMap = {}) {
2257
- let result = "";
2317
+ return this.extractParagraphChunks(pElement, imageMap).join("<br/>");
2318
+ }
2319
+ extractParagraphChunks(pElement, imageMap = {}) {
2320
+ const chunks = [""];
2321
+ let currentChunkIndex = 0;
2258
2322
  const drawings = Array.from(pElement.getElementsByTagNameNS("*", "drawing"));
2259
2323
  for (const drawing of drawings) {
2260
2324
  const blip = drawing.getElementsByTagNameNS("*", "blip")[0];
2261
2325
  const rId = blip?.getAttribute("r:embed") || blip?.getAttribute("r:id");
2262
2326
  if (rId && imageMap[rId]) {
2263
- result += `<div style="text-align:center; margin: 12px 0;"><img src="${imageMap[rId]}" style="max-width: 100%; height: auto; border-radius: 4px;" /></div>`;
2327
+ chunks[currentChunkIndex] += `<div style="text-align:center; margin: 12px 0;"><img src="${imageMap[rId]}" style="max-width: 100%; height: auto; border-radius: 4px;" /></div>`;
2264
2328
  }
2265
2329
  }
2266
2330
  const runs = Array.from(pElement.getElementsByTagNameNS("*", "r"));
2267
- if (runs.length === 0 && !result) {
2268
- return DOMPurify2.sanitize(pElement.textContent || "");
2331
+ if (runs.length === 0 && !chunks[currentChunkIndex]) {
2332
+ chunks[currentChunkIndex] = DOMPurify6.sanitize(pElement.textContent || "");
2333
+ return chunks;
2269
2334
  }
2270
2335
  for (const r of runs) {
2271
2336
  const blip = r.getElementsByTagNameNS("*", "blip")[0] || r.getElementsByTagNameNS("*", "imagedata")[0];
2272
2337
  const rId = blip?.getAttribute("r:embed") || blip?.getAttribute("r:id");
2273
2338
  if (rId && imageMap[rId]) {
2274
- result += `<img src="${imageMap[rId]}" style="max-width: 100%; height: auto; display: inline-block; margin: 4px;" />`;
2339
+ chunks[currentChunkIndex] += `<img src="${imageMap[rId]}" style="max-width: 100%; height: auto; display: inline-block; margin: 4px;" />`;
2275
2340
  }
2276
- const brs = r.getElementsByTagNameNS("*", "br");
2277
- for (let i = 0; i < brs.length; i++) {
2278
- result += "<br/>";
2341
+ const brs = Array.from(r.getElementsByTagNameNS("*", "br"));
2342
+ for (const br of brs) {
2343
+ const type = br.getAttribute("w:type") || br.getAttribute("type");
2344
+ if (type === "page") {
2345
+ chunks.push("");
2346
+ currentChunkIndex++;
2347
+ } else {
2348
+ chunks[currentChunkIndex] += "<br/>";
2349
+ }
2279
2350
  }
2280
2351
  const tabs = r.getElementsByTagNameNS("*", "tab");
2281
2352
  if (tabs.length > 0) {
2282
- result += "&emsp;";
2353
+ chunks[currentChunkIndex] += "&emsp;";
2283
2354
  }
2284
2355
  const rPr = r.getElementsByTagNameNS("*", "rPr")[0];
2285
2356
  const isBold = !!rPr?.getElementsByTagNameNS("*", "b")[0];
@@ -2293,7 +2364,7 @@ var DocxPlugin = class {
2293
2364
  const texts = Array.from(r.getElementsByTagNameNS("*", "t"));
2294
2365
  let text = texts.map((t) => t.textContent || "").join("");
2295
2366
  if (!text) continue;
2296
- text = DOMPurify2.sanitize(text);
2367
+ text = DOMPurify6.sanitize(text);
2297
2368
  let styles = "";
2298
2369
  if (isBold) styles += "font-weight: bold; ";
2299
2370
  if (isItalic) styles += "font-style: italic; ";
@@ -2305,12 +2376,12 @@ var DocxPlugin = class {
2305
2376
  if (!isNaN(pt) && pt > 0) styles += `font-size: ${pt}pt; `;
2306
2377
  }
2307
2378
  if (styles) {
2308
- result += `<span style="${styles}">${text}</span>`;
2379
+ chunks[currentChunkIndex] += `<span style="${styles}">${text}</span>`;
2309
2380
  } else {
2310
- result += text;
2381
+ chunks[currentChunkIndex] += text;
2311
2382
  }
2312
2383
  }
2313
- return result;
2384
+ return chunks;
2314
2385
  }
2315
2386
  renderBinaryDocFallback(ctx, wrapper) {
2316
2387
  const card = document.createElement("div");
@@ -2327,14 +2398,14 @@ var DocxPlugin = class {
2327
2398
  const wordDocStream = cfbf.readStream("WordDocument");
2328
2399
  if (wordDocStream && wordDocStream.length >= 512) {
2329
2400
  const text2 = this.extractReadableStrings(wordDocStream);
2330
- card.innerHTML = `<div style="white-space: pre-wrap;">${DOMPurify2.sanitize(text2)}</div>`;
2401
+ card.innerHTML = `<div style="white-space: pre-wrap;">${DOMPurify6.sanitize(text2)}</div>`;
2331
2402
  wrapper.appendChild(card);
2332
2403
  return;
2333
2404
  }
2334
2405
  } catch {
2335
2406
  }
2336
2407
  const text = this.extractReadableStrings(new Uint8Array(ctx.buffer));
2337
- card.innerHTML = `<div style="white-space: pre-wrap;">${DOMPurify2.sanitize(text)}</div>`;
2408
+ card.innerHTML = `<div style="white-space: pre-wrap;">${DOMPurify6.sanitize(text)}</div>`;
2338
2409
  wrapper.appendChild(card);
2339
2410
  }
2340
2411
  extractReadableStrings(bytes) {
@@ -2933,41 +3004,94 @@ var CodePlugin = class {
2933
3004
  async render(ctx) {
2934
3005
  const decoder = new TextDecoder("utf-8");
2935
3006
  const fullText = decoder.decode(ctx.buffer);
2936
- const pageSplitRegex = /(?:\f|\x0C|(?:\r?\n|^)\s*[-=_]{3,}\s*(?:PAGE|Page|page break|Page Break)[\s\d\w-]*[-=_]{3,}\s*(?:\r?\n|$))/i;
2937
- const rawPages = fullText.split(pageSplitRegex).map((p) => p.trim()).filter((p) => p.length > 0);
3007
+ const extRaw = ctx.metadata.extension?.toLowerCase();
3008
+ const mimeRaw = ctx.metadata.mimeType?.toLowerCase();
3009
+ const isTxt = extRaw === ".txt" || mimeRaw === "text/plain" && (!extRaw || !this.extensions.filter((e) => e !== ".txt").includes(extRaw));
3010
+ let rawPages = [];
3011
+ if (isTxt) {
3012
+ const explicitPages = fullText.split(/(?:\f|\x0C)/);
3013
+ for (const ep of explicitPages) {
3014
+ const lines = ep.split(/\r?\n/);
3015
+ let currentChunk = [];
3016
+ for (let i = 0; i < lines.length; i++) {
3017
+ currentChunk.push(lines[i]);
3018
+ if (currentChunk.length >= 46) {
3019
+ rawPages.push(currentChunk.join("\n"));
3020
+ currentChunk = [];
3021
+ }
3022
+ }
3023
+ if (currentChunk.length > 0 || lines.length === 0) {
3024
+ rawPages.push(currentChunk.join("\n"));
3025
+ }
3026
+ }
3027
+ if (rawPages.length === 0) rawPages = [""];
3028
+ } else {
3029
+ const pageSplitRegex = /(?:\f|\x0C|(?:\r?\n|^)\s*[-=_]{3,}\s*(?:PAGE|Page|page break|Page Break)[\s\d\w-]*[-=_]{3,}\s*(?:\r?\n|$))/i;
3030
+ rawPages = fullText.split(pageSplitRegex).map((p) => p.trim()).filter((p) => p.length > 0);
3031
+ }
2938
3032
  const totalPages = Math.max(1, rawPages.length);
2939
3033
  let currentPage = 1;
2940
3034
  const container = document.createElement("div");
2941
3035
  container.style.width = "100%";
2942
3036
  container.style.height = "100%";
2943
3037
  container.style.overflow = "auto";
2944
- container.style.backgroundColor = "#1e1e1e";
2945
- container.style.color = "#d4d4d4";
2946
- container.style.padding = "16px";
2947
- container.style.boxSizing = "border-box";
2948
3038
  let fontSize = 13;
2949
3039
  let rotation = 0;
3040
+ let zoomLevel = 1;
2950
3041
  const pre = document.createElement("pre");
2951
- pre.style.margin = "0";
2952
- pre.style.fontFamily = "Consolas, Menlo, Monaco, monospace";
2953
- pre.style.fontSize = `${fontSize}px`;
2954
- pre.style.lineHeight = "1.5";
2955
- pre.style.whiteSpace = "pre-wrap";
2956
- pre.style.wordBreak = "break-all";
2957
3042
  const code = document.createElement("code");
3043
+ if (isTxt) {
3044
+ container.style.backgroundColor = "#f1f5f9";
3045
+ container.style.padding = "32px";
3046
+ container.style.boxSizing = "border-box";
3047
+ container.style.display = "flex";
3048
+ container.style.flexDirection = "column";
3049
+ container.style.alignItems = "center";
3050
+ pre.style.margin = "0";
3051
+ pre.style.fontFamily = "Consolas, 'Courier New', monospace";
3052
+ pre.style.fontSize = "13px";
3053
+ pre.style.color = "#1e293b";
3054
+ pre.style.lineHeight = "1.5";
3055
+ pre.style.whiteSpace = "pre-wrap";
3056
+ pre.style.wordBreak = "break-word";
3057
+ pre.style.backgroundColor = "#ffffff";
3058
+ pre.style.width = "816px";
3059
+ pre.style.minHeight = "1056px";
3060
+ pre.style.padding = "72px 56px";
3061
+ pre.style.boxSizing = "border-box";
3062
+ pre.style.boxShadow = "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)";
3063
+ pre.style.borderRadius = "4px";
3064
+ pre.style.transformOrigin = "top center";
3065
+ } else {
3066
+ container.style.backgroundColor = "#1e1e1e";
3067
+ container.style.color = "#d4d4d4";
3068
+ container.style.padding = "16px";
3069
+ container.style.boxSizing = "border-box";
3070
+ pre.style.margin = "0";
3071
+ pre.style.fontFamily = "Consolas, Menlo, Monaco, monospace";
3072
+ pre.style.fontSize = `${fontSize}px`;
3073
+ pre.style.lineHeight = "1.5";
3074
+ pre.style.whiteSpace = "pre-wrap";
3075
+ pre.style.wordBreak = "break-all";
3076
+ pre.style.transformOrigin = "top left";
3077
+ }
2958
3078
  const ext = (ctx.metadata.extension || "").replace(".", "");
2959
3079
  const renderCodePage = (text) => {
2960
- try {
2961
- if (ext && hljs.getLanguage(ext)) {
2962
- code.innerHTML = hljs.highlight(text, { language: ext }).value;
2963
- } else {
2964
- code.innerHTML = hljs.highlightAuto(text).value;
2965
- }
2966
- } catch {
3080
+ if (isTxt) {
2967
3081
  code.textContent = text;
3082
+ } else {
3083
+ try {
3084
+ if (ext && hljs.getLanguage(ext)) {
3085
+ code.innerHTML = hljs.highlight(text, { language: ext }).value;
3086
+ } else {
3087
+ code.innerHTML = hljs.highlightAuto(text).value;
3088
+ }
3089
+ } catch {
3090
+ code.textContent = text;
3091
+ }
2968
3092
  }
2969
3093
  };
2970
- renderCodePage(rawPages[0] || fullText);
3094
+ renderCodePage(rawPages[0] || (isTxt ? "" : fullText));
2971
3095
  pre.appendChild(code);
2972
3096
  container.appendChild(pre);
2973
3097
  let indicator = null;
@@ -2976,15 +3100,22 @@ var CodePlugin = class {
2976
3100
  indicator.className = "fp-code-page-indicator";
2977
3101
  indicator.style.position = "sticky";
2978
3102
  indicator.style.bottom = "16px";
2979
- indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
2980
- indicator.style.backdropFilter = "blur(8px)";
2981
- indicator.style.color = "#f8fafc";
3103
+ if (isTxt) {
3104
+ indicator.style.backgroundColor = "rgba(255, 255, 255, 0.9)";
3105
+ indicator.style.color = "#334155";
3106
+ indicator.style.border = "1px solid #e2e8f0";
3107
+ indicator.style.boxShadow = "0 1px 3px rgba(0,0,0,0.1)";
3108
+ } else {
3109
+ indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
3110
+ indicator.style.color = "#f8fafc";
3111
+ indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
3112
+ indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
3113
+ indicator.style.backdropFilter = "blur(8px)";
3114
+ }
2982
3115
  indicator.style.fontSize = "12px";
2983
3116
  indicator.style.fontWeight = "600";
2984
3117
  indicator.style.padding = "5px 14px";
2985
3118
  indicator.style.borderRadius = "20px";
2986
- indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
2987
- indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
2988
3119
  indicator.style.zIndex = "10";
2989
3120
  indicator.style.userSelect = "none";
2990
3121
  indicator.style.pointerEvents = "none";
@@ -2995,7 +3126,7 @@ var CodePlugin = class {
2995
3126
  }
2996
3127
  const showPage = (pageNum) => {
2997
3128
  currentPage = Math.max(1, Math.min(totalPages, pageNum));
2998
- renderCodePage(rawPages[currentPage - 1] || fullText);
3129
+ renderCodePage(rawPages[currentPage - 1] || (isTxt ? "" : fullText));
2999
3130
  if (indicator) {
3000
3131
  indicator.textContent = `Page ${currentPage} of ${totalPages}`;
3001
3132
  }
@@ -3011,39 +3142,57 @@ var CodePlugin = class {
3011
3142
  ctx.container.innerHTML = "";
3012
3143
  };
3013
3144
  ctx.signal.addEventListener("abort", cleanup);
3145
+ const updateTransform = () => {
3146
+ if (isTxt) {
3147
+ pre.style.transform = `scale(${zoomLevel}) rotate(${rotation}deg)`;
3148
+ } else {
3149
+ pre.style.transform = `rotate(${rotation}deg)`;
3150
+ pre.style.fontSize = `${fontSize}px`;
3151
+ }
3152
+ };
3014
3153
  return {
3015
3154
  destroy: cleanup,
3016
3155
  getPageCount: () => totalPages,
3017
3156
  getCurrentPage: () => currentPage,
3018
3157
  goToPage: (page) => showPage(page),
3019
3158
  zoomIn: () => {
3020
- fontSize = Math.min(32, fontSize + 2);
3021
- pre.style.fontSize = `${fontSize}px`;
3159
+ if (isTxt) {
3160
+ zoomLevel = Math.min(3, zoomLevel + 0.1);
3161
+ } else {
3162
+ fontSize = Math.min(32, fontSize + 2);
3163
+ }
3164
+ updateTransform();
3022
3165
  },
3023
3166
  zoomOut: () => {
3024
- fontSize = Math.max(8, fontSize - 2);
3025
- pre.style.fontSize = `${fontSize}px`;
3167
+ if (isTxt) {
3168
+ zoomLevel = Math.max(0.1, zoomLevel - 0.1);
3169
+ } else {
3170
+ fontSize = Math.max(8, fontSize - 2);
3171
+ }
3172
+ updateTransform();
3026
3173
  },
3027
- getZoom: () => fontSize / 13,
3174
+ getZoom: () => isTxt ? zoomLevel : fontSize / 13,
3028
3175
  setZoom: (level) => {
3029
- fontSize = Math.round(13 * level);
3030
- pre.style.fontSize = `${fontSize}px`;
3176
+ if (isTxt) {
3177
+ zoomLevel = level;
3178
+ } else {
3179
+ fontSize = Math.round(13 * level);
3180
+ }
3181
+ updateTransform();
3031
3182
  },
3032
3183
  fitToPage: () => {
3033
3184
  fontSize = 13;
3034
3185
  rotation = 0;
3035
- pre.style.fontSize = "13px";
3036
- pre.style.transform = "none";
3186
+ zoomLevel = 1;
3187
+ updateTransform();
3037
3188
  },
3038
3189
  rotateCW: () => {
3039
3190
  rotation = (rotation + 90) % 360;
3040
- pre.style.transform = `rotate(${rotation}deg)`;
3041
- pre.style.transformOrigin = "top left";
3191
+ updateTransform();
3042
3192
  },
3043
3193
  rotateCCW: () => {
3044
3194
  rotation = (rotation - 90 + 360) % 360;
3045
- pre.style.transform = `rotate(${rotation}deg)`;
3046
- pre.style.transformOrigin = "top left";
3195
+ updateTransform();
3047
3196
  },
3048
3197
  download: () => {
3049
3198
  const mimeType = ctx.metadata.mimeType || "text/plain";
@@ -3342,7 +3491,7 @@ var MarkdownPlugin = class {
3342
3491
  gfm: true,
3343
3492
  breaks: true
3344
3493
  });
3345
- const cleanHtml = DOMPurify2.sanitize(rawHtml, {
3494
+ const cleanHtml = DOMPurify6.sanitize(rawHtml, {
3346
3495
  USE_PROFILES: { html: true }
3347
3496
  });
3348
3497
  const wrapper = document.createElement("div");
@@ -3978,6 +4127,14 @@ var RtfPlugin = class {
3978
4127
  group: "actions",
3979
4128
  execute: () => instance.download?.()
3980
4129
  },
4130
+ {
4131
+ id: "copy",
4132
+ icon: "copy",
4133
+ label: "Copy Text",
4134
+ type: "button",
4135
+ group: "actions",
4136
+ execute: () => instance.copy?.()
4137
+ },
3981
4138
  {
3982
4139
  id: "print",
3983
4140
  icon: "print",
@@ -4039,14 +4196,31 @@ var RtfPlugin = class {
4039
4196
  } catch (err) {
4040
4197
  console.warn("[RtfPlugin] RTF render error, fallback text:", err);
4041
4198
  const text = new TextDecoder("latin1").decode(ctx.buffer);
4042
- const clean = text.replace(/\\par[d]?/g, "\n").replace(/\\[a-zA-Z0-9\-]+/g, "").replace(/[{}]/g, "");
4043
- const pre = document.createElement("pre");
4044
- pre.style.whiteSpace = "pre-wrap";
4045
- pre.style.fontFamily = "serif";
4046
- pre.style.color = "#333";
4047
- pre.textContent = clean;
4048
- wrapper.appendChild(pre);
4049
- pageElements = [pre];
4199
+ const rawPages = text.split(/\\page\b/).map((segment) => {
4200
+ return segment.replace(/\\par[d]?/g, "\n").replace(/\\[a-zA-Z0-9\-]+/g, "").replace(/[{}]/g, "").trim();
4201
+ }).filter((p) => p.length > 0);
4202
+ const pages = rawPages.length > 0 ? rawPages : [text.replace(/\\par[d]?/g, "\n").replace(/\\[a-zA-Z0-9\-]+/g, "").replace(/[{}]/g, "").trim()];
4203
+ for (let i = 0; i < pages.length; i++) {
4204
+ const pageCard = document.createElement("div");
4205
+ pageCard.className = "fp-rtf-page-card";
4206
+ pageCard.style.backgroundColor = "#ffffff";
4207
+ pageCard.style.boxShadow = "0 2px 12px rgba(0,0,0,0.08)";
4208
+ pageCard.style.borderRadius = "4px";
4209
+ pageCard.style.padding = "72px 56px";
4210
+ pageCard.style.width = "816px";
4211
+ pageCard.style.minHeight = "1056px";
4212
+ pageCard.style.maxWidth = "100%";
4213
+ pageCard.style.boxSizing = "border-box";
4214
+ pageCard.style.fontFamily = "serif";
4215
+ pageCard.style.fontSize = "12pt";
4216
+ pageCard.style.lineHeight = "1.6";
4217
+ pageCard.style.color = "#1e293b";
4218
+ pageCard.style.whiteSpace = "pre-wrap";
4219
+ pageCard.style.display = i === 0 ? "block" : "none";
4220
+ pageCard.textContent = pages[i];
4221
+ wrapper.appendChild(pageCard);
4222
+ pageElements.push(pageCard);
4223
+ }
4050
4224
  }
4051
4225
  const totalPages = Math.max(1, pageElements.length);
4052
4226
  let currentPage = 1;
@@ -4134,6 +4308,10 @@ var RtfPlugin = class {
4134
4308
  a.click();
4135
4309
  URL.revokeObjectURL(url);
4136
4310
  },
4311
+ copy: () => {
4312
+ const text = wrapper.textContent || "";
4313
+ navigator.clipboard?.writeText(text);
4314
+ },
4137
4315
  print: () => {
4138
4316
  window.print();
4139
4317
  }
@@ -4208,7 +4386,7 @@ var HtmlPreviewPlugin = class {
4208
4386
  }
4209
4387
  async render(ctx) {
4210
4388
  const rawHtml = new TextDecoder("utf-8").decode(ctx.buffer);
4211
- const sanitized = DOMPurify2.sanitize(rawHtml, {
4389
+ const sanitized = DOMPurify6.sanitize(rawHtml, {
4212
4390
  WHOLE_DOCUMENT: true,
4213
4391
  ADD_TAGS: ["style", "link"],
4214
4392
  ADD_ATTR: ["target", "rel"]
@@ -4474,14 +4652,97 @@ var OpenDocumentPlugin = class {
4474
4652
  wrapper.textContent = contentDoc.documentElement.textContent || "Formula content";
4475
4653
  }
4476
4654
  } else {
4477
- wrapper.style.maxWidth = "850px";
4478
- wrapper.style.padding = "48px";
4655
+ wrapper.style.maxWidth = "none";
4656
+ wrapper.style.padding = "0";
4479
4657
  wrapper.style.minHeight = "100%";
4658
+ wrapper.style.backgroundColor = "transparent";
4659
+ wrapper.style.boxShadow = "none";
4660
+ wrapper.style.position = "relative";
4661
+ const dims = this.getPageDimensions(stylesDoc, contentDoc);
4480
4662
  const bodyHtml = this.renderOdfBody(contentDoc, styleMap, imageUrls);
4481
- wrapper.innerHTML = DOMPurify2.sanitize(bodyHtml, {
4482
- ADD_TAGS: ["math", "semantics", "mrow", "mi", "mo", "mn", "msup", "msub"],
4663
+ const tempDiv = document.createElement("div");
4664
+ tempDiv.style.width = `${dims.width - dims.marginLeft - dims.marginRight}px`;
4665
+ tempDiv.style.position = "absolute";
4666
+ tempDiv.style.visibility = "hidden";
4667
+ tempDiv.innerHTML = DOMPurify6.sanitize(bodyHtml, {
4668
+ ADD_TAGS: ["math", "semantics", "mrow", "mi", "mo", "mn", "msup", "msub", "hr"],
4483
4669
  ADD_ATTR: ["style", "colspan", "rowspan"]
4484
4670
  });
4671
+ document.body.appendChild(tempDiv);
4672
+ const contentHeight = dims.height - dims.marginTop - dims.marginBottom;
4673
+ const pageElements = [[]];
4674
+ let currentHeight = 0;
4675
+ let currentPageIdx = 0;
4676
+ Array.from(tempDiv.children).forEach((child) => {
4677
+ const el = child;
4678
+ const style = el.getAttribute("style") || "";
4679
+ const isBreakBefore = style.includes("page-break-before: always");
4680
+ const isBreakAfter = style.includes("page-break-after: always");
4681
+ const isSoftBreak = el.classList.contains("odf-page-break");
4682
+ if (isBreakBefore) {
4683
+ if (pageElements[currentPageIdx].length > 0) {
4684
+ currentPageIdx++;
4685
+ pageElements.push([]);
4686
+ currentHeight = 0;
4687
+ }
4688
+ }
4689
+ const h2 = el.offsetHeight || 0;
4690
+ if (currentHeight + h2 > contentHeight && pageElements[currentPageIdx].length > 0 && !isSoftBreak) {
4691
+ currentPageIdx++;
4692
+ pageElements.push([]);
4693
+ currentHeight = 0;
4694
+ }
4695
+ if (!isSoftBreak) {
4696
+ pageElements[currentPageIdx].push(el.cloneNode(true));
4697
+ currentHeight += h2;
4698
+ }
4699
+ if (isBreakAfter || isSoftBreak) {
4700
+ currentPageIdx++;
4701
+ pageElements.push([]);
4702
+ currentHeight = 0;
4703
+ }
4704
+ });
4705
+ document.body.removeChild(tempDiv);
4706
+ if (pageElements.length > 1 && pageElements[pageElements.length - 1].length === 0) {
4707
+ pageElements.pop();
4708
+ }
4709
+ totalPages = Math.max(1, pageElements.length);
4710
+ pageElements.forEach((elements, idx) => {
4711
+ const page = document.createElement("div");
4712
+ page.className = `fp-odt-page fp-odt-page-${idx + 1}`;
4713
+ page.style.width = `${dims.width}px`;
4714
+ page.style.minHeight = `${dims.height}px`;
4715
+ page.style.padding = `${dims.marginTop}px ${dims.marginRight}px ${dims.marginBottom}px ${dims.marginLeft}px`;
4716
+ page.style.margin = "0 auto";
4717
+ page.style.backgroundColor = "#ffffff";
4718
+ page.style.boxShadow = "0 2px 10px rgba(0,0,0,0.08)";
4719
+ page.style.borderRadius = "4px";
4720
+ page.style.boxSizing = "border-box";
4721
+ page.style.display = idx === 0 ? "block" : "none";
4722
+ page.style.position = "absolute";
4723
+ page.style.top = "0";
4724
+ page.style.left = "50%";
4725
+ page.style.transform = "translateX(-50%)";
4726
+ elements.forEach((el) => page.appendChild(el));
4727
+ wrapper.appendChild(page);
4728
+ slides.push(page);
4729
+ });
4730
+ const pageIndicator = document.createElement("div");
4731
+ pageIndicator.className = "fp-odt-page-indicator";
4732
+ pageIndicator.style.position = "sticky";
4733
+ pageIndicator.style.bottom = "16px";
4734
+ pageIndicator.style.left = "50%";
4735
+ pageIndicator.style.transform = "translateX(-50%)";
4736
+ pageIndicator.style.backgroundColor = "rgba(0, 0, 0, 0.6)";
4737
+ pageIndicator.style.color = "#fff";
4738
+ pageIndicator.style.padding = "6px 12px";
4739
+ pageIndicator.style.borderRadius = "16px";
4740
+ pageIndicator.style.fontSize = "12px";
4741
+ pageIndicator.style.zIndex = "100";
4742
+ pageIndicator.style.display = "inline-block";
4743
+ pageIndicator.style.width = "fit-content";
4744
+ pageIndicator.textContent = `Page 1 of ${totalPages}`;
4745
+ container.appendChild(pageIndicator);
4485
4746
  }
4486
4747
  const cleanup = () => {
4487
4748
  imageUrls.forEach((url) => URL.revokeObjectURL(url));
@@ -4490,11 +4751,15 @@ var OpenDocumentPlugin = class {
4490
4751
  };
4491
4752
  ctx.signal.addEventListener("abort", cleanup);
4492
4753
  const goToPage = (page) => {
4493
- if (!isPresentation || page < 1 || page > totalPages) return;
4754
+ if (page < 1 || page > totalPages) return;
4494
4755
  currentPage = page;
4495
4756
  slides.forEach((s, idx) => {
4496
4757
  s.style.display = idx === page - 1 ? "block" : "none";
4497
4758
  });
4759
+ const indicator = container.querySelector(".fp-odt-page-indicator");
4760
+ if (indicator) {
4761
+ indicator.textContent = `Page ${currentPage} of ${totalPages}`;
4762
+ }
4498
4763
  ctx.emit("page-change", { page: currentPage, total: totalPages });
4499
4764
  };
4500
4765
  return {
@@ -4563,6 +4828,43 @@ var OpenDocumentPlugin = class {
4563
4828
  }
4564
4829
  };
4565
4830
  }
4831
+ getPageDimensions(stylesDoc, contentDoc) {
4832
+ let width = 850;
4833
+ let height = 1123;
4834
+ let marginTop = 48;
4835
+ let marginBottom = 48;
4836
+ let marginLeft = 48;
4837
+ let marginRight = 48;
4838
+ const parseUnit = (val) => {
4839
+ if (!val) return null;
4840
+ if (val.endsWith("cm")) return parseFloat(val) * 37.8;
4841
+ if (val.endsWith("mm")) return parseFloat(val) * 3.78;
4842
+ if (val.endsWith("in")) return parseFloat(val) * 96;
4843
+ if (val.endsWith("pt")) return parseFloat(val) * 1.33;
4844
+ if (val.endsWith("px")) return parseFloat(val);
4845
+ return parseFloat(val);
4846
+ };
4847
+ const docs = [stylesDoc, contentDoc].filter(Boolean);
4848
+ for (const doc of docs) {
4849
+ const pageLayout = doc.querySelector("page-layout-properties, [page-width]");
4850
+ if (pageLayout) {
4851
+ const w = parseUnit(pageLayout.getAttribute("fo:page-width") || pageLayout.getAttribute("page-width"));
4852
+ const h2 = parseUnit(pageLayout.getAttribute("fo:page-height") || pageLayout.getAttribute("page-height"));
4853
+ const mt = parseUnit(pageLayout.getAttribute("fo:margin-top") || pageLayout.getAttribute("margin-top"));
4854
+ const mb = parseUnit(pageLayout.getAttribute("fo:margin-bottom") || pageLayout.getAttribute("margin-bottom"));
4855
+ const ml = parseUnit(pageLayout.getAttribute("fo:margin-left") || pageLayout.getAttribute("margin-left"));
4856
+ const mr = parseUnit(pageLayout.getAttribute("fo:margin-right") || pageLayout.getAttribute("margin-right"));
4857
+ if (w !== null) width = w;
4858
+ if (h2 !== null) height = h2;
4859
+ if (mt !== null) marginTop = mt;
4860
+ if (mb !== null) marginBottom = mb;
4861
+ if (ml !== null) marginLeft = ml;
4862
+ if (mr !== null) marginRight = mr;
4863
+ break;
4864
+ }
4865
+ }
4866
+ return { width, height, marginTop, marginBottom, marginLeft, marginRight };
4867
+ }
4566
4868
  extractStyles(stylesDoc, contentDoc) {
4567
4869
  const map = /* @__PURE__ */ new Map();
4568
4870
  const styleNodes = [];
@@ -4585,14 +4887,21 @@ var OpenDocumentPlugin = class {
4585
4887
  if (color) css += `color: ${color}; `;
4586
4888
  if (size) css += `font-size: ${size}; `;
4587
4889
  }
4588
- const paraProp = node.querySelector("paragraph-properties, [text-align]");
4890
+ let paraProp = node.querySelector("paragraph-properties, [text-align]");
4891
+ if (!paraProp) {
4892
+ paraProp = Array.from(node.children).find((c) => c.tagName.includes("paragraph-properties")) || null;
4893
+ }
4589
4894
  if (paraProp) {
4590
4895
  const align = paraProp.getAttribute("fo:text-align") || paraProp.getAttribute("text-align");
4591
4896
  const mt = paraProp.getAttribute("fo:margin-top") || paraProp.getAttribute("margin-top");
4592
4897
  const mb = paraProp.getAttribute("fo:margin-bottom") || paraProp.getAttribute("margin-bottom");
4898
+ const breakBefore = paraProp.getAttribute("fo:break-before") || paraProp.getAttribute("break-before");
4899
+ const breakAfter = paraProp.getAttribute("fo:break-after") || paraProp.getAttribute("break-after");
4593
4900
  if (align) css += `text-align: ${align}; `;
4594
4901
  if (mt) css += `margin-top: ${mt}; `;
4595
4902
  if (mb) css += `margin-bottom: ${mb}; `;
4903
+ if (breakBefore === "page") css += "page-break-before: always; ";
4904
+ if (breakAfter === "page") css += "page-break-after: always; ";
4596
4905
  }
4597
4906
  if (css) map.set(name, css);
4598
4907
  }
@@ -4677,6 +4986,8 @@ var OpenDocumentPlugin = class {
4677
4986
  result += "&emsp;";
4678
4987
  } else if (tag === "line-break") {
4679
4988
  result += "<br/>";
4989
+ } else if (tag === "soft-page-break") {
4990
+ result += '<hr class="odf-page-break" style="page-break-after: always; border: none; margin: 0; padding: 0; height: 0;" />';
4680
4991
  } else {
4681
4992
  result += el.textContent || "";
4682
4993
  }
@@ -4837,20 +5148,9 @@ var DocPlugin = class {
4837
5148
  container.style.overflow = "auto";
4838
5149
  container.style.padding = "32px 16px";
4839
5150
  container.style.backgroundColor = "#f1f5f9";
4840
- const wrapper = document.createElement("div");
4841
- wrapper.className = "fp-doc-wrapper";
4842
- wrapper.style.maxWidth = "850px";
4843
- wrapper.style.margin = "0 auto";
4844
- wrapper.style.backgroundColor = "#ffffff";
4845
- wrapper.style.boxShadow = "0 2px 12px rgba(0,0,0,0.08)";
4846
- wrapper.style.borderRadius = "4px";
4847
- wrapper.style.padding = "56px 48px";
4848
- wrapper.style.minHeight = "100%";
4849
- wrapper.style.transformOrigin = "top center";
4850
- wrapper.style.transition = "transform 0.2s ease";
4851
- wrapper.style.fontFamily = 'Calibri, "Segoe UI", Arial, sans-serif';
4852
- wrapper.style.color = "#1e293b";
4853
- container.appendChild(wrapper);
5151
+ container.style.display = "flex";
5152
+ container.style.justifyContent = "center";
5153
+ container.style.alignItems = "flex-start";
4854
5154
  ctx.container.appendChild(container);
4855
5155
  let scale = 1;
4856
5156
  let extractedRawText = "";
@@ -4877,21 +5177,27 @@ var DocPlugin = class {
4877
5177
  const rawPages = this.splitIntoPages(extractedRawText);
4878
5178
  const totalPages = Math.max(1, rawPages.length);
4879
5179
  let currentPage = 1;
4880
- wrapper.innerHTML = "";
4881
5180
  const pageCards = [];
4882
5181
  for (let i = 0; i < totalPages; i++) {
4883
5182
  const pageCard = document.createElement("div");
4884
5183
  pageCard.className = "fp-doc-page-card";
5184
+ pageCard.style.width = "816px";
5185
+ pageCard.style.height = "1056px";
4885
5186
  pageCard.style.backgroundColor = "#ffffff";
4886
5187
  pageCard.style.boxShadow = "0 2px 12px rgba(0,0,0,0.08)";
4887
5188
  pageCard.style.borderRadius = "4px";
4888
- pageCard.style.padding = "56px 48px";
4889
- pageCard.style.minHeight = "100%";
5189
+ pageCard.style.padding = "96px 72px";
5190
+ pageCard.style.boxSizing = "border-box";
5191
+ pageCard.style.overflow = "hidden";
4890
5192
  pageCard.style.display = i === 0 ? "block" : "none";
5193
+ pageCard.style.transformOrigin = "top center";
5194
+ pageCard.style.transition = "transform 0.2s ease";
5195
+ pageCard.style.fontFamily = 'Calibri, "Segoe UI", Arial, sans-serif';
5196
+ pageCard.style.color = "#1e293b";
4891
5197
  if (isFallback && i === 0) {
4892
5198
  pageCard.innerHTML = `
4893
5199
  <div style="border-bottom: 1px solid #e2e8f0; padding-bottom: 12px; margin-bottom: 24px;">
4894
- <h2 style="margin: 0 0 6px; font-size: 20px; color: #334155;">${DOMPurify2.sanitize(ctx.metadata.name || "Word Document (.doc)")}</h2>
5200
+ <h2 style="margin: 0 0 6px; font-size: 20px; color: #334155;">${DOMPurify6.sanitize(ctx.metadata.name || "Word Document (.doc)")}</h2>
4895
5201
  <span style="font-size: 12px; color: #64748b; background: #f1f5f9; padding: 2px 8px; border-radius: 4px;">Legacy Word 97-2003 Binary Preview</span>
4896
5202
  </div>
4897
5203
  ${this.formatDocToHtml(rawPages[i], ctx.metadata.name || "Document")}
@@ -4899,15 +5205,17 @@ var DocPlugin = class {
4899
5205
  } else {
4900
5206
  pageCard.innerHTML = this.formatDocToHtml(rawPages[i], ctx.metadata.name || "Document");
4901
5207
  }
4902
- wrapper.appendChild(pageCard);
5208
+ container.appendChild(pageCard);
4903
5209
  pageCards.push(pageCard);
4904
5210
  }
4905
5211
  let indicator = null;
4906
5212
  if (totalPages > 1) {
4907
5213
  indicator = document.createElement("div");
4908
5214
  indicator.className = "fp-doc-page-indicator";
4909
- indicator.style.position = "sticky";
5215
+ indicator.style.position = "fixed";
4910
5216
  indicator.style.bottom = "16px";
5217
+ indicator.style.left = "50%";
5218
+ indicator.style.transform = "translateX(-50%)";
4911
5219
  indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
4912
5220
  indicator.style.backdropFilter = "blur(8px)";
4913
5221
  indicator.style.color = "#f8fafc";
@@ -4921,17 +5229,25 @@ var DocPlugin = class {
4921
5229
  indicator.style.userSelect = "none";
4922
5230
  indicator.style.pointerEvents = "none";
4923
5231
  indicator.style.textAlign = "center";
4924
- indicator.style.width = "fit-content";
4925
- indicator.style.margin = "16px auto 0";
4926
5232
  container.appendChild(indicator);
4927
5233
  }
5234
+ let rotation = 0;
5235
+ const applyTransform = () => {
5236
+ const activeCard = pageCards[currentPage - 1];
5237
+ if (activeCard) {
5238
+ activeCard.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
5239
+ }
5240
+ };
4928
5241
  const showPage = (pageNum) => {
4929
5242
  currentPage = Math.max(1, Math.min(totalPages, pageNum));
4930
- if (totalPages > 1) {
4931
- pageCards.forEach((card, idx) => {
4932
- card.style.display = idx + 1 === currentPage ? "block" : "none";
4933
- });
4934
- }
5243
+ pageCards.forEach((card, idx) => {
5244
+ if (idx + 1 === currentPage) {
5245
+ card.style.display = "block";
5246
+ card.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
5247
+ } else {
5248
+ card.style.display = "none";
5249
+ }
5250
+ });
4935
5251
  if (indicator) {
4936
5252
  indicator.textContent = `Page ${currentPage} of ${totalPages}`;
4937
5253
  }
@@ -4940,19 +5256,13 @@ var DocPlugin = class {
4940
5256
  if (totalPages > 1) {
4941
5257
  showPage(1);
4942
5258
  }
4943
- let rotation = 0;
4944
5259
  const calculateFitScale = () => {
4945
- const activeCard = pageCards[currentPage - 1] || wrapper;
4946
- const elW = activeCard.offsetWidth || 850;
4947
- const elH = activeCard.offsetHeight || 1e3;
5260
+ const elW = 816;
5261
+ const elH = 1056;
4948
5262
  const availW = Math.max(200, ctx.container.clientWidth - 48);
4949
5263
  const availH = Math.max(200, ctx.container.clientHeight - 80);
4950
5264
  return Math.min(1.1, Math.min(availW / elW, availH / elH));
4951
5265
  };
4952
- const applyTransform = () => {
4953
- wrapper.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
4954
- wrapper.style.transformOrigin = "top center";
4955
- };
4956
5266
  setTimeout(() => {
4957
5267
  scale = calculateFitScale();
4958
5268
  applyTransform();
@@ -5110,8 +5420,28 @@ var DocPlugin = class {
5110
5420
  }
5111
5421
  splitIntoPages(text) {
5112
5422
  if (!text) return [""];
5113
- 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);
5114
- return parts.length > 0 ? parts : [text];
5423
+ 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);
5424
+ if (explicitParts.length === 0) explicitParts.push(text);
5425
+ const maxLinesPerPage = 48;
5426
+ const finalPages = [];
5427
+ for (const part of explicitParts) {
5428
+ const lines = part.split(/\r?\n/);
5429
+ let currentLines = [];
5430
+ let count = 0;
5431
+ for (const line of lines) {
5432
+ currentLines.push(line);
5433
+ count++;
5434
+ if (count >= maxLinesPerPage) {
5435
+ finalPages.push(currentLines.join("\n"));
5436
+ currentLines = [];
5437
+ count = 0;
5438
+ }
5439
+ }
5440
+ if (currentLines.length > 0) {
5441
+ finalPages.push(currentLines.join("\n"));
5442
+ }
5443
+ }
5444
+ return finalPages.length > 0 ? finalPages : [text];
5115
5445
  }
5116
5446
  /**
5117
5447
  * Format cleaned extracted text into attractive HTML paragraphs, headings, and lists
@@ -5120,17 +5450,65 @@ var DocPlugin = class {
5120
5450
  const lines = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\x0B/g, "\n").split("\n");
5121
5451
  let html = "";
5122
5452
  let inList = false;
5123
- for (const rawLine of lines) {
5124
- const line = rawLine.trim();
5453
+ let tableLines = [];
5454
+ const flushTable = () => {
5455
+ if (tableLines.length > 0) {
5456
+ html += '<table style="width: 100%; border-collapse: collapse; margin: 16px 0; font-size: 12px;">';
5457
+ for (const tLine of tableLines) {
5458
+ html += "<tr>";
5459
+ const cols = tLine.split(" ");
5460
+ for (const col of cols) {
5461
+ html += `<td style="border: 1px solid #cbd5e1; padding: 6px 8px;">${DOMPurify6.sanitize(col.trim())}</td>`;
5462
+ }
5463
+ html += "</tr>";
5464
+ }
5465
+ html += "</table>";
5466
+ tableLines = [];
5467
+ }
5468
+ };
5469
+ let i = 0;
5470
+ while (i < lines.length) {
5471
+ let line = lines[i];
5472
+ let tabCount = (line.match(/\t/g) || []).length;
5473
+ if (tabCount > 0) {
5474
+ let consecutiveTableLines = 1;
5475
+ let j = i + 1;
5476
+ while (j < lines.length) {
5477
+ const nextTabCount = (lines[j].match(/\t/g) || []).length;
5478
+ if (nextTabCount === tabCount) {
5479
+ consecutiveTableLines++;
5480
+ j++;
5481
+ } else {
5482
+ break;
5483
+ }
5484
+ }
5485
+ if (consecutiveTableLines >= 3) {
5486
+ if (inList) {
5487
+ html += "</ul>";
5488
+ inList = false;
5489
+ }
5490
+ tableLines = lines.slice(i, j);
5491
+ flushTable();
5492
+ i = j;
5493
+ continue;
5494
+ }
5495
+ }
5496
+ line = line.trim();
5125
5497
  if (!line) {
5126
5498
  if (inList) {
5127
5499
  html += "</ul>";
5128
5500
  inList = false;
5129
5501
  }
5502
+ html += '<div style="height: 1.15em;"></div>';
5503
+ i++;
5504
+ continue;
5505
+ }
5506
+ if (/^[\x00-\x1F\x7F-\x9F]+$/.test(line)) {
5507
+ i++;
5130
5508
  continue;
5131
5509
  }
5132
- if (/^[\x00-\x1F\x7F-\x9F]+$/.test(line)) continue;
5133
- if (line.includes("Normal.dot") || line.includes("Microsoft Word") || line.includes("Times New Roman") && line.length < 30) {
5510
+ if ((line.includes("Normal.dot") || line.includes("Microsoft Word") || line.includes("Times New Roman")) && line.length < 30) {
5511
+ i++;
5134
5512
  continue;
5135
5513
  }
5136
5514
  if (line.length < 60 && !line.endsWith(".") && (/^[A-Z0-9\s:_-]+$/.test(line) || line.startsWith("#"))) {
@@ -5138,24 +5516,26 @@ var DocPlugin = class {
5138
5516
  html += "</ul>";
5139
5517
  inList = false;
5140
5518
  }
5141
- 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>`;
5519
+ 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>`;
5142
5520
  } else if (line.startsWith("\u2022") || line.startsWith("-") || line.startsWith("*")) {
5143
5521
  if (!inList) {
5144
5522
  html += '<ul style="margin: 8px 0; padding-left: 24px;">';
5145
5523
  inList = true;
5146
5524
  }
5147
5525
  const bulletText = line.replace(/^[•\-\*]\s*/, "");
5148
- html += `<li style="margin: 4px 0; line-height: 1.6;">${DOMPurify2.sanitize(bulletText)}</li>`;
5526
+ html += `<li style="margin: 4px 0; line-height: 1.15; font-size: 12pt;">${DOMPurify6.sanitize(bulletText)}</li>`;
5149
5527
  } else {
5150
5528
  if (inList) {
5151
5529
  html += "</ul>";
5152
5530
  inList = false;
5153
5531
  }
5154
- html += `<p style="line-height: 1.7; margin: 10px 0; font-size: 14px; text-align: justify;">${DOMPurify2.sanitize(line)}</p>`;
5532
+ html += `<p style="line-height: 1.15; margin: 0; font-size: 12pt; text-align: justify;">${DOMPurify6.sanitize(line)}</p>`;
5155
5533
  }
5534
+ i++;
5156
5535
  }
5157
5536
  if (inList) html += "</ul>";
5158
- return html || `<p style="color: #64748b; font-style: italic;">(No readable text found in ${DOMPurify2.sanitize(filename)})</p>`;
5537
+ flushTable();
5538
+ return html || `<p style="color: #64748b; font-style: italic;">(No readable text found in ${DOMPurify6.sanitize(filename)})</p>`;
5159
5539
  }
5160
5540
  };
5161
5541
  function docPlugin() {
@@ -5333,7 +5713,7 @@ var PptPlugin = class {
5333
5713
  if (s.pictureUrl) {
5334
5714
  contentHtml = `
5335
5715
  <div style="flex: 1; display: flex; justify-content: center; align-items: center; padding: 12px; overflow: hidden;">
5336
- <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;" />
5716
+ <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;" />
5337
5717
  </div>
5338
5718
  `;
5339
5719
  } else if (s.tableColumns.length > 0) {
@@ -5343,7 +5723,7 @@ var PptPlugin = class {
5343
5723
  <table style="width: 100%; border-collapse: collapse; border: 1px solid #cbd5e1; border-radius: 6px; overflow: hidden; background: #ffffff;">
5344
5724
  <thead>
5345
5725
  <tr style="background: #e2e8f0; color: #1e293b; font-weight: 600; font-size: 14px;">
5346
- ${cols.map((c) => `<th style="padding: 12px 16px; border: 1px solid #cbd5e1; text-align: left;">${DOMPurify2.sanitize(c)}</th>`).join("")}
5726
+ ${cols.map((c) => `<th style="padding: 12px 16px; border: 1px solid #cbd5e1; text-align: left;">${DOMPurify6.sanitize(c)}</th>`).join("")}
5347
5727
  </tr>
5348
5728
  </thead>
5349
5729
  <tbody>
@@ -5359,7 +5739,7 @@ var PptPlugin = class {
5359
5739
  } else {
5360
5740
  const pTags = s.paragraphs.map((p) => {
5361
5741
  const lines = p.split(/[\r\n]+/).map((l) => l.trim()).filter(Boolean);
5362
- 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("");
5742
+ 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("");
5363
5743
  }).join("");
5364
5744
  contentHtml = `
5365
5745
  <div style="flex: 1; overflow: auto; padding: 4px 8px; display: flex; flex-direction: column; justify-content: flex-start;">
@@ -5372,11 +5752,11 @@ var PptPlugin = class {
5372
5752
  <!-- Header Banner matching PowerPoint design -->
5373
5753
  <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;">
5374
5754
  <h1 style="margin: 0; font-size: 26px; font-weight: 700; color: #1e293b; letter-spacing: -0.3px;">
5375
- ${DOMPurify2.sanitize(s.title)}
5755
+ ${DOMPurify6.sanitize(s.title)}
5376
5756
  </h1>
5377
5757
  ${s.subtitle ? `
5378
5758
  <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);">
5379
- ${DOMPurify2.sanitize(s.subtitle)}
5759
+ ${DOMPurify6.sanitize(s.subtitle)}
5380
5760
  </span>
5381
5761
  ` : `
5382
5762
  <span style="font-size: 12px; color: #365314; font-weight: 600;">