@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.cjs CHANGED
@@ -5,6 +5,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
5
5
  var core = require('@angular/core');
6
6
  var common = require('@angular/common');
7
7
  var DOMPurify2 = require('dompurify');
8
+ var pdfjsLib = require('pdfjs-dist');
8
9
  var docx = require('docx-preview');
9
10
  var fflate = require('fflate');
10
11
  var XLSX = require('xlsx');
@@ -38,6 +39,7 @@ function _interopNamespace(e) {
38
39
  }
39
40
 
40
41
  var DOMPurify2__default = /*#__PURE__*/_interopDefault(DOMPurify2);
42
+ var pdfjsLib__namespace = /*#__PURE__*/_interopNamespace(pdfjsLib);
41
43
  var docx__namespace = /*#__PURE__*/_interopNamespace(docx);
42
44
  var XLSX__namespace = /*#__PURE__*/_interopNamespace(XLSX);
43
45
  var hljs__default = /*#__PURE__*/_interopDefault(hljs);
@@ -558,11 +560,21 @@ var ToolbarController = class {
558
560
  el;
559
561
  toolbarEl;
560
562
  actions = [];
563
+ pageInputEl = null;
564
+ pageLabelEl = null;
561
565
  constructor(container) {
562
566
  this.el = container;
563
567
  this.toolbarEl = createElement("div", { className: "fp-toolbar" });
564
568
  this.el.appendChild(this.toolbarEl);
565
569
  }
570
+ setPage(page, max) {
571
+ if (this.pageInputEl) {
572
+ this.pageInputEl.value = page.toString();
573
+ }
574
+ if (max !== void 0 && this.pageLabelEl) {
575
+ this.pageLabelEl.textContent = ` / ${max}`;
576
+ }
577
+ }
566
578
  update(actions) {
567
579
  this.actions = actions;
568
580
  this.render();
@@ -605,6 +617,7 @@ var ToolbarController = class {
605
617
  if (action.type === "separator") {
606
618
  groupEl.appendChild(createElement("div", { className: "fp-toolbar-separator" }));
607
619
  } else if (action.type === "page-nav") {
620
+ const max = action.max ?? 1;
608
621
  const prevBtn = this.createButton(
609
622
  "prev",
610
623
  ICON_PAGE_PREV,
@@ -623,7 +636,6 @@ var ToolbarController = class {
623
636
  "Next Page",
624
637
  () => {
625
638
  const cur = parseInt(input.value, 10) || 1;
626
- const max = action.max ?? 1;
627
639
  if (cur < max) {
628
640
  input.value = (cur + 1).toString();
629
641
  action.execute("next", cur + 1);
@@ -635,15 +647,18 @@ var ToolbarController = class {
635
647
  type: "number",
636
648
  value: (action.value ?? 1).toString(),
637
649
  min: "1",
638
- max: (action.max ?? 1).toString()
650
+ max: max.toString()
639
651
  });
640
652
  input.addEventListener("change", () => {
641
- const val = parseInt(input.value, 10);
642
- if (!isNaN(val)) {
643
- action.execute("go", val);
644
- }
653
+ let val = parseInt(input.value, 10);
654
+ if (isNaN(val)) val = 1;
655
+ val = Math.max(1, Math.min(max, val));
656
+ input.value = val.toString();
657
+ action.execute("go", val);
645
658
  });
646
- const label = createElement("span", { className: "fp-toolbar-label" }, ` / ${action.max ?? 1}`);
659
+ const label = createElement("span", { className: "fp-toolbar-label" }, ` / ${max}`);
660
+ this.pageInputEl = input;
661
+ this.pageLabelEl = label;
647
662
  groupEl.appendChild(prevBtn);
648
663
  groupEl.appendChild(input);
649
664
  groupEl.appendChild(label);
@@ -821,7 +836,13 @@ var FilePreviewViewer = class {
821
836
  buffer,
822
837
  options,
823
838
  signal,
824
- emit: (event, payload) => this.eventEmitter.emit(event, payload)
839
+ emit: (event, payload) => {
840
+ if (event === "page-change" && payload && typeof payload.page === "number") {
841
+ const total = payload.total ?? payload.totalPages;
842
+ this.toolbar?.setPage(payload.page, total);
843
+ }
844
+ this.eventEmitter.emit(event, payload);
845
+ }
825
846
  });
826
847
  this.activeInstance = instance;
827
848
  this.hideLoading();
@@ -855,6 +876,7 @@ var FilePreviewViewer = class {
855
876
  if (thumbnails && thumbnails.length > 0 && this.thumbnailPanel) {
856
877
  this.thumbnailPanel.update(thumbnails, (index) => {
857
878
  instance.goToPage?.(index + 1);
879
+ this.toolbar?.setPage(index + 1);
858
880
  });
859
881
  if (options.showThumbnails) {
860
882
  this.thumbnailPanel.show();
@@ -980,9 +1002,13 @@ var FilePreviewViewer = class {
980
1002
  if (e.key === "ArrowRight" || e.key === "PageDown") {
981
1003
  const cur = this.activeInstance.getCurrentPage?.() ?? 1;
982
1004
  this.activeInstance.goToPage?.(cur + 1);
1005
+ const nextCur = this.activeInstance.getCurrentPage?.() ?? cur + 1;
1006
+ this.toolbar?.setPage(nextCur);
983
1007
  } else if (e.key === "ArrowLeft" || e.key === "PageUp") {
984
1008
  const cur = this.activeInstance.getCurrentPage?.() ?? 1;
985
1009
  this.activeInstance.goToPage?.(Math.max(1, cur - 1));
1010
+ const prevCur = this.activeInstance.getCurrentPage?.() ?? Math.max(1, cur - 1);
1011
+ this.toolbar?.setPage(prevCur);
986
1012
  } else if (e.key === "+" || e.key === "=") {
987
1013
  this.activeInstance.zoomIn?.();
988
1014
  } else if (e.key === "-" || e.key === "_") {
@@ -1248,30 +1274,62 @@ var CfbfReader = class {
1248
1274
  return result.subarray(0, targetSize);
1249
1275
  }
1250
1276
  };
1251
-
1252
- // ../plugins/pdf/dist/index.js
1277
+ if (typeof window !== "undefined" && pdfjsLib__namespace.GlobalWorkerOptions) {
1278
+ if (!pdfjsLib__namespace.GlobalWorkerOptions.workerSrc) {
1279
+ pdfjsLib__namespace.GlobalWorkerOptions.workerSrc = `https://unpkg.com/pdfjs-dist@${pdfjsLib__namespace.version || "4.10.38"}/build/pdf.worker.min.mjs`;
1280
+ }
1281
+ }
1253
1282
  var PdfPlugin = class {
1254
1283
  id = "pdf";
1255
- name = "PDF Preview";
1284
+ name = "PDF Document Preview";
1256
1285
  extensions = [".pdf"];
1257
1286
  mimeTypes = ["application/pdf"];
1258
1287
  weight = 100;
1259
1288
  supports(file) {
1260
1289
  const ext = file.metadata.extension?.toLowerCase();
1261
1290
  const mime = file.metadata.mimeType?.toLowerCase();
1262
- return ext === ".pdf" || mime === "application/pdf";
1291
+ if (ext) return this.extensions.includes(ext);
1292
+ return this.mimeTypes.includes(mime || "");
1263
1293
  }
1264
1294
  getToolbarActions(instance) {
1295
+ const totalPages = instance.getPageCount?.() ?? 1;
1296
+ const curPage = instance.getCurrentPage?.() ?? 1;
1265
1297
  return [
1298
+ {
1299
+ id: "thumbnails",
1300
+ icon: "thumbnails",
1301
+ label: "Page Thumbnails",
1302
+ type: "button",
1303
+ group: "navigation",
1304
+ execute: () => instance.toggleThumbnails?.()
1305
+ },
1306
+ {
1307
+ id: "page-nav",
1308
+ icon: "",
1309
+ label: "Page Navigation",
1310
+ type: "page-nav",
1311
+ group: "navigation",
1312
+ value: curPage,
1313
+ max: totalPages,
1314
+ execute: (action, page) => {
1315
+ const cur = instance.getCurrentPage?.() ?? 1;
1316
+ const max = instance.getPageCount?.() ?? 1;
1317
+ if (action === "prev") {
1318
+ if (cur > 1) instance.goToPage?.(cur - 1);
1319
+ } else if (action === "next") {
1320
+ if (cur < max) instance.goToPage?.(cur + 1);
1321
+ } else if (typeof page === "number") {
1322
+ instance.goToPage?.(page);
1323
+ }
1324
+ }
1325
+ },
1266
1326
  {
1267
1327
  id: "zoom-out",
1268
1328
  icon: "zoom-out",
1269
1329
  label: "Zoom Out",
1270
1330
  type: "button",
1271
1331
  group: "zoom",
1272
- execute: () => {
1273
- instance.zoomOut?.();
1274
- }
1332
+ execute: () => instance.zoomOut?.()
1275
1333
  },
1276
1334
  {
1277
1335
  id: "zoom-in",
@@ -1279,9 +1337,7 @@ var PdfPlugin = class {
1279
1337
  label: "Zoom In",
1280
1338
  type: "button",
1281
1339
  group: "zoom",
1282
- execute: () => {
1283
- instance.zoomIn?.();
1284
- }
1340
+ execute: () => instance.zoomIn?.()
1285
1341
  },
1286
1342
  {
1287
1343
  id: "fit-page",
@@ -1289,39 +1345,23 @@ var PdfPlugin = class {
1289
1345
  label: "Fit to Page",
1290
1346
  type: "button",
1291
1347
  group: "zoom",
1292
- execute: () => {
1293
- instance.fitToPage?.();
1294
- }
1348
+ execute: () => instance.fitToPage?.()
1295
1349
  },
1296
1350
  {
1297
1351
  id: "rotate-cw",
1298
1352
  icon: "rotate-cw",
1299
- label: "Rotate",
1353
+ label: "Rotate Clockwise",
1300
1354
  type: "button",
1301
1355
  group: "view",
1302
- execute: () => {
1303
- instance.rotateCW?.();
1304
- }
1305
- },
1306
- {
1307
- id: "page-nav",
1308
- icon: "page-nav",
1309
- label: "Page Navigation",
1310
- type: "page-nav",
1311
- group: "navigation",
1312
- execute: (page) => {
1313
- if (typeof page === "number") instance.goToPage?.(page);
1314
- }
1356
+ execute: () => instance.rotateCW?.()
1315
1357
  },
1316
1358
  {
1317
1359
  id: "download",
1318
1360
  icon: "download",
1319
- label: "Download",
1361
+ label: "Download PDF",
1320
1362
  type: "button",
1321
1363
  group: "actions",
1322
- execute: () => {
1323
- instance.download?.();
1324
- }
1364
+ execute: () => instance.download?.()
1325
1365
  },
1326
1366
  {
1327
1367
  id: "print",
@@ -1329,99 +1369,213 @@ var PdfPlugin = class {
1329
1369
  label: "Print",
1330
1370
  type: "button",
1331
1371
  group: "actions",
1332
- execute: () => {
1333
- instance.print?.();
1334
- }
1372
+ execute: () => instance.print?.()
1335
1373
  }
1336
1374
  ];
1337
1375
  }
1338
1376
  async render(ctx) {
1339
- const blob = new Blob([ctx.buffer], { type: "application/pdf" });
1340
- const url = URL.createObjectURL(blob);
1341
- const wrapper = document.createElement("div");
1342
- wrapper.style.width = "100%";
1343
- wrapper.style.height = "100%";
1344
- wrapper.style.overflow = "hidden";
1345
- wrapper.style.display = "flex";
1346
- wrapper.style.justifyContent = "center";
1347
- wrapper.style.alignItems = "center";
1348
- const iframe = document.createElement("iframe");
1349
- iframe.src = url;
1350
- iframe.style.width = "100%";
1351
- iframe.style.height = "100%";
1352
- iframe.style.border = "none";
1353
- wrapper.appendChild(iframe);
1354
- ctx.container.appendChild(wrapper);
1377
+ const container = document.createElement("div");
1378
+ container.className = "fp-pdf-container";
1379
+ container.style.width = "100%";
1380
+ container.style.height = "100%";
1381
+ container.style.overflow = "auto";
1382
+ container.style.display = "flex";
1383
+ container.style.flexDirection = "column";
1384
+ container.style.alignItems = "center";
1385
+ container.style.padding = "24px 16px";
1386
+ container.style.backgroundColor = "#0f172a";
1387
+ container.style.boxSizing = "border-box";
1388
+ container.style.position = "relative";
1389
+ const pageCard = document.createElement("div");
1390
+ pageCard.className = "fp-pdf-page-card";
1391
+ pageCard.style.boxShadow = "0 10px 35px rgba(0, 0, 0, 0.5)";
1392
+ pageCard.style.backgroundColor = "#ffffff";
1393
+ pageCard.style.borderRadius = "4px";
1394
+ pageCard.style.overflow = "hidden";
1395
+ pageCard.style.lineHeight = "0";
1396
+ pageCard.style.transition = "transform 0.15s ease";
1397
+ pageCard.style.position = "relative";
1398
+ const canvas = document.createElement("canvas");
1399
+ pageCard.appendChild(canvas);
1400
+ container.appendChild(pageCard);
1401
+ const indicator = document.createElement("div");
1402
+ indicator.className = "fp-pdf-page-indicator";
1403
+ indicator.style.position = "sticky";
1404
+ indicator.style.bottom = "16px";
1405
+ indicator.style.marginTop = "16px";
1406
+ indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
1407
+ indicator.style.backdropFilter = "blur(8px)";
1408
+ indicator.style.color = "#f8fafc";
1409
+ indicator.style.fontSize = "12px";
1410
+ indicator.style.fontWeight = "600";
1411
+ indicator.style.padding = "5px 14px";
1412
+ indicator.style.borderRadius = "20px";
1413
+ indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
1414
+ indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
1415
+ indicator.style.zIndex = "10";
1416
+ indicator.style.userSelect = "none";
1417
+ indicator.style.pointerEvents = "none";
1418
+ container.appendChild(indicator);
1419
+ ctx.container.appendChild(container);
1420
+ const loadingTask = pdfjsLib__namespace.getDocument({
1421
+ data: new Uint8Array(ctx.buffer),
1422
+ cMapUrl: `https://unpkg.com/pdfjs-dist@${pdfjsLib__namespace.version || "4.10.38"}/cmaps/`,
1423
+ cMapPacked: true,
1424
+ standardFontDataUrl: `https://unpkg.com/pdfjs-dist@${pdfjsLib__namespace.version || "4.10.38"}/standard_fonts/`
1425
+ });
1426
+ const pdfDoc = await loadingTask.promise;
1427
+ const totalPages = Math.max(1, pdfDoc.numPages);
1355
1428
  let currentPage = 1;
1356
- let currentZoom = 1;
1429
+ let zoomScale = 1;
1357
1430
  let rotation = 0;
1431
+ let currentRenderTask = null;
1432
+ const renderPage = async (pageNum) => {
1433
+ if (currentRenderTask) {
1434
+ try {
1435
+ currentRenderTask.cancel();
1436
+ } catch {
1437
+ }
1438
+ currentRenderTask = null;
1439
+ }
1440
+ currentPage = Math.max(1, Math.min(totalPages, pageNum));
1441
+ indicator.textContent = `Page ${currentPage} of ${totalPages}`;
1442
+ ctx.emit("page-change", { page: currentPage, total: totalPages });
1443
+ const page = await pdfDoc.getPage(currentPage);
1444
+ const containerWidth = container.clientWidth || 900;
1445
+ const unscaledVp = page.getViewport({ scale: 1, rotation });
1446
+ const baseScale = Math.min((containerWidth - 64) / unscaledVp.width, 1.6);
1447
+ const effectiveScale = (baseScale > 0 ? baseScale : 1) * zoomScale;
1448
+ const pixelRatio = window.devicePixelRatio || 1;
1449
+ const viewport = page.getViewport({ scale: effectiveScale, rotation });
1450
+ canvas.width = Math.floor(viewport.width * pixelRatio);
1451
+ canvas.height = Math.floor(viewport.height * pixelRatio);
1452
+ canvas.style.width = `${Math.floor(viewport.width)}px`;
1453
+ canvas.style.height = `${Math.floor(viewport.height)}px`;
1454
+ const canvasCtx = canvas.getContext("2d");
1455
+ if (!canvasCtx) return;
1456
+ canvasCtx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
1457
+ currentRenderTask = page.render({
1458
+ canvasContext: canvasCtx,
1459
+ viewport
1460
+ });
1461
+ try {
1462
+ await currentRenderTask.promise;
1463
+ } catch (err) {
1464
+ if (err?.name !== "RenderingCancelledException") {
1465
+ console.warn("[PdfPlugin] Page render warning:", err);
1466
+ }
1467
+ } finally {
1468
+ currentRenderTask = null;
1469
+ }
1470
+ };
1471
+ await renderPage(1);
1358
1472
  const cleanup = () => {
1359
- URL.revokeObjectURL(url);
1360
- wrapper.remove();
1473
+ if (currentRenderTask) {
1474
+ try {
1475
+ currentRenderTask.cancel();
1476
+ } catch {
1477
+ }
1478
+ }
1479
+ try {
1480
+ pdfDoc.destroy();
1481
+ } catch {
1482
+ }
1483
+ container.remove();
1361
1484
  ctx.container.innerHTML = "";
1362
1485
  };
1363
1486
  ctx.signal.addEventListener("abort", cleanup);
1364
- return {
1487
+ const instance = {
1365
1488
  destroy: cleanup,
1366
1489
  zoomIn: () => {
1367
- currentZoom += 0.1;
1368
- iframe.style.transform = `scale(${currentZoom}) rotate(${rotation}deg)`;
1490
+ zoomScale = Math.min(3.5, zoomScale + 0.2);
1491
+ renderPage(currentPage);
1369
1492
  },
1370
1493
  zoomOut: () => {
1371
- currentZoom = Math.max(0.2, currentZoom - 0.1);
1372
- iframe.style.transform = `scale(${currentZoom}) rotate(${rotation}deg)`;
1494
+ zoomScale = Math.max(0.3, zoomScale - 0.2);
1495
+ renderPage(currentPage);
1373
1496
  },
1374
- getZoom: () => currentZoom,
1497
+ getZoom: () => zoomScale,
1375
1498
  setZoom: (level) => {
1376
- currentZoom = level;
1377
- iframe.style.transform = `scale(${currentZoom}) rotate(${rotation}deg)`;
1499
+ zoomScale = Math.max(0.3, Math.min(3.5, level));
1500
+ renderPage(currentPage);
1378
1501
  },
1379
1502
  fitToPage: () => {
1380
- currentZoom = 1;
1381
- iframe.style.transform = `scale(1) rotate(${rotation}deg)`;
1503
+ zoomScale = 1;
1504
+ renderPage(currentPage);
1382
1505
  },
1383
1506
  rotateCW: () => {
1384
1507
  rotation = (rotation + 90) % 360;
1385
- iframe.style.transform = `scale(${currentZoom}) rotate(${rotation}deg)`;
1508
+ renderPage(currentPage);
1386
1509
  },
1387
1510
  rotateCCW: () => {
1388
1511
  rotation = (rotation - 90 + 360) % 360;
1389
- iframe.style.transform = `scale(${currentZoom}) rotate(${rotation}deg)`;
1512
+ renderPage(currentPage);
1390
1513
  },
1391
1514
  getRotation: () => rotation,
1515
+ getPageCount: () => totalPages,
1516
+ getCurrentPage: () => currentPage,
1392
1517
  goToPage: (page) => {
1393
- currentPage = page;
1394
- iframe.src = `${url}#page=${page}`;
1518
+ renderPage(page);
1395
1519
  },
1396
- getCurrentPage: () => currentPage,
1397
1520
  download: () => {
1521
+ const blob = new Blob([ctx.buffer], { type: "application/pdf" });
1522
+ const url = URL.createObjectURL(blob);
1398
1523
  const a = document.createElement("a");
1399
1524
  a.href = url;
1400
1525
  a.download = ctx.metadata.name || "document.pdf";
1401
1526
  a.click();
1527
+ URL.revokeObjectURL(url);
1402
1528
  },
1403
1529
  print: () => {
1404
- iframe.contentWindow?.print();
1530
+ const blob = new Blob([ctx.buffer], { type: "application/pdf" });
1531
+ const url = URL.createObjectURL(blob);
1532
+ const hiddenIframe = document.createElement("iframe");
1533
+ hiddenIframe.style.position = "fixed";
1534
+ hiddenIframe.style.right = "0";
1535
+ hiddenIframe.style.bottom = "0";
1536
+ hiddenIframe.style.width = "0";
1537
+ hiddenIframe.style.height = "0";
1538
+ hiddenIframe.style.border = "0";
1539
+ document.body.appendChild(hiddenIframe);
1540
+ hiddenIframe.src = url;
1541
+ hiddenIframe.onload = () => {
1542
+ setTimeout(() => {
1543
+ hiddenIframe.contentWindow?.print();
1544
+ setTimeout(() => {
1545
+ hiddenIframe.remove();
1546
+ URL.revokeObjectURL(url);
1547
+ }, 1e3);
1548
+ }, 300);
1549
+ };
1405
1550
  },
1406
1551
  getThumbnails: async () => {
1407
- return [
1408
- {
1409
- index: 1,
1410
- label: "Page 1",
1411
- render: async (canvas) => {
1412
- const context = canvas.getContext("2d");
1413
- if (context) {
1414
- context.fillStyle = "#fff";
1415
- context.fillRect(0, 0, canvas.width, canvas.height);
1416
- context.fillStyle = "#333";
1417
- context.font = "12px sans-serif";
1418
- context.fillText("PDF Preview", 10, 20);
1552
+ const thumbnails = [];
1553
+ const count = Math.min(totalPages, 50);
1554
+ for (let i = 1; i <= count; i++) {
1555
+ thumbnails.push({
1556
+ index: i,
1557
+ label: `Page ${i}`,
1558
+ render: async (thumbCanvas) => {
1559
+ try {
1560
+ const p = await pdfDoc.getPage(i);
1561
+ const baseVp = p.getViewport({ scale: 1 });
1562
+ const thumbScale = (thumbCanvas.width || 120) / baseVp.width;
1563
+ const thumbVp = p.getViewport({ scale: thumbScale });
1564
+ thumbCanvas.height = Math.floor(thumbVp.height);
1565
+ const tCtx = thumbCanvas.getContext("2d");
1566
+ if (tCtx) {
1567
+ await p.render({ canvasContext: tCtx, viewport: thumbVp }).promise;
1568
+ }
1569
+ } catch (e) {
1570
+ console.warn(`[PdfPlugin] Error generating thumbnail for page ${i}:`, e);
1419
1571
  }
1420
1572
  }
1421
- }
1422
- ];
1573
+ });
1574
+ }
1575
+ return thumbnails;
1423
1576
  }
1424
1577
  };
1578
+ return instance;
1425
1579
  }
1426
1580
  };
1427
1581
  function pdfPlugin() {
@@ -1748,7 +1902,31 @@ var DocxPlugin = class {
1748
1902
  return this.mimeTypes.includes(mime || "");
1749
1903
  }
1750
1904
  getToolbarActions(instance) {
1751
- return [
1905
+ const totalPages = instance.getPageCount?.() ?? 1;
1906
+ const actions = [];
1907
+ if (totalPages > 1) {
1908
+ actions.push({
1909
+ id: "page-nav",
1910
+ icon: "",
1911
+ label: "Page Navigation",
1912
+ type: "page-nav",
1913
+ group: "navigation",
1914
+ value: instance.getCurrentPage?.() ?? 1,
1915
+ max: totalPages,
1916
+ execute: (action, page) => {
1917
+ const cur = instance.getCurrentPage?.() ?? 1;
1918
+ const max = instance.getPageCount?.() ?? 1;
1919
+ if (action === "prev") {
1920
+ if (cur > 1) instance.goToPage?.(cur - 1);
1921
+ } else if (action === "next") {
1922
+ if (cur < max) instance.goToPage?.(cur + 1);
1923
+ } else if (typeof page === "number") {
1924
+ instance.goToPage?.(page);
1925
+ }
1926
+ }
1927
+ });
1928
+ }
1929
+ actions.push(
1752
1930
  {
1753
1931
  id: "zoom-out",
1754
1932
  icon: "zoom-out",
@@ -1789,7 +1967,8 @@ var DocxPlugin = class {
1789
1967
  group: "actions",
1790
1968
  execute: () => instance.print?.()
1791
1969
  }
1792
- ];
1970
+ );
1971
+ return actions;
1793
1972
  }
1794
1973
  async render(ctx) {
1795
1974
  const wrapper = document.createElement("div");
@@ -1865,7 +2044,51 @@ var DocxPlugin = class {
1865
2044
  }
1866
2045
  }
1867
2046
  }
2047
+ const sections = wrapper.querySelectorAll("section.docx");
2048
+ const cards = wrapper.querySelectorAll(".fp-docx-page-card");
2049
+ const pageElements = sections.length > 0 ? sections : cards;
2050
+ const totalPages = Math.max(1, pageElements.length);
2051
+ let currentPage = 1;
2052
+ let indicator = null;
2053
+ if (totalPages > 1) {
2054
+ indicator = document.createElement("div");
2055
+ indicator.className = "fp-docx-page-indicator";
2056
+ indicator.style.position = "sticky";
2057
+ indicator.style.bottom = "16px";
2058
+ indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
2059
+ indicator.style.backdropFilter = "blur(8px)";
2060
+ indicator.style.color = "#f8fafc";
2061
+ indicator.style.fontSize = "12px";
2062
+ indicator.style.fontWeight = "600";
2063
+ indicator.style.padding = "5px 14px";
2064
+ indicator.style.borderRadius = "20px";
2065
+ indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
2066
+ indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
2067
+ indicator.style.zIndex = "10";
2068
+ indicator.style.userSelect = "none";
2069
+ indicator.style.pointerEvents = "none";
2070
+ indicator.style.textAlign = "center";
2071
+ indicator.style.width = "fit-content";
2072
+ indicator.style.margin = "16px auto 0";
2073
+ ctx.container.appendChild(indicator);
2074
+ }
2075
+ const showPage = (pageNum) => {
2076
+ currentPage = Math.max(1, Math.min(totalPages, pageNum));
2077
+ if (pageElements.length > 1) {
2078
+ pageElements.forEach((sec, idx) => {
2079
+ sec.style.display = idx + 1 === currentPage ? "block" : "none";
2080
+ });
2081
+ }
2082
+ if (indicator) {
2083
+ indicator.textContent = `Page ${currentPage} of ${totalPages}`;
2084
+ }
2085
+ ctx.emit("page-change", { page: currentPage, total: totalPages });
2086
+ };
2087
+ if (totalPages > 1) {
2088
+ showPage(1);
2089
+ }
1868
2090
  const cleanup = () => {
2091
+ indicator?.remove();
1869
2092
  for (const url of createdBlobUrls) {
1870
2093
  URL.revokeObjectURL(url);
1871
2094
  }
@@ -1876,6 +2099,11 @@ var DocxPlugin = class {
1876
2099
  ctx.signal.addEventListener("abort", cleanup);
1877
2100
  return {
1878
2101
  destroy: cleanup,
2102
+ getPageCount: () => totalPages,
2103
+ getCurrentPage: () => currentPage,
2104
+ goToPage: (page) => {
2105
+ showPage(page);
2106
+ },
1879
2107
  zoomIn: () => {
1880
2108
  scale += 0.1;
1881
2109
  wrapper.style.transform = `scale(${scale})`;
@@ -2140,7 +2368,31 @@ var ExcelPlugin = class {
2140
2368
  return this.mimeTypes.includes(mime || "");
2141
2369
  }
2142
2370
  getToolbarActions(instance) {
2143
- return [
2371
+ const totalSheets = instance.getPageCount?.() ?? 1;
2372
+ const actions = [];
2373
+ if (totalSheets > 1) {
2374
+ actions.push({
2375
+ id: "page-nav",
2376
+ icon: "",
2377
+ label: "Sheet Navigation",
2378
+ type: "page-nav",
2379
+ group: "navigation",
2380
+ value: instance.getCurrentPage?.() ?? 1,
2381
+ max: totalSheets,
2382
+ execute: (action, sheet) => {
2383
+ const cur = instance.getCurrentPage?.() ?? 1;
2384
+ const max = instance.getPageCount?.() ?? 1;
2385
+ if (action === "prev") {
2386
+ if (cur > 1) instance.goToPage?.(cur - 1);
2387
+ } else if (action === "next") {
2388
+ if (cur < max) instance.goToPage?.(cur + 1);
2389
+ } else if (typeof sheet === "number") {
2390
+ instance.goToPage?.(sheet);
2391
+ }
2392
+ }
2393
+ });
2394
+ }
2395
+ actions.push(
2144
2396
  {
2145
2397
  id: "zoom-out",
2146
2398
  icon: "zoom-out",
@@ -2161,16 +2413,6 @@ var ExcelPlugin = class {
2161
2413
  instance.zoomIn?.();
2162
2414
  }
2163
2415
  },
2164
- {
2165
- id: "page-nav",
2166
- icon: "page-nav",
2167
- label: "Sheet Navigation",
2168
- type: "page-nav",
2169
- group: "navigation",
2170
- execute: (sheet) => {
2171
- if (typeof sheet === "number") instance.goToPage?.(sheet);
2172
- }
2173
- },
2174
2416
  {
2175
2417
  id: "download",
2176
2418
  icon: "download",
@@ -2191,7 +2433,8 @@ var ExcelPlugin = class {
2191
2433
  instance.print?.();
2192
2434
  }
2193
2435
  }
2194
- ];
2436
+ );
2437
+ return actions;
2195
2438
  }
2196
2439
  async render(ctx) {
2197
2440
  const container = document.createElement("div");
@@ -2275,6 +2518,7 @@ var ExcelPlugin = class {
2275
2518
  b.style.fontWeight = "normal";
2276
2519
  }
2277
2520
  });
2521
+ ctx.emit("page-change", { page: currentSheetIndex, total: sheetNames.length });
2278
2522
  };
2279
2523
  if (sheetNames.length > 0) {
2280
2524
  sheetNames.forEach((name, idx) => {
@@ -2524,7 +2768,31 @@ var CodePlugin = class {
2524
2768
  return false;
2525
2769
  }
2526
2770
  getToolbarActions(instance) {
2527
- return [
2771
+ const totalPages = instance.getPageCount?.() ?? 1;
2772
+ const actions = [];
2773
+ if (totalPages > 1) {
2774
+ actions.push({
2775
+ id: "page-nav",
2776
+ icon: "",
2777
+ label: "Page Navigation",
2778
+ type: "page-nav",
2779
+ group: "navigation",
2780
+ value: instance.getCurrentPage?.() ?? 1,
2781
+ max: totalPages,
2782
+ execute: (action, page) => {
2783
+ const cur = instance.getCurrentPage?.() ?? 1;
2784
+ const max = instance.getPageCount?.() ?? 1;
2785
+ if (action === "prev") {
2786
+ if (cur > 1) instance.goToPage?.(cur - 1);
2787
+ } else if (action === "next") {
2788
+ if (cur < max) instance.goToPage?.(cur + 1);
2789
+ } else if (typeof page === "number") {
2790
+ instance.goToPage?.(page);
2791
+ }
2792
+ }
2793
+ });
2794
+ }
2795
+ actions.push(
2528
2796
  {
2529
2797
  id: "zoom-out",
2530
2798
  icon: "zoom-out",
@@ -2575,11 +2843,16 @@ var CodePlugin = class {
2575
2843
  instance.print?.();
2576
2844
  }
2577
2845
  }
2578
- ];
2846
+ );
2847
+ return actions;
2579
2848
  }
2580
2849
  async render(ctx) {
2581
2850
  const decoder = new TextDecoder("utf-8");
2582
- const text = decoder.decode(ctx.buffer);
2851
+ const fullText = decoder.decode(ctx.buffer);
2852
+ const pageSplitRegex = /(?:\f|\x0C|(?:\r?\n|^)\s*[-=_]{3,}\s*(?:PAGE|Page|page break|Page Break)[\s\d\w-]*[-=_]{3,}\s*(?:\r?\n|$))/i;
2853
+ const rawPages = fullText.split(pageSplitRegex).map((p) => p.trim()).filter((p) => p.length > 0);
2854
+ const totalPages = Math.max(1, rawPages.length);
2855
+ let currentPage = 1;
2583
2856
  const container = document.createElement("div");
2584
2857
  container.style.width = "100%";
2585
2858
  container.style.height = "100%";
@@ -2598,25 +2871,66 @@ var CodePlugin = class {
2598
2871
  pre.style.wordBreak = "break-all";
2599
2872
  const code = document.createElement("code");
2600
2873
  const ext = (ctx.metadata.extension || "").replace(".", "");
2601
- try {
2602
- if (ext && hljs__default.default.getLanguage(ext)) {
2603
- code.innerHTML = hljs__default.default.highlight(text, { language: ext }).value;
2604
- } else {
2605
- code.innerHTML = hljs__default.default.highlightAuto(text).value;
2874
+ const renderCodePage = (text) => {
2875
+ try {
2876
+ if (ext && hljs__default.default.getLanguage(ext)) {
2877
+ code.innerHTML = hljs__default.default.highlight(text, { language: ext }).value;
2878
+ } else {
2879
+ code.innerHTML = hljs__default.default.highlightAuto(text).value;
2880
+ }
2881
+ } catch {
2882
+ code.textContent = text;
2606
2883
  }
2607
- } catch {
2608
- code.textContent = text;
2609
- }
2884
+ };
2885
+ renderCodePage(rawPages[0] || fullText);
2610
2886
  pre.appendChild(code);
2611
2887
  container.appendChild(pre);
2888
+ let indicator = null;
2889
+ if (totalPages > 1) {
2890
+ indicator = document.createElement("div");
2891
+ indicator.className = "fp-code-page-indicator";
2892
+ indicator.style.position = "sticky";
2893
+ indicator.style.bottom = "16px";
2894
+ indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
2895
+ indicator.style.backdropFilter = "blur(8px)";
2896
+ indicator.style.color = "#f8fafc";
2897
+ indicator.style.fontSize = "12px";
2898
+ indicator.style.fontWeight = "600";
2899
+ indicator.style.padding = "5px 14px";
2900
+ indicator.style.borderRadius = "20px";
2901
+ indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
2902
+ indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
2903
+ indicator.style.zIndex = "10";
2904
+ indicator.style.userSelect = "none";
2905
+ indicator.style.pointerEvents = "none";
2906
+ indicator.style.textAlign = "center";
2907
+ indicator.style.width = "fit-content";
2908
+ indicator.style.margin = "16px auto 0";
2909
+ container.appendChild(indicator);
2910
+ }
2911
+ const showPage = (pageNum) => {
2912
+ currentPage = Math.max(1, Math.min(totalPages, pageNum));
2913
+ renderCodePage(rawPages[currentPage - 1] || fullText);
2914
+ if (indicator) {
2915
+ indicator.textContent = `Page ${currentPage} of ${totalPages}`;
2916
+ }
2917
+ ctx.emit("page-change", { page: currentPage, total: totalPages });
2918
+ };
2919
+ if (totalPages > 1) {
2920
+ showPage(1);
2921
+ }
2612
2922
  ctx.container.appendChild(container);
2613
2923
  const cleanup = () => {
2924
+ indicator?.remove();
2614
2925
  container.remove();
2615
2926
  ctx.container.innerHTML = "";
2616
2927
  };
2617
2928
  ctx.signal.addEventListener("abort", cleanup);
2618
2929
  return {
2619
2930
  destroy: cleanup,
2931
+ getPageCount: () => totalPages,
2932
+ getCurrentPage: () => currentPage,
2933
+ goToPage: (page) => showPage(page),
2620
2934
  zoomIn: () => {
2621
2935
  fontSize = Math.min(32, fontSize + 2);
2622
2936
  pre.style.fontSize = `${fontSize}px`;
@@ -2644,7 +2958,7 @@ var CodePlugin = class {
2644
2958
  window.print();
2645
2959
  },
2646
2960
  copy: () => {
2647
- navigator.clipboard?.writeText(text);
2961
+ navigator.clipboard?.writeText(fullText);
2648
2962
  }
2649
2963
  };
2650
2964
  }
@@ -3466,7 +3780,31 @@ var RtfPlugin = class {
3466
3780
  return this.extensions.includes(ext || "") || this.mimeTypes.includes(mime || "");
3467
3781
  }
3468
3782
  getToolbarActions(instance) {
3469
- return [
3783
+ const totalPages = instance.getPageCount?.() ?? 1;
3784
+ const actions = [];
3785
+ if (totalPages > 1) {
3786
+ actions.push({
3787
+ id: "page-nav",
3788
+ icon: "",
3789
+ label: "Page Navigation",
3790
+ type: "page-nav",
3791
+ group: "navigation",
3792
+ value: instance.getCurrentPage?.() ?? 1,
3793
+ max: totalPages,
3794
+ execute: (action, page) => {
3795
+ const cur = instance.getCurrentPage?.() ?? 1;
3796
+ const max = instance.getPageCount?.() ?? 1;
3797
+ if (action === "prev") {
3798
+ if (cur > 1) instance.goToPage?.(cur - 1);
3799
+ } else if (action === "next") {
3800
+ if (cur < max) instance.goToPage?.(cur + 1);
3801
+ } else if (typeof page === "number") {
3802
+ instance.goToPage?.(page);
3803
+ }
3804
+ }
3805
+ });
3806
+ }
3807
+ actions.push(
3470
3808
  {
3471
3809
  id: "zoom-out",
3472
3810
  icon: "zoom-out",
@@ -3507,7 +3845,8 @@ var RtfPlugin = class {
3507
3845
  group: "actions",
3508
3846
  execute: () => instance.print?.()
3509
3847
  }
3510
- ];
3848
+ );
3849
+ return actions;
3511
3850
  }
3512
3851
  async render(ctx) {
3513
3852
  const wrapper = document.createElement("div");
@@ -3526,28 +3865,82 @@ var RtfPlugin = class {
3526
3865
  ctx.container.style.backgroundColor = "#f1f5f9";
3527
3866
  ctx.container.appendChild(wrapper);
3528
3867
  let scale = 1;
3868
+ let pageElements = [];
3529
3869
  try {
3530
3870
  if (typeof RTFJS__namespace.loggingEnabled === "function") {
3531
3871
  RTFJS__namespace.loggingEnabled(false);
3532
3872
  }
3533
3873
  const doc = new RTFJS__namespace.Document(ctx.buffer, {});
3534
3874
  const htmlElements = await doc.render();
3535
- for (const el of htmlElements) {
3875
+ pageElements = htmlElements;
3876
+ for (let i = 0; i < htmlElements.length; i++) {
3877
+ const el = htmlElements[i];
3878
+ el.style.display = i === 0 ? "block" : "none";
3536
3879
  wrapper.appendChild(el);
3537
3880
  }
3538
3881
  } catch (err) {
3539
3882
  console.warn("[RtfPlugin] RTF render error, fallback text:", err);
3540
3883
  const text = new TextDecoder("latin1").decode(ctx.buffer);
3541
3884
  const clean = text.replace(/\\par[d]?/g, "\n").replace(/\\[a-zA-Z0-9\-]+/g, "").replace(/[{}]/g, "");
3542
- wrapper.innerHTML = `<pre style="white-space: pre-wrap; font-family: serif; color: #333;">${clean}</pre>`;
3885
+ const pre = document.createElement("pre");
3886
+ pre.style.whiteSpace = "pre-wrap";
3887
+ pre.style.fontFamily = "serif";
3888
+ pre.style.color = "#333";
3889
+ pre.textContent = clean;
3890
+ wrapper.appendChild(pre);
3891
+ pageElements = [pre];
3892
+ }
3893
+ const totalPages = Math.max(1, pageElements.length);
3894
+ let currentPage = 1;
3895
+ let indicator = null;
3896
+ if (totalPages > 1) {
3897
+ indicator = document.createElement("div");
3898
+ indicator.className = "fp-rtf-page-indicator";
3899
+ indicator.style.position = "sticky";
3900
+ indicator.style.bottom = "16px";
3901
+ indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
3902
+ indicator.style.backdropFilter = "blur(8px)";
3903
+ indicator.style.color = "#f8fafc";
3904
+ indicator.style.fontSize = "12px";
3905
+ indicator.style.fontWeight = "600";
3906
+ indicator.style.padding = "5px 14px";
3907
+ indicator.style.borderRadius = "20px";
3908
+ indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
3909
+ indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
3910
+ indicator.style.zIndex = "10";
3911
+ indicator.style.userSelect = "none";
3912
+ indicator.style.pointerEvents = "none";
3913
+ indicator.style.textAlign = "center";
3914
+ indicator.style.width = "fit-content";
3915
+ indicator.style.margin = "16px auto 0";
3916
+ ctx.container.appendChild(indicator);
3917
+ }
3918
+ const showPage = (pageNum) => {
3919
+ currentPage = Math.max(1, Math.min(totalPages, pageNum));
3920
+ if (totalPages > 1) {
3921
+ pageElements.forEach((el, idx) => {
3922
+ el.style.display = idx + 1 === currentPage ? "block" : "none";
3923
+ });
3924
+ }
3925
+ if (indicator) {
3926
+ indicator.textContent = `Page ${currentPage} of ${totalPages}`;
3927
+ }
3928
+ ctx.emit("page-change", { page: currentPage, total: totalPages });
3929
+ };
3930
+ if (totalPages > 1) {
3931
+ showPage(1);
3543
3932
  }
3544
3933
  const cleanup = () => {
3934
+ indicator?.remove();
3545
3935
  wrapper.remove();
3546
3936
  ctx.container.innerHTML = "";
3547
3937
  };
3548
3938
  ctx.signal.addEventListener("abort", cleanup);
3549
3939
  return {
3550
3940
  destroy: cleanup,
3941
+ getPageCount: () => totalPages,
3942
+ getCurrentPage: () => currentPage,
3943
+ goToPage: (page) => showPage(page),
3551
3944
  zoomIn: () => {
3552
3945
  scale += 0.1;
3553
3946
  wrapper.style.transform = `scale(${scale})`;
@@ -3735,41 +4128,41 @@ var OpenDocumentPlugin = class {
3735
4128
  }
3736
4129
  getToolbarActions(instance) {
3737
4130
  const isPresentation = instance.isPresentation;
4131
+ const totalPages = instance.getPageCount?.() ?? 1;
3738
4132
  const actions = [];
3739
- if (isPresentation) {
3740
- actions.push(
3741
- {
4133
+ if (isPresentation || totalPages > 1) {
4134
+ if (isPresentation) {
4135
+ actions.push({
3742
4136
  id: "thumbnails",
3743
4137
  icon: "thumbnails",
3744
4138
  label: "Slide Thumbnails",
3745
4139
  type: "button",
3746
4140
  group: "navigation",
3747
4141
  execute: () => instance.toggleThumbnails?.()
3748
- },
3749
- {
3750
- id: "page-nav",
3751
- icon: "",
3752
- label: "Slide Navigation",
3753
- type: "page-nav",
3754
- group: "navigation",
3755
- value: instance.getCurrentPage?.() ?? 1,
3756
- max: instance.getPageCount?.() ?? 1,
3757
- execute: (action, page) => {
3758
- if (action === "prev") {
3759
- const cur = instance.getCurrentPage?.() ?? 1;
3760
- if (cur > 1) instance.goToPage?.(cur - 1);
3761
- } else if (action === "next") {
3762
- const cur = instance.getCurrentPage?.() ?? 1;
3763
- const total = instance.getPageCount?.() ?? 1;
3764
- if (cur < total) instance.goToPage?.(cur + 1);
3765
- } else if (typeof page === "number") {
3766
- instance.goToPage?.(page);
3767
- } else if (typeof action === "number") {
3768
- instance.goToPage?.(action);
3769
- }
4142
+ });
4143
+ }
4144
+ actions.push({
4145
+ id: "page-nav",
4146
+ icon: "",
4147
+ label: isPresentation ? "Slide Navigation" : "Page Navigation",
4148
+ type: "page-nav",
4149
+ group: "navigation",
4150
+ value: instance.getCurrentPage?.() ?? 1,
4151
+ max: totalPages,
4152
+ execute: (action, page) => {
4153
+ const cur = instance.getCurrentPage?.() ?? 1;
4154
+ const max = instance.getPageCount?.() ?? 1;
4155
+ if (action === "prev") {
4156
+ if (cur > 1) instance.goToPage?.(cur - 1);
4157
+ } else if (action === "next") {
4158
+ if (cur < max) instance.goToPage?.(cur + 1);
4159
+ } else if (typeof page === "number") {
4160
+ instance.goToPage?.(page);
4161
+ } else if (typeof action === "number") {
4162
+ instance.goToPage?.(action);
3770
4163
  }
3771
4164
  }
3772
- );
4165
+ });
3773
4166
  }
3774
4167
  actions.push(
3775
4168
  {
@@ -4152,7 +4545,31 @@ var DocPlugin = class {
4152
4545
  return this.mimeTypes.includes(mime || "");
4153
4546
  }
4154
4547
  getToolbarActions(instance) {
4155
- return [
4548
+ const totalPages = instance.getPageCount?.() ?? 1;
4549
+ const actions = [];
4550
+ if (totalPages > 1) {
4551
+ actions.push({
4552
+ id: "page-nav",
4553
+ icon: "",
4554
+ label: "Page Navigation",
4555
+ type: "page-nav",
4556
+ group: "navigation",
4557
+ value: instance.getCurrentPage?.() ?? 1,
4558
+ max: totalPages,
4559
+ execute: (action, page) => {
4560
+ const cur = instance.getCurrentPage?.() ?? 1;
4561
+ const max = instance.getPageCount?.() ?? 1;
4562
+ if (action === "prev") {
4563
+ if (cur > 1) instance.goToPage?.(cur - 1);
4564
+ } else if (action === "next") {
4565
+ if (cur < max) instance.goToPage?.(cur + 1);
4566
+ } else if (typeof page === "number") {
4567
+ instance.goToPage?.(page);
4568
+ }
4569
+ }
4570
+ });
4571
+ }
4572
+ actions.push(
4156
4573
  {
4157
4574
  id: "zoom-out",
4158
4575
  icon: "zoom-out",
@@ -4201,7 +4618,8 @@ var DocPlugin = class {
4201
4618
  group: "actions",
4202
4619
  execute: () => instance.print?.()
4203
4620
  }
4204
- ];
4621
+ );
4622
+ return actions;
4205
4623
  }
4206
4624
  async render(ctx) {
4207
4625
  const container = document.createElement("div");
@@ -4228,6 +4646,7 @@ var DocPlugin = class {
4228
4646
  ctx.container.appendChild(container);
4229
4647
  let scale = 1;
4230
4648
  let extractedRawText = "";
4649
+ let isFallback = false;
4231
4650
  try {
4232
4651
  const cfbf = new CfbfReader(ctx.buffer);
4233
4652
  const wordDocStream = cfbf.readStream("WordDocument");
@@ -4241,26 +4660,89 @@ var DocPlugin = class {
4241
4660
  const tableStream = cfbf.readStream(tableName);
4242
4661
  const text = this.extractDocText(wordDocStream, tableStream);
4243
4662
  extractedRawText = text;
4244
- wrapper.innerHTML = this.formatDocToHtml(text, ctx.metadata.name || "Document");
4245
4663
  } catch (err) {
4246
4664
  console.warn("[DocPlugin] Binary parsing error, fallback text:", err);
4247
4665
  const fallback = this.heuristicTextExtraction(ctx.buffer);
4248
4666
  extractedRawText = fallback;
4249
- wrapper.innerHTML = `
4250
- <div style="border-bottom: 1px solid #e2e8f0; padding-bottom: 12px; margin-bottom: 24px;">
4251
- <h2 style="margin: 0 0 6px; font-size: 20px; color: #334155;">${ctx.metadata.name || "Word Document (.doc)"}</h2>
4252
- <span style="font-size: 12px; color: #64748b; background: #f1f5f9; padding: 2px 8px; border-radius: 4px;">Legacy Word 97-2003 Binary Preview</span>
4253
- </div>
4254
- ${this.formatDocToHtml(fallback, ctx.metadata.name || "Document")}
4255
- `;
4667
+ isFallback = true;
4668
+ }
4669
+ const rawPages = this.splitIntoPages(extractedRawText);
4670
+ const totalPages = Math.max(1, rawPages.length);
4671
+ let currentPage = 1;
4672
+ wrapper.innerHTML = "";
4673
+ const pageCards = [];
4674
+ for (let i = 0; i < totalPages; i++) {
4675
+ const pageCard = document.createElement("div");
4676
+ pageCard.className = "fp-doc-page-card";
4677
+ pageCard.style.backgroundColor = "#ffffff";
4678
+ pageCard.style.boxShadow = "0 2px 12px rgba(0,0,0,0.08)";
4679
+ pageCard.style.borderRadius = "4px";
4680
+ pageCard.style.padding = "56px 48px";
4681
+ pageCard.style.minHeight = "100%";
4682
+ pageCard.style.display = i === 0 ? "block" : "none";
4683
+ if (isFallback && i === 0) {
4684
+ pageCard.innerHTML = `
4685
+ <div style="border-bottom: 1px solid #e2e8f0; padding-bottom: 12px; margin-bottom: 24px;">
4686
+ <h2 style="margin: 0 0 6px; font-size: 20px; color: #334155;">${DOMPurify2__default.default.sanitize(ctx.metadata.name || "Word Document (.doc)")}</h2>
4687
+ <span style="font-size: 12px; color: #64748b; background: #f1f5f9; padding: 2px 8px; border-radius: 4px;">Legacy Word 97-2003 Binary Preview</span>
4688
+ </div>
4689
+ ${this.formatDocToHtml(rawPages[i], ctx.metadata.name || "Document")}
4690
+ `;
4691
+ } else {
4692
+ pageCard.innerHTML = this.formatDocToHtml(rawPages[i], ctx.metadata.name || "Document");
4693
+ }
4694
+ wrapper.appendChild(pageCard);
4695
+ pageCards.push(pageCard);
4696
+ }
4697
+ let indicator = null;
4698
+ if (totalPages > 1) {
4699
+ indicator = document.createElement("div");
4700
+ indicator.className = "fp-doc-page-indicator";
4701
+ indicator.style.position = "sticky";
4702
+ indicator.style.bottom = "16px";
4703
+ indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
4704
+ indicator.style.backdropFilter = "blur(8px)";
4705
+ indicator.style.color = "#f8fafc";
4706
+ indicator.style.fontSize = "12px";
4707
+ indicator.style.fontWeight = "600";
4708
+ indicator.style.padding = "5px 14px";
4709
+ indicator.style.borderRadius = "20px";
4710
+ indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
4711
+ indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
4712
+ indicator.style.zIndex = "10";
4713
+ indicator.style.userSelect = "none";
4714
+ indicator.style.pointerEvents = "none";
4715
+ indicator.style.textAlign = "center";
4716
+ indicator.style.width = "fit-content";
4717
+ indicator.style.margin = "16px auto 0";
4718
+ container.appendChild(indicator);
4719
+ }
4720
+ const showPage = (pageNum) => {
4721
+ currentPage = Math.max(1, Math.min(totalPages, pageNum));
4722
+ if (totalPages > 1) {
4723
+ pageCards.forEach((card, idx) => {
4724
+ card.style.display = idx + 1 === currentPage ? "block" : "none";
4725
+ });
4726
+ }
4727
+ if (indicator) {
4728
+ indicator.textContent = `Page ${currentPage} of ${totalPages}`;
4729
+ }
4730
+ ctx.emit("page-change", { page: currentPage, total: totalPages });
4731
+ };
4732
+ if (totalPages > 1) {
4733
+ showPage(1);
4256
4734
  }
4257
4735
  const cleanup = () => {
4736
+ indicator?.remove();
4258
4737
  container.remove();
4259
4738
  ctx.container.innerHTML = "";
4260
4739
  };
4261
4740
  ctx.signal.addEventListener("abort", cleanup);
4262
4741
  return {
4263
4742
  destroy: cleanup,
4743
+ getPageCount: () => totalPages,
4744
+ getCurrentPage: () => currentPage,
4745
+ goToPage: (page) => showPage(page),
4264
4746
  zoomIn: () => {
4265
4747
  scale += 0.1;
4266
4748
  wrapper.style.transform = `scale(${scale})`;
@@ -4392,11 +4874,16 @@ var DocPlugin = class {
4392
4874
  heuristicTextExtraction(buffer) {
4393
4875
  return this.extractStringsFromBytes(new Uint8Array(buffer));
4394
4876
  }
4877
+ splitIntoPages(text) {
4878
+ if (!text) return [""];
4879
+ 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);
4880
+ return parts.length > 0 ? parts : [text];
4881
+ }
4395
4882
  /**
4396
4883
  * Format cleaned extracted text into attractive HTML paragraphs, headings, and lists
4397
4884
  */
4398
4885
  formatDocToHtml(text, filename) {
4399
- const lines = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\x0B/g, "\n").replace(/\x0C/g, "\n\n").split("\n");
4886
+ const lines = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\x0B/g, "\n").split("\n");
4400
4887
  let html = "";
4401
4888
  let inList = false;
4402
4889
  for (const rawLine of lines) {