@files-preview-app/preview-file 1.2.4 → 1.2.5

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/angular.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { Component, ChangeDetectionStrategy, Input, Output, ViewChild, EventEmitter as EventEmitter$1 } from '@angular/core';
2
2
  import { CommonModule } from '@angular/common';
3
3
  import DOMPurify2 from 'dompurify';
4
+ import * as pdfjsLib from 'pdfjs-dist';
4
5
  import * as docx from 'docx-preview';
5
6
  import { unzipSync, strFromU8, unzip } from 'fflate';
6
7
  import * as XLSX from 'xlsx';
@@ -527,11 +528,21 @@ var ToolbarController = class {
527
528
  el;
528
529
  toolbarEl;
529
530
  actions = [];
531
+ pageInputEl = null;
532
+ pageLabelEl = null;
530
533
  constructor(container) {
531
534
  this.el = container;
532
535
  this.toolbarEl = createElement("div", { className: "fp-toolbar" });
533
536
  this.el.appendChild(this.toolbarEl);
534
537
  }
538
+ setPage(page, max) {
539
+ if (this.pageInputEl) {
540
+ this.pageInputEl.value = page.toString();
541
+ }
542
+ if (max !== void 0 && this.pageLabelEl) {
543
+ this.pageLabelEl.textContent = ` / ${max}`;
544
+ }
545
+ }
535
546
  update(actions) {
536
547
  this.actions = actions;
537
548
  this.render();
@@ -574,6 +585,7 @@ var ToolbarController = class {
574
585
  if (action.type === "separator") {
575
586
  groupEl.appendChild(createElement("div", { className: "fp-toolbar-separator" }));
576
587
  } else if (action.type === "page-nav") {
588
+ const max = action.max ?? 1;
577
589
  const prevBtn = this.createButton(
578
590
  "prev",
579
591
  ICON_PAGE_PREV,
@@ -592,7 +604,6 @@ var ToolbarController = class {
592
604
  "Next Page",
593
605
  () => {
594
606
  const cur = parseInt(input.value, 10) || 1;
595
- const max = action.max ?? 1;
596
607
  if (cur < max) {
597
608
  input.value = (cur + 1).toString();
598
609
  action.execute("next", cur + 1);
@@ -604,15 +615,18 @@ var ToolbarController = class {
604
615
  type: "number",
605
616
  value: (action.value ?? 1).toString(),
606
617
  min: "1",
607
- max: (action.max ?? 1).toString()
618
+ max: max.toString()
608
619
  });
609
620
  input.addEventListener("change", () => {
610
- const val = parseInt(input.value, 10);
611
- if (!isNaN(val)) {
612
- action.execute("go", val);
613
- }
621
+ let val = parseInt(input.value, 10);
622
+ if (isNaN(val)) val = 1;
623
+ val = Math.max(1, Math.min(max, val));
624
+ input.value = val.toString();
625
+ action.execute("go", val);
614
626
  });
615
- const label = createElement("span", { className: "fp-toolbar-label" }, ` / ${action.max ?? 1}`);
627
+ const label = createElement("span", { className: "fp-toolbar-label" }, ` / ${max}`);
628
+ this.pageInputEl = input;
629
+ this.pageLabelEl = label;
616
630
  groupEl.appendChild(prevBtn);
617
631
  groupEl.appendChild(input);
618
632
  groupEl.appendChild(label);
@@ -790,7 +804,13 @@ var FilePreviewViewer = class {
790
804
  buffer,
791
805
  options,
792
806
  signal,
793
- emit: (event, payload) => this.eventEmitter.emit(event, payload)
807
+ emit: (event, payload) => {
808
+ if (event === "page-change" && payload && typeof payload.page === "number") {
809
+ const total = payload.total ?? payload.totalPages;
810
+ this.toolbar?.setPage(payload.page, total);
811
+ }
812
+ this.eventEmitter.emit(event, payload);
813
+ }
794
814
  });
795
815
  this.activeInstance = instance;
796
816
  this.hideLoading();
@@ -824,6 +844,7 @@ var FilePreviewViewer = class {
824
844
  if (thumbnails && thumbnails.length > 0 && this.thumbnailPanel) {
825
845
  this.thumbnailPanel.update(thumbnails, (index) => {
826
846
  instance.goToPage?.(index + 1);
847
+ this.toolbar?.setPage(index + 1);
827
848
  });
828
849
  if (options.showThumbnails) {
829
850
  this.thumbnailPanel.show();
@@ -949,9 +970,13 @@ var FilePreviewViewer = class {
949
970
  if (e.key === "ArrowRight" || e.key === "PageDown") {
950
971
  const cur = this.activeInstance.getCurrentPage?.() ?? 1;
951
972
  this.activeInstance.goToPage?.(cur + 1);
973
+ const nextCur = this.activeInstance.getCurrentPage?.() ?? cur + 1;
974
+ this.toolbar?.setPage(nextCur);
952
975
  } else if (e.key === "ArrowLeft" || e.key === "PageUp") {
953
976
  const cur = this.activeInstance.getCurrentPage?.() ?? 1;
954
977
  this.activeInstance.goToPage?.(Math.max(1, cur - 1));
978
+ const prevCur = this.activeInstance.getCurrentPage?.() ?? Math.max(1, cur - 1);
979
+ this.toolbar?.setPage(prevCur);
955
980
  } else if (e.key === "+" || e.key === "=") {
956
981
  this.activeInstance.zoomIn?.();
957
982
  } else if (e.key === "-" || e.key === "_") {
@@ -1217,30 +1242,62 @@ var CfbfReader = class {
1217
1242
  return result.subarray(0, targetSize);
1218
1243
  }
1219
1244
  };
1220
-
1221
- // ../plugins/pdf/dist/index.js
1245
+ if (typeof window !== "undefined" && pdfjsLib.GlobalWorkerOptions) {
1246
+ if (!pdfjsLib.GlobalWorkerOptions.workerSrc) {
1247
+ pdfjsLib.GlobalWorkerOptions.workerSrc = `https://unpkg.com/pdfjs-dist@${pdfjsLib.version || "4.10.38"}/build/pdf.worker.min.mjs`;
1248
+ }
1249
+ }
1222
1250
  var PdfPlugin = class {
1223
1251
  id = "pdf";
1224
- name = "PDF Preview";
1252
+ name = "PDF Document Preview";
1225
1253
  extensions = [".pdf"];
1226
1254
  mimeTypes = ["application/pdf"];
1227
1255
  weight = 100;
1228
1256
  supports(file) {
1229
1257
  const ext = file.metadata.extension?.toLowerCase();
1230
1258
  const mime = file.metadata.mimeType?.toLowerCase();
1231
- return ext === ".pdf" || mime === "application/pdf";
1259
+ if (ext) return this.extensions.includes(ext);
1260
+ return this.mimeTypes.includes(mime || "");
1232
1261
  }
1233
1262
  getToolbarActions(instance) {
1263
+ const totalPages = instance.getPageCount?.() ?? 1;
1264
+ const curPage = instance.getCurrentPage?.() ?? 1;
1234
1265
  return [
1266
+ {
1267
+ id: "thumbnails",
1268
+ icon: "thumbnails",
1269
+ label: "Page Thumbnails",
1270
+ type: "button",
1271
+ group: "navigation",
1272
+ execute: () => instance.toggleThumbnails?.()
1273
+ },
1274
+ {
1275
+ id: "page-nav",
1276
+ icon: "",
1277
+ label: "Page Navigation",
1278
+ type: "page-nav",
1279
+ group: "navigation",
1280
+ value: curPage,
1281
+ max: totalPages,
1282
+ execute: (action, page) => {
1283
+ const cur = instance.getCurrentPage?.() ?? 1;
1284
+ const max = instance.getPageCount?.() ?? 1;
1285
+ if (action === "prev") {
1286
+ if (cur > 1) instance.goToPage?.(cur - 1);
1287
+ } else if (action === "next") {
1288
+ if (cur < max) instance.goToPage?.(cur + 1);
1289
+ } else if (typeof page === "number") {
1290
+ instance.goToPage?.(page);
1291
+ }
1292
+ }
1293
+ },
1235
1294
  {
1236
1295
  id: "zoom-out",
1237
1296
  icon: "zoom-out",
1238
1297
  label: "Zoom Out",
1239
1298
  type: "button",
1240
1299
  group: "zoom",
1241
- execute: () => {
1242
- instance.zoomOut?.();
1243
- }
1300
+ execute: () => instance.zoomOut?.()
1244
1301
  },
1245
1302
  {
1246
1303
  id: "zoom-in",
@@ -1248,9 +1305,7 @@ var PdfPlugin = class {
1248
1305
  label: "Zoom In",
1249
1306
  type: "button",
1250
1307
  group: "zoom",
1251
- execute: () => {
1252
- instance.zoomIn?.();
1253
- }
1308
+ execute: () => instance.zoomIn?.()
1254
1309
  },
1255
1310
  {
1256
1311
  id: "fit-page",
@@ -1258,39 +1313,23 @@ var PdfPlugin = class {
1258
1313
  label: "Fit to Page",
1259
1314
  type: "button",
1260
1315
  group: "zoom",
1261
- execute: () => {
1262
- instance.fitToPage?.();
1263
- }
1316
+ execute: () => instance.fitToPage?.()
1264
1317
  },
1265
1318
  {
1266
1319
  id: "rotate-cw",
1267
1320
  icon: "rotate-cw",
1268
- label: "Rotate",
1321
+ label: "Rotate Clockwise",
1269
1322
  type: "button",
1270
1323
  group: "view",
1271
- execute: () => {
1272
- instance.rotateCW?.();
1273
- }
1274
- },
1275
- {
1276
- id: "page-nav",
1277
- icon: "page-nav",
1278
- label: "Page Navigation",
1279
- type: "page-nav",
1280
- group: "navigation",
1281
- execute: (page) => {
1282
- if (typeof page === "number") instance.goToPage?.(page);
1283
- }
1324
+ execute: () => instance.rotateCW?.()
1284
1325
  },
1285
1326
  {
1286
1327
  id: "download",
1287
1328
  icon: "download",
1288
- label: "Download",
1329
+ label: "Download PDF",
1289
1330
  type: "button",
1290
1331
  group: "actions",
1291
- execute: () => {
1292
- instance.download?.();
1293
- }
1332
+ execute: () => instance.download?.()
1294
1333
  },
1295
1334
  {
1296
1335
  id: "print",
@@ -1298,99 +1337,213 @@ var PdfPlugin = class {
1298
1337
  label: "Print",
1299
1338
  type: "button",
1300
1339
  group: "actions",
1301
- execute: () => {
1302
- instance.print?.();
1303
- }
1340
+ execute: () => instance.print?.()
1304
1341
  }
1305
1342
  ];
1306
1343
  }
1307
1344
  async render(ctx) {
1308
- const blob = new Blob([ctx.buffer], { type: "application/pdf" });
1309
- const url = URL.createObjectURL(blob);
1310
- const wrapper = document.createElement("div");
1311
- wrapper.style.width = "100%";
1312
- wrapper.style.height = "100%";
1313
- wrapper.style.overflow = "hidden";
1314
- wrapper.style.display = "flex";
1315
- wrapper.style.justifyContent = "center";
1316
- wrapper.style.alignItems = "center";
1317
- const iframe = document.createElement("iframe");
1318
- iframe.src = url;
1319
- iframe.style.width = "100%";
1320
- iframe.style.height = "100%";
1321
- iframe.style.border = "none";
1322
- wrapper.appendChild(iframe);
1323
- ctx.container.appendChild(wrapper);
1345
+ const container = document.createElement("div");
1346
+ container.className = "fp-pdf-container";
1347
+ container.style.width = "100%";
1348
+ container.style.height = "100%";
1349
+ container.style.overflow = "auto";
1350
+ container.style.display = "flex";
1351
+ container.style.flexDirection = "column";
1352
+ container.style.alignItems = "center";
1353
+ container.style.padding = "24px 16px";
1354
+ container.style.backgroundColor = "#0f172a";
1355
+ container.style.boxSizing = "border-box";
1356
+ container.style.position = "relative";
1357
+ const pageCard = document.createElement("div");
1358
+ pageCard.className = "fp-pdf-page-card";
1359
+ pageCard.style.boxShadow = "0 10px 35px rgba(0, 0, 0, 0.5)";
1360
+ pageCard.style.backgroundColor = "#ffffff";
1361
+ pageCard.style.borderRadius = "4px";
1362
+ pageCard.style.overflow = "hidden";
1363
+ pageCard.style.lineHeight = "0";
1364
+ pageCard.style.transition = "transform 0.15s ease";
1365
+ pageCard.style.position = "relative";
1366
+ const canvas = document.createElement("canvas");
1367
+ pageCard.appendChild(canvas);
1368
+ container.appendChild(pageCard);
1369
+ const indicator = document.createElement("div");
1370
+ indicator.className = "fp-pdf-page-indicator";
1371
+ indicator.style.position = "sticky";
1372
+ indicator.style.bottom = "16px";
1373
+ indicator.style.marginTop = "16px";
1374
+ indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
1375
+ indicator.style.backdropFilter = "blur(8px)";
1376
+ indicator.style.color = "#f8fafc";
1377
+ indicator.style.fontSize = "12px";
1378
+ indicator.style.fontWeight = "600";
1379
+ indicator.style.padding = "5px 14px";
1380
+ indicator.style.borderRadius = "20px";
1381
+ indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
1382
+ indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
1383
+ indicator.style.zIndex = "10";
1384
+ indicator.style.userSelect = "none";
1385
+ indicator.style.pointerEvents = "none";
1386
+ container.appendChild(indicator);
1387
+ ctx.container.appendChild(container);
1388
+ const loadingTask = pdfjsLib.getDocument({
1389
+ data: new Uint8Array(ctx.buffer),
1390
+ cMapUrl: `https://unpkg.com/pdfjs-dist@${pdfjsLib.version || "4.10.38"}/cmaps/`,
1391
+ cMapPacked: true,
1392
+ standardFontDataUrl: `https://unpkg.com/pdfjs-dist@${pdfjsLib.version || "4.10.38"}/standard_fonts/`
1393
+ });
1394
+ const pdfDoc = await loadingTask.promise;
1395
+ const totalPages = Math.max(1, pdfDoc.numPages);
1324
1396
  let currentPage = 1;
1325
- let currentZoom = 1;
1397
+ let zoomScale = 1;
1326
1398
  let rotation = 0;
1399
+ let currentRenderTask = null;
1400
+ const renderPage = async (pageNum) => {
1401
+ if (currentRenderTask) {
1402
+ try {
1403
+ currentRenderTask.cancel();
1404
+ } catch {
1405
+ }
1406
+ currentRenderTask = null;
1407
+ }
1408
+ currentPage = Math.max(1, Math.min(totalPages, pageNum));
1409
+ indicator.textContent = `Page ${currentPage} of ${totalPages}`;
1410
+ ctx.emit("page-change", { page: currentPage, total: totalPages });
1411
+ const page = await pdfDoc.getPage(currentPage);
1412
+ const containerWidth = container.clientWidth || 900;
1413
+ const unscaledVp = page.getViewport({ scale: 1, rotation });
1414
+ const baseScale = Math.min((containerWidth - 64) / unscaledVp.width, 1.6);
1415
+ const effectiveScale = (baseScale > 0 ? baseScale : 1) * zoomScale;
1416
+ const pixelRatio = window.devicePixelRatio || 1;
1417
+ const viewport = page.getViewport({ scale: effectiveScale, rotation });
1418
+ canvas.width = Math.floor(viewport.width * pixelRatio);
1419
+ canvas.height = Math.floor(viewport.height * pixelRatio);
1420
+ canvas.style.width = `${Math.floor(viewport.width)}px`;
1421
+ canvas.style.height = `${Math.floor(viewport.height)}px`;
1422
+ const canvasCtx = canvas.getContext("2d");
1423
+ if (!canvasCtx) return;
1424
+ canvasCtx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
1425
+ currentRenderTask = page.render({
1426
+ canvasContext: canvasCtx,
1427
+ viewport
1428
+ });
1429
+ try {
1430
+ await currentRenderTask.promise;
1431
+ } catch (err) {
1432
+ if (err?.name !== "RenderingCancelledException") {
1433
+ console.warn("[PdfPlugin] Page render warning:", err);
1434
+ }
1435
+ } finally {
1436
+ currentRenderTask = null;
1437
+ }
1438
+ };
1439
+ await renderPage(1);
1327
1440
  const cleanup = () => {
1328
- URL.revokeObjectURL(url);
1329
- wrapper.remove();
1441
+ if (currentRenderTask) {
1442
+ try {
1443
+ currentRenderTask.cancel();
1444
+ } catch {
1445
+ }
1446
+ }
1447
+ try {
1448
+ pdfDoc.destroy();
1449
+ } catch {
1450
+ }
1451
+ container.remove();
1330
1452
  ctx.container.innerHTML = "";
1331
1453
  };
1332
1454
  ctx.signal.addEventListener("abort", cleanup);
1333
- return {
1455
+ const instance = {
1334
1456
  destroy: cleanup,
1335
1457
  zoomIn: () => {
1336
- currentZoom += 0.1;
1337
- iframe.style.transform = `scale(${currentZoom}) rotate(${rotation}deg)`;
1458
+ zoomScale = Math.min(3.5, zoomScale + 0.2);
1459
+ renderPage(currentPage);
1338
1460
  },
1339
1461
  zoomOut: () => {
1340
- currentZoom = Math.max(0.2, currentZoom - 0.1);
1341
- iframe.style.transform = `scale(${currentZoom}) rotate(${rotation}deg)`;
1462
+ zoomScale = Math.max(0.3, zoomScale - 0.2);
1463
+ renderPage(currentPage);
1342
1464
  },
1343
- getZoom: () => currentZoom,
1465
+ getZoom: () => zoomScale,
1344
1466
  setZoom: (level) => {
1345
- currentZoom = level;
1346
- iframe.style.transform = `scale(${currentZoom}) rotate(${rotation}deg)`;
1467
+ zoomScale = Math.max(0.3, Math.min(3.5, level));
1468
+ renderPage(currentPage);
1347
1469
  },
1348
1470
  fitToPage: () => {
1349
- currentZoom = 1;
1350
- iframe.style.transform = `scale(1) rotate(${rotation}deg)`;
1471
+ zoomScale = 1;
1472
+ renderPage(currentPage);
1351
1473
  },
1352
1474
  rotateCW: () => {
1353
1475
  rotation = (rotation + 90) % 360;
1354
- iframe.style.transform = `scale(${currentZoom}) rotate(${rotation}deg)`;
1476
+ renderPage(currentPage);
1355
1477
  },
1356
1478
  rotateCCW: () => {
1357
1479
  rotation = (rotation - 90 + 360) % 360;
1358
- iframe.style.transform = `scale(${currentZoom}) rotate(${rotation}deg)`;
1480
+ renderPage(currentPage);
1359
1481
  },
1360
1482
  getRotation: () => rotation,
1483
+ getPageCount: () => totalPages,
1484
+ getCurrentPage: () => currentPage,
1361
1485
  goToPage: (page) => {
1362
- currentPage = page;
1363
- iframe.src = `${url}#page=${page}`;
1486
+ renderPage(page);
1364
1487
  },
1365
- getCurrentPage: () => currentPage,
1366
1488
  download: () => {
1489
+ const blob = new Blob([ctx.buffer], { type: "application/pdf" });
1490
+ const url = URL.createObjectURL(blob);
1367
1491
  const a = document.createElement("a");
1368
1492
  a.href = url;
1369
1493
  a.download = ctx.metadata.name || "document.pdf";
1370
1494
  a.click();
1495
+ URL.revokeObjectURL(url);
1371
1496
  },
1372
1497
  print: () => {
1373
- iframe.contentWindow?.print();
1498
+ const blob = new Blob([ctx.buffer], { type: "application/pdf" });
1499
+ const url = URL.createObjectURL(blob);
1500
+ const hiddenIframe = document.createElement("iframe");
1501
+ hiddenIframe.style.position = "fixed";
1502
+ hiddenIframe.style.right = "0";
1503
+ hiddenIframe.style.bottom = "0";
1504
+ hiddenIframe.style.width = "0";
1505
+ hiddenIframe.style.height = "0";
1506
+ hiddenIframe.style.border = "0";
1507
+ document.body.appendChild(hiddenIframe);
1508
+ hiddenIframe.src = url;
1509
+ hiddenIframe.onload = () => {
1510
+ setTimeout(() => {
1511
+ hiddenIframe.contentWindow?.print();
1512
+ setTimeout(() => {
1513
+ hiddenIframe.remove();
1514
+ URL.revokeObjectURL(url);
1515
+ }, 1e3);
1516
+ }, 300);
1517
+ };
1374
1518
  },
1375
1519
  getThumbnails: async () => {
1376
- return [
1377
- {
1378
- index: 1,
1379
- label: "Page 1",
1380
- render: async (canvas) => {
1381
- const context = canvas.getContext("2d");
1382
- if (context) {
1383
- context.fillStyle = "#fff";
1384
- context.fillRect(0, 0, canvas.width, canvas.height);
1385
- context.fillStyle = "#333";
1386
- context.font = "12px sans-serif";
1387
- context.fillText("PDF Preview", 10, 20);
1520
+ const thumbnails = [];
1521
+ const count = Math.min(totalPages, 50);
1522
+ for (let i = 1; i <= count; i++) {
1523
+ thumbnails.push({
1524
+ index: i,
1525
+ label: `Page ${i}`,
1526
+ render: async (thumbCanvas) => {
1527
+ try {
1528
+ const p = await pdfDoc.getPage(i);
1529
+ const baseVp = p.getViewport({ scale: 1 });
1530
+ const thumbScale = (thumbCanvas.width || 120) / baseVp.width;
1531
+ const thumbVp = p.getViewport({ scale: thumbScale });
1532
+ thumbCanvas.height = Math.floor(thumbVp.height);
1533
+ const tCtx = thumbCanvas.getContext("2d");
1534
+ if (tCtx) {
1535
+ await p.render({ canvasContext: tCtx, viewport: thumbVp }).promise;
1536
+ }
1537
+ } catch (e) {
1538
+ console.warn(`[PdfPlugin] Error generating thumbnail for page ${i}:`, e);
1388
1539
  }
1389
1540
  }
1390
- }
1391
- ];
1541
+ });
1542
+ }
1543
+ return thumbnails;
1392
1544
  }
1393
1545
  };
1546
+ return instance;
1394
1547
  }
1395
1548
  };
1396
1549
  function pdfPlugin() {
@@ -1717,7 +1870,31 @@ var DocxPlugin = class {
1717
1870
  return this.mimeTypes.includes(mime || "");
1718
1871
  }
1719
1872
  getToolbarActions(instance) {
1720
- return [
1873
+ const totalPages = instance.getPageCount?.() ?? 1;
1874
+ const actions = [];
1875
+ if (totalPages > 1) {
1876
+ actions.push({
1877
+ id: "page-nav",
1878
+ icon: "",
1879
+ label: "Page Navigation",
1880
+ type: "page-nav",
1881
+ group: "navigation",
1882
+ value: instance.getCurrentPage?.() ?? 1,
1883
+ max: totalPages,
1884
+ execute: (action, page) => {
1885
+ const cur = instance.getCurrentPage?.() ?? 1;
1886
+ const max = instance.getPageCount?.() ?? 1;
1887
+ if (action === "prev") {
1888
+ if (cur > 1) instance.goToPage?.(cur - 1);
1889
+ } else if (action === "next") {
1890
+ if (cur < max) instance.goToPage?.(cur + 1);
1891
+ } else if (typeof page === "number") {
1892
+ instance.goToPage?.(page);
1893
+ }
1894
+ }
1895
+ });
1896
+ }
1897
+ actions.push(
1721
1898
  {
1722
1899
  id: "zoom-out",
1723
1900
  icon: "zoom-out",
@@ -1758,7 +1935,8 @@ var DocxPlugin = class {
1758
1935
  group: "actions",
1759
1936
  execute: () => instance.print?.()
1760
1937
  }
1761
- ];
1938
+ );
1939
+ return actions;
1762
1940
  }
1763
1941
  async render(ctx) {
1764
1942
  const wrapper = document.createElement("div");
@@ -1834,7 +2012,51 @@ var DocxPlugin = class {
1834
2012
  }
1835
2013
  }
1836
2014
  }
2015
+ const sections = wrapper.querySelectorAll("section.docx");
2016
+ const cards = wrapper.querySelectorAll(".fp-docx-page-card");
2017
+ const pageElements = sections.length > 0 ? sections : cards;
2018
+ const totalPages = Math.max(1, pageElements.length);
2019
+ let currentPage = 1;
2020
+ let indicator = null;
2021
+ if (totalPages > 1) {
2022
+ indicator = document.createElement("div");
2023
+ indicator.className = "fp-docx-page-indicator";
2024
+ indicator.style.position = "sticky";
2025
+ indicator.style.bottom = "16px";
2026
+ indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
2027
+ indicator.style.backdropFilter = "blur(8px)";
2028
+ indicator.style.color = "#f8fafc";
2029
+ indicator.style.fontSize = "12px";
2030
+ indicator.style.fontWeight = "600";
2031
+ indicator.style.padding = "5px 14px";
2032
+ indicator.style.borderRadius = "20px";
2033
+ indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
2034
+ indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
2035
+ indicator.style.zIndex = "10";
2036
+ indicator.style.userSelect = "none";
2037
+ indicator.style.pointerEvents = "none";
2038
+ indicator.style.textAlign = "center";
2039
+ indicator.style.width = "fit-content";
2040
+ indicator.style.margin = "16px auto 0";
2041
+ ctx.container.appendChild(indicator);
2042
+ }
2043
+ const showPage = (pageNum) => {
2044
+ currentPage = Math.max(1, Math.min(totalPages, pageNum));
2045
+ if (pageElements.length > 1) {
2046
+ pageElements.forEach((sec, idx) => {
2047
+ sec.style.display = idx + 1 === currentPage ? "block" : "none";
2048
+ });
2049
+ }
2050
+ if (indicator) {
2051
+ indicator.textContent = `Page ${currentPage} of ${totalPages}`;
2052
+ }
2053
+ ctx.emit("page-change", { page: currentPage, total: totalPages });
2054
+ };
2055
+ if (totalPages > 1) {
2056
+ showPage(1);
2057
+ }
1837
2058
  const cleanup = () => {
2059
+ indicator?.remove();
1838
2060
  for (const url of createdBlobUrls) {
1839
2061
  URL.revokeObjectURL(url);
1840
2062
  }
@@ -1845,6 +2067,11 @@ var DocxPlugin = class {
1845
2067
  ctx.signal.addEventListener("abort", cleanup);
1846
2068
  return {
1847
2069
  destroy: cleanup,
2070
+ getPageCount: () => totalPages,
2071
+ getCurrentPage: () => currentPage,
2072
+ goToPage: (page) => {
2073
+ showPage(page);
2074
+ },
1848
2075
  zoomIn: () => {
1849
2076
  scale += 0.1;
1850
2077
  wrapper.style.transform = `scale(${scale})`;
@@ -2109,7 +2336,31 @@ var ExcelPlugin = class {
2109
2336
  return this.mimeTypes.includes(mime || "");
2110
2337
  }
2111
2338
  getToolbarActions(instance) {
2112
- return [
2339
+ const totalSheets = instance.getPageCount?.() ?? 1;
2340
+ const actions = [];
2341
+ if (totalSheets > 1) {
2342
+ actions.push({
2343
+ id: "page-nav",
2344
+ icon: "",
2345
+ label: "Sheet Navigation",
2346
+ type: "page-nav",
2347
+ group: "navigation",
2348
+ value: instance.getCurrentPage?.() ?? 1,
2349
+ max: totalSheets,
2350
+ execute: (action, sheet) => {
2351
+ const cur = instance.getCurrentPage?.() ?? 1;
2352
+ const max = instance.getPageCount?.() ?? 1;
2353
+ if (action === "prev") {
2354
+ if (cur > 1) instance.goToPage?.(cur - 1);
2355
+ } else if (action === "next") {
2356
+ if (cur < max) instance.goToPage?.(cur + 1);
2357
+ } else if (typeof sheet === "number") {
2358
+ instance.goToPage?.(sheet);
2359
+ }
2360
+ }
2361
+ });
2362
+ }
2363
+ actions.push(
2113
2364
  {
2114
2365
  id: "zoom-out",
2115
2366
  icon: "zoom-out",
@@ -2130,16 +2381,6 @@ var ExcelPlugin = class {
2130
2381
  instance.zoomIn?.();
2131
2382
  }
2132
2383
  },
2133
- {
2134
- id: "page-nav",
2135
- icon: "page-nav",
2136
- label: "Sheet Navigation",
2137
- type: "page-nav",
2138
- group: "navigation",
2139
- execute: (sheet) => {
2140
- if (typeof sheet === "number") instance.goToPage?.(sheet);
2141
- }
2142
- },
2143
2384
  {
2144
2385
  id: "download",
2145
2386
  icon: "download",
@@ -2160,7 +2401,8 @@ var ExcelPlugin = class {
2160
2401
  instance.print?.();
2161
2402
  }
2162
2403
  }
2163
- ];
2404
+ );
2405
+ return actions;
2164
2406
  }
2165
2407
  async render(ctx) {
2166
2408
  const container = document.createElement("div");
@@ -2244,6 +2486,7 @@ var ExcelPlugin = class {
2244
2486
  b.style.fontWeight = "normal";
2245
2487
  }
2246
2488
  });
2489
+ ctx.emit("page-change", { page: currentSheetIndex, total: sheetNames.length });
2247
2490
  };
2248
2491
  if (sheetNames.length > 0) {
2249
2492
  sheetNames.forEach((name, idx) => {
@@ -2493,7 +2736,31 @@ var CodePlugin = class {
2493
2736
  return false;
2494
2737
  }
2495
2738
  getToolbarActions(instance) {
2496
- return [
2739
+ const totalPages = instance.getPageCount?.() ?? 1;
2740
+ const actions = [];
2741
+ if (totalPages > 1) {
2742
+ actions.push({
2743
+ id: "page-nav",
2744
+ icon: "",
2745
+ label: "Page Navigation",
2746
+ type: "page-nav",
2747
+ group: "navigation",
2748
+ value: instance.getCurrentPage?.() ?? 1,
2749
+ max: totalPages,
2750
+ execute: (action, page) => {
2751
+ const cur = instance.getCurrentPage?.() ?? 1;
2752
+ const max = instance.getPageCount?.() ?? 1;
2753
+ if (action === "prev") {
2754
+ if (cur > 1) instance.goToPage?.(cur - 1);
2755
+ } else if (action === "next") {
2756
+ if (cur < max) instance.goToPage?.(cur + 1);
2757
+ } else if (typeof page === "number") {
2758
+ instance.goToPage?.(page);
2759
+ }
2760
+ }
2761
+ });
2762
+ }
2763
+ actions.push(
2497
2764
  {
2498
2765
  id: "zoom-out",
2499
2766
  icon: "zoom-out",
@@ -2544,11 +2811,16 @@ var CodePlugin = class {
2544
2811
  instance.print?.();
2545
2812
  }
2546
2813
  }
2547
- ];
2814
+ );
2815
+ return actions;
2548
2816
  }
2549
2817
  async render(ctx) {
2550
2818
  const decoder = new TextDecoder("utf-8");
2551
- const text = decoder.decode(ctx.buffer);
2819
+ const fullText = decoder.decode(ctx.buffer);
2820
+ const pageSplitRegex = /(?:\f|\x0C|(?:\r?\n|^)\s*[-=_]{3,}\s*(?:PAGE|Page|page break|Page Break)[\s\d\w-]*[-=_]{3,}\s*(?:\r?\n|$))/i;
2821
+ const rawPages = fullText.split(pageSplitRegex).map((p) => p.trim()).filter((p) => p.length > 0);
2822
+ const totalPages = Math.max(1, rawPages.length);
2823
+ let currentPage = 1;
2552
2824
  const container = document.createElement("div");
2553
2825
  container.style.width = "100%";
2554
2826
  container.style.height = "100%";
@@ -2567,25 +2839,66 @@ var CodePlugin = class {
2567
2839
  pre.style.wordBreak = "break-all";
2568
2840
  const code = document.createElement("code");
2569
2841
  const ext = (ctx.metadata.extension || "").replace(".", "");
2570
- try {
2571
- if (ext && hljs.getLanguage(ext)) {
2572
- code.innerHTML = hljs.highlight(text, { language: ext }).value;
2573
- } else {
2574
- code.innerHTML = hljs.highlightAuto(text).value;
2842
+ const renderCodePage = (text) => {
2843
+ try {
2844
+ if (ext && hljs.getLanguage(ext)) {
2845
+ code.innerHTML = hljs.highlight(text, { language: ext }).value;
2846
+ } else {
2847
+ code.innerHTML = hljs.highlightAuto(text).value;
2848
+ }
2849
+ } catch {
2850
+ code.textContent = text;
2575
2851
  }
2576
- } catch {
2577
- code.textContent = text;
2578
- }
2852
+ };
2853
+ renderCodePage(rawPages[0] || fullText);
2579
2854
  pre.appendChild(code);
2580
2855
  container.appendChild(pre);
2856
+ let indicator = null;
2857
+ if (totalPages > 1) {
2858
+ indicator = document.createElement("div");
2859
+ indicator.className = "fp-code-page-indicator";
2860
+ indicator.style.position = "sticky";
2861
+ indicator.style.bottom = "16px";
2862
+ indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
2863
+ indicator.style.backdropFilter = "blur(8px)";
2864
+ indicator.style.color = "#f8fafc";
2865
+ indicator.style.fontSize = "12px";
2866
+ indicator.style.fontWeight = "600";
2867
+ indicator.style.padding = "5px 14px";
2868
+ indicator.style.borderRadius = "20px";
2869
+ indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
2870
+ indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
2871
+ indicator.style.zIndex = "10";
2872
+ indicator.style.userSelect = "none";
2873
+ indicator.style.pointerEvents = "none";
2874
+ indicator.style.textAlign = "center";
2875
+ indicator.style.width = "fit-content";
2876
+ indicator.style.margin = "16px auto 0";
2877
+ container.appendChild(indicator);
2878
+ }
2879
+ const showPage = (pageNum) => {
2880
+ currentPage = Math.max(1, Math.min(totalPages, pageNum));
2881
+ renderCodePage(rawPages[currentPage - 1] || fullText);
2882
+ if (indicator) {
2883
+ indicator.textContent = `Page ${currentPage} of ${totalPages}`;
2884
+ }
2885
+ ctx.emit("page-change", { page: currentPage, total: totalPages });
2886
+ };
2887
+ if (totalPages > 1) {
2888
+ showPage(1);
2889
+ }
2581
2890
  ctx.container.appendChild(container);
2582
2891
  const cleanup = () => {
2892
+ indicator?.remove();
2583
2893
  container.remove();
2584
2894
  ctx.container.innerHTML = "";
2585
2895
  };
2586
2896
  ctx.signal.addEventListener("abort", cleanup);
2587
2897
  return {
2588
2898
  destroy: cleanup,
2899
+ getPageCount: () => totalPages,
2900
+ getCurrentPage: () => currentPage,
2901
+ goToPage: (page) => showPage(page),
2589
2902
  zoomIn: () => {
2590
2903
  fontSize = Math.min(32, fontSize + 2);
2591
2904
  pre.style.fontSize = `${fontSize}px`;
@@ -2613,7 +2926,7 @@ var CodePlugin = class {
2613
2926
  window.print();
2614
2927
  },
2615
2928
  copy: () => {
2616
- navigator.clipboard?.writeText(text);
2929
+ navigator.clipboard?.writeText(fullText);
2617
2930
  }
2618
2931
  };
2619
2932
  }
@@ -3435,7 +3748,31 @@ var RtfPlugin = class {
3435
3748
  return this.extensions.includes(ext || "") || this.mimeTypes.includes(mime || "");
3436
3749
  }
3437
3750
  getToolbarActions(instance) {
3438
- return [
3751
+ const totalPages = instance.getPageCount?.() ?? 1;
3752
+ const actions = [];
3753
+ if (totalPages > 1) {
3754
+ actions.push({
3755
+ id: "page-nav",
3756
+ icon: "",
3757
+ label: "Page Navigation",
3758
+ type: "page-nav",
3759
+ group: "navigation",
3760
+ value: instance.getCurrentPage?.() ?? 1,
3761
+ max: totalPages,
3762
+ execute: (action, page) => {
3763
+ const cur = instance.getCurrentPage?.() ?? 1;
3764
+ const max = instance.getPageCount?.() ?? 1;
3765
+ if (action === "prev") {
3766
+ if (cur > 1) instance.goToPage?.(cur - 1);
3767
+ } else if (action === "next") {
3768
+ if (cur < max) instance.goToPage?.(cur + 1);
3769
+ } else if (typeof page === "number") {
3770
+ instance.goToPage?.(page);
3771
+ }
3772
+ }
3773
+ });
3774
+ }
3775
+ actions.push(
3439
3776
  {
3440
3777
  id: "zoom-out",
3441
3778
  icon: "zoom-out",
@@ -3476,7 +3813,8 @@ var RtfPlugin = class {
3476
3813
  group: "actions",
3477
3814
  execute: () => instance.print?.()
3478
3815
  }
3479
- ];
3816
+ );
3817
+ return actions;
3480
3818
  }
3481
3819
  async render(ctx) {
3482
3820
  const wrapper = document.createElement("div");
@@ -3495,28 +3833,82 @@ var RtfPlugin = class {
3495
3833
  ctx.container.style.backgroundColor = "#f1f5f9";
3496
3834
  ctx.container.appendChild(wrapper);
3497
3835
  let scale = 1;
3836
+ let pageElements = [];
3498
3837
  try {
3499
3838
  if (typeof RTFJS.loggingEnabled === "function") {
3500
3839
  RTFJS.loggingEnabled(false);
3501
3840
  }
3502
3841
  const doc = new RTFJS.Document(ctx.buffer, {});
3503
3842
  const htmlElements = await doc.render();
3504
- for (const el of htmlElements) {
3843
+ pageElements = htmlElements;
3844
+ for (let i = 0; i < htmlElements.length; i++) {
3845
+ const el = htmlElements[i];
3846
+ el.style.display = i === 0 ? "block" : "none";
3505
3847
  wrapper.appendChild(el);
3506
3848
  }
3507
3849
  } catch (err) {
3508
3850
  console.warn("[RtfPlugin] RTF render error, fallback text:", err);
3509
3851
  const text = new TextDecoder("latin1").decode(ctx.buffer);
3510
3852
  const clean = text.replace(/\\par[d]?/g, "\n").replace(/\\[a-zA-Z0-9\-]+/g, "").replace(/[{}]/g, "");
3511
- wrapper.innerHTML = `<pre style="white-space: pre-wrap; font-family: serif; color: #333;">${clean}</pre>`;
3853
+ const pre = document.createElement("pre");
3854
+ pre.style.whiteSpace = "pre-wrap";
3855
+ pre.style.fontFamily = "serif";
3856
+ pre.style.color = "#333";
3857
+ pre.textContent = clean;
3858
+ wrapper.appendChild(pre);
3859
+ pageElements = [pre];
3860
+ }
3861
+ const totalPages = Math.max(1, pageElements.length);
3862
+ let currentPage = 1;
3863
+ let indicator = null;
3864
+ if (totalPages > 1) {
3865
+ indicator = document.createElement("div");
3866
+ indicator.className = "fp-rtf-page-indicator";
3867
+ indicator.style.position = "sticky";
3868
+ indicator.style.bottom = "16px";
3869
+ indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
3870
+ indicator.style.backdropFilter = "blur(8px)";
3871
+ indicator.style.color = "#f8fafc";
3872
+ indicator.style.fontSize = "12px";
3873
+ indicator.style.fontWeight = "600";
3874
+ indicator.style.padding = "5px 14px";
3875
+ indicator.style.borderRadius = "20px";
3876
+ indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
3877
+ indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
3878
+ indicator.style.zIndex = "10";
3879
+ indicator.style.userSelect = "none";
3880
+ indicator.style.pointerEvents = "none";
3881
+ indicator.style.textAlign = "center";
3882
+ indicator.style.width = "fit-content";
3883
+ indicator.style.margin = "16px auto 0";
3884
+ ctx.container.appendChild(indicator);
3885
+ }
3886
+ const showPage = (pageNum) => {
3887
+ currentPage = Math.max(1, Math.min(totalPages, pageNum));
3888
+ if (totalPages > 1) {
3889
+ pageElements.forEach((el, idx) => {
3890
+ el.style.display = idx + 1 === currentPage ? "block" : "none";
3891
+ });
3892
+ }
3893
+ if (indicator) {
3894
+ indicator.textContent = `Page ${currentPage} of ${totalPages}`;
3895
+ }
3896
+ ctx.emit("page-change", { page: currentPage, total: totalPages });
3897
+ };
3898
+ if (totalPages > 1) {
3899
+ showPage(1);
3512
3900
  }
3513
3901
  const cleanup = () => {
3902
+ indicator?.remove();
3514
3903
  wrapper.remove();
3515
3904
  ctx.container.innerHTML = "";
3516
3905
  };
3517
3906
  ctx.signal.addEventListener("abort", cleanup);
3518
3907
  return {
3519
3908
  destroy: cleanup,
3909
+ getPageCount: () => totalPages,
3910
+ getCurrentPage: () => currentPage,
3911
+ goToPage: (page) => showPage(page),
3520
3912
  zoomIn: () => {
3521
3913
  scale += 0.1;
3522
3914
  wrapper.style.transform = `scale(${scale})`;
@@ -3704,41 +4096,41 @@ var OpenDocumentPlugin = class {
3704
4096
  }
3705
4097
  getToolbarActions(instance) {
3706
4098
  const isPresentation = instance.isPresentation;
4099
+ const totalPages = instance.getPageCount?.() ?? 1;
3707
4100
  const actions = [];
3708
- if (isPresentation) {
3709
- actions.push(
3710
- {
4101
+ if (isPresentation || totalPages > 1) {
4102
+ if (isPresentation) {
4103
+ actions.push({
3711
4104
  id: "thumbnails",
3712
4105
  icon: "thumbnails",
3713
4106
  label: "Slide Thumbnails",
3714
4107
  type: "button",
3715
4108
  group: "navigation",
3716
4109
  execute: () => instance.toggleThumbnails?.()
3717
- },
3718
- {
3719
- id: "page-nav",
3720
- icon: "",
3721
- label: "Slide Navigation",
3722
- type: "page-nav",
3723
- group: "navigation",
3724
- value: instance.getCurrentPage?.() ?? 1,
3725
- max: instance.getPageCount?.() ?? 1,
3726
- execute: (action, page) => {
3727
- if (action === "prev") {
3728
- const cur = instance.getCurrentPage?.() ?? 1;
3729
- if (cur > 1) instance.goToPage?.(cur - 1);
3730
- } else if (action === "next") {
3731
- const cur = instance.getCurrentPage?.() ?? 1;
3732
- const total = instance.getPageCount?.() ?? 1;
3733
- if (cur < total) instance.goToPage?.(cur + 1);
3734
- } else if (typeof page === "number") {
3735
- instance.goToPage?.(page);
3736
- } else if (typeof action === "number") {
3737
- instance.goToPage?.(action);
3738
- }
4110
+ });
4111
+ }
4112
+ actions.push({
4113
+ id: "page-nav",
4114
+ icon: "",
4115
+ label: isPresentation ? "Slide Navigation" : "Page Navigation",
4116
+ type: "page-nav",
4117
+ group: "navigation",
4118
+ value: instance.getCurrentPage?.() ?? 1,
4119
+ max: totalPages,
4120
+ execute: (action, page) => {
4121
+ const cur = instance.getCurrentPage?.() ?? 1;
4122
+ const max = instance.getPageCount?.() ?? 1;
4123
+ if (action === "prev") {
4124
+ if (cur > 1) instance.goToPage?.(cur - 1);
4125
+ } else if (action === "next") {
4126
+ if (cur < max) instance.goToPage?.(cur + 1);
4127
+ } else if (typeof page === "number") {
4128
+ instance.goToPage?.(page);
4129
+ } else if (typeof action === "number") {
4130
+ instance.goToPage?.(action);
3739
4131
  }
3740
4132
  }
3741
- );
4133
+ });
3742
4134
  }
3743
4135
  actions.push(
3744
4136
  {
@@ -4121,7 +4513,31 @@ var DocPlugin = class {
4121
4513
  return this.mimeTypes.includes(mime || "");
4122
4514
  }
4123
4515
  getToolbarActions(instance) {
4124
- return [
4516
+ const totalPages = instance.getPageCount?.() ?? 1;
4517
+ const actions = [];
4518
+ if (totalPages > 1) {
4519
+ actions.push({
4520
+ id: "page-nav",
4521
+ icon: "",
4522
+ label: "Page Navigation",
4523
+ type: "page-nav",
4524
+ group: "navigation",
4525
+ value: instance.getCurrentPage?.() ?? 1,
4526
+ max: totalPages,
4527
+ execute: (action, page) => {
4528
+ const cur = instance.getCurrentPage?.() ?? 1;
4529
+ const max = instance.getPageCount?.() ?? 1;
4530
+ if (action === "prev") {
4531
+ if (cur > 1) instance.goToPage?.(cur - 1);
4532
+ } else if (action === "next") {
4533
+ if (cur < max) instance.goToPage?.(cur + 1);
4534
+ } else if (typeof page === "number") {
4535
+ instance.goToPage?.(page);
4536
+ }
4537
+ }
4538
+ });
4539
+ }
4540
+ actions.push(
4125
4541
  {
4126
4542
  id: "zoom-out",
4127
4543
  icon: "zoom-out",
@@ -4170,7 +4586,8 @@ var DocPlugin = class {
4170
4586
  group: "actions",
4171
4587
  execute: () => instance.print?.()
4172
4588
  }
4173
- ];
4589
+ );
4590
+ return actions;
4174
4591
  }
4175
4592
  async render(ctx) {
4176
4593
  const container = document.createElement("div");
@@ -4197,6 +4614,7 @@ var DocPlugin = class {
4197
4614
  ctx.container.appendChild(container);
4198
4615
  let scale = 1;
4199
4616
  let extractedRawText = "";
4617
+ let isFallback = false;
4200
4618
  try {
4201
4619
  const cfbf = new CfbfReader(ctx.buffer);
4202
4620
  const wordDocStream = cfbf.readStream("WordDocument");
@@ -4210,26 +4628,89 @@ var DocPlugin = class {
4210
4628
  const tableStream = cfbf.readStream(tableName);
4211
4629
  const text = this.extractDocText(wordDocStream, tableStream);
4212
4630
  extractedRawText = text;
4213
- wrapper.innerHTML = this.formatDocToHtml(text, ctx.metadata.name || "Document");
4214
4631
  } catch (err) {
4215
4632
  console.warn("[DocPlugin] Binary parsing error, fallback text:", err);
4216
4633
  const fallback = this.heuristicTextExtraction(ctx.buffer);
4217
4634
  extractedRawText = fallback;
4218
- wrapper.innerHTML = `
4219
- <div style="border-bottom: 1px solid #e2e8f0; padding-bottom: 12px; margin-bottom: 24px;">
4220
- <h2 style="margin: 0 0 6px; font-size: 20px; color: #334155;">${ctx.metadata.name || "Word Document (.doc)"}</h2>
4221
- <span style="font-size: 12px; color: #64748b; background: #f1f5f9; padding: 2px 8px; border-radius: 4px;">Legacy Word 97-2003 Binary Preview</span>
4222
- </div>
4223
- ${this.formatDocToHtml(fallback, ctx.metadata.name || "Document")}
4224
- `;
4635
+ isFallback = true;
4636
+ }
4637
+ const rawPages = this.splitIntoPages(extractedRawText);
4638
+ const totalPages = Math.max(1, rawPages.length);
4639
+ let currentPage = 1;
4640
+ wrapper.innerHTML = "";
4641
+ const pageCards = [];
4642
+ for (let i = 0; i < totalPages; i++) {
4643
+ const pageCard = document.createElement("div");
4644
+ pageCard.className = "fp-doc-page-card";
4645
+ pageCard.style.backgroundColor = "#ffffff";
4646
+ pageCard.style.boxShadow = "0 2px 12px rgba(0,0,0,0.08)";
4647
+ pageCard.style.borderRadius = "4px";
4648
+ pageCard.style.padding = "56px 48px";
4649
+ pageCard.style.minHeight = "100%";
4650
+ pageCard.style.display = i === 0 ? "block" : "none";
4651
+ if (isFallback && i === 0) {
4652
+ pageCard.innerHTML = `
4653
+ <div style="border-bottom: 1px solid #e2e8f0; padding-bottom: 12px; margin-bottom: 24px;">
4654
+ <h2 style="margin: 0 0 6px; font-size: 20px; color: #334155;">${DOMPurify2.sanitize(ctx.metadata.name || "Word Document (.doc)")}</h2>
4655
+ <span style="font-size: 12px; color: #64748b; background: #f1f5f9; padding: 2px 8px; border-radius: 4px;">Legacy Word 97-2003 Binary Preview</span>
4656
+ </div>
4657
+ ${this.formatDocToHtml(rawPages[i], ctx.metadata.name || "Document")}
4658
+ `;
4659
+ } else {
4660
+ pageCard.innerHTML = this.formatDocToHtml(rawPages[i], ctx.metadata.name || "Document");
4661
+ }
4662
+ wrapper.appendChild(pageCard);
4663
+ pageCards.push(pageCard);
4664
+ }
4665
+ let indicator = null;
4666
+ if (totalPages > 1) {
4667
+ indicator = document.createElement("div");
4668
+ indicator.className = "fp-doc-page-indicator";
4669
+ indicator.style.position = "sticky";
4670
+ indicator.style.bottom = "16px";
4671
+ indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
4672
+ indicator.style.backdropFilter = "blur(8px)";
4673
+ indicator.style.color = "#f8fafc";
4674
+ indicator.style.fontSize = "12px";
4675
+ indicator.style.fontWeight = "600";
4676
+ indicator.style.padding = "5px 14px";
4677
+ indicator.style.borderRadius = "20px";
4678
+ indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
4679
+ indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
4680
+ indicator.style.zIndex = "10";
4681
+ indicator.style.userSelect = "none";
4682
+ indicator.style.pointerEvents = "none";
4683
+ indicator.style.textAlign = "center";
4684
+ indicator.style.width = "fit-content";
4685
+ indicator.style.margin = "16px auto 0";
4686
+ container.appendChild(indicator);
4687
+ }
4688
+ const showPage = (pageNum) => {
4689
+ currentPage = Math.max(1, Math.min(totalPages, pageNum));
4690
+ if (totalPages > 1) {
4691
+ pageCards.forEach((card, idx) => {
4692
+ card.style.display = idx + 1 === currentPage ? "block" : "none";
4693
+ });
4694
+ }
4695
+ if (indicator) {
4696
+ indicator.textContent = `Page ${currentPage} of ${totalPages}`;
4697
+ }
4698
+ ctx.emit("page-change", { page: currentPage, total: totalPages });
4699
+ };
4700
+ if (totalPages > 1) {
4701
+ showPage(1);
4225
4702
  }
4226
4703
  const cleanup = () => {
4704
+ indicator?.remove();
4227
4705
  container.remove();
4228
4706
  ctx.container.innerHTML = "";
4229
4707
  };
4230
4708
  ctx.signal.addEventListener("abort", cleanup);
4231
4709
  return {
4232
4710
  destroy: cleanup,
4711
+ getPageCount: () => totalPages,
4712
+ getCurrentPage: () => currentPage,
4713
+ goToPage: (page) => showPage(page),
4233
4714
  zoomIn: () => {
4234
4715
  scale += 0.1;
4235
4716
  wrapper.style.transform = `scale(${scale})`;
@@ -4361,11 +4842,16 @@ var DocPlugin = class {
4361
4842
  heuristicTextExtraction(buffer) {
4362
4843
  return this.extractStringsFromBytes(new Uint8Array(buffer));
4363
4844
  }
4845
+ splitIntoPages(text) {
4846
+ if (!text) return [""];
4847
+ 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);
4848
+ return parts.length > 0 ? parts : [text];
4849
+ }
4364
4850
  /**
4365
4851
  * Format cleaned extracted text into attractive HTML paragraphs, headings, and lists
4366
4852
  */
4367
4853
  formatDocToHtml(text, filename) {
4368
- const lines = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\x0B/g, "\n").replace(/\x0C/g, "\n\n").split("\n");
4854
+ const lines = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\x0B/g, "\n").split("\n");
4369
4855
  let html = "";
4370
4856
  let inList = false;
4371
4857
  for (const rawLine of lines) {