@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/index.cjs CHANGED
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var DOMPurify2 = require('dompurify');
4
+ var pdfjsLib = require('pdfjs-dist');
4
5
  var docx = require('docx-preview');
5
6
  var fflate = require('fflate');
6
7
  var XLSX = require('xlsx');
@@ -34,6 +35,7 @@ function _interopNamespace(e) {
34
35
  }
35
36
 
36
37
  var DOMPurify2__default = /*#__PURE__*/_interopDefault(DOMPurify2);
38
+ var pdfjsLib__namespace = /*#__PURE__*/_interopNamespace(pdfjsLib);
37
39
  var docx__namespace = /*#__PURE__*/_interopNamespace(docx);
38
40
  var XLSX__namespace = /*#__PURE__*/_interopNamespace(XLSX);
39
41
  var hljs__default = /*#__PURE__*/_interopDefault(hljs);
@@ -551,11 +553,21 @@ var ToolbarController = class {
551
553
  el;
552
554
  toolbarEl;
553
555
  actions = [];
556
+ pageInputEl = null;
557
+ pageLabelEl = null;
554
558
  constructor(container) {
555
559
  this.el = container;
556
560
  this.toolbarEl = createElement("div", { className: "fp-toolbar" });
557
561
  this.el.appendChild(this.toolbarEl);
558
562
  }
563
+ setPage(page, max) {
564
+ if (this.pageInputEl) {
565
+ this.pageInputEl.value = page.toString();
566
+ }
567
+ if (max !== void 0 && this.pageLabelEl) {
568
+ this.pageLabelEl.textContent = ` / ${max}`;
569
+ }
570
+ }
559
571
  update(actions) {
560
572
  this.actions = actions;
561
573
  this.render();
@@ -598,6 +610,7 @@ var ToolbarController = class {
598
610
  if (action.type === "separator") {
599
611
  groupEl.appendChild(createElement("div", { className: "fp-toolbar-separator" }));
600
612
  } else if (action.type === "page-nav") {
613
+ const max = action.max ?? 1;
601
614
  const prevBtn = this.createButton(
602
615
  "prev",
603
616
  ICON_PAGE_PREV,
@@ -616,7 +629,6 @@ var ToolbarController = class {
616
629
  "Next Page",
617
630
  () => {
618
631
  const cur = parseInt(input.value, 10) || 1;
619
- const max = action.max ?? 1;
620
632
  if (cur < max) {
621
633
  input.value = (cur + 1).toString();
622
634
  action.execute("next", cur + 1);
@@ -628,15 +640,18 @@ var ToolbarController = class {
628
640
  type: "number",
629
641
  value: (action.value ?? 1).toString(),
630
642
  min: "1",
631
- max: (action.max ?? 1).toString()
643
+ max: max.toString()
632
644
  });
633
645
  input.addEventListener("change", () => {
634
- const val = parseInt(input.value, 10);
635
- if (!isNaN(val)) {
636
- action.execute("go", val);
637
- }
646
+ let val = parseInt(input.value, 10);
647
+ if (isNaN(val)) val = 1;
648
+ val = Math.max(1, Math.min(max, val));
649
+ input.value = val.toString();
650
+ action.execute("go", val);
638
651
  });
639
- const label = createElement("span", { className: "fp-toolbar-label" }, ` / ${action.max ?? 1}`);
652
+ const label = createElement("span", { className: "fp-toolbar-label" }, ` / ${max}`);
653
+ this.pageInputEl = input;
654
+ this.pageLabelEl = label;
640
655
  groupEl.appendChild(prevBtn);
641
656
  groupEl.appendChild(input);
642
657
  groupEl.appendChild(label);
@@ -814,7 +829,13 @@ var FilePreviewViewer = class {
814
829
  buffer,
815
830
  options,
816
831
  signal,
817
- emit: (event, payload) => this.eventEmitter.emit(event, payload)
832
+ emit: (event, payload) => {
833
+ if (event === "page-change" && payload && typeof payload.page === "number") {
834
+ const total = payload.total ?? payload.totalPages;
835
+ this.toolbar?.setPage(payload.page, total);
836
+ }
837
+ this.eventEmitter.emit(event, payload);
838
+ }
818
839
  });
819
840
  this.activeInstance = instance;
820
841
  this.hideLoading();
@@ -848,6 +869,7 @@ var FilePreviewViewer = class {
848
869
  if (thumbnails && thumbnails.length > 0 && this.thumbnailPanel) {
849
870
  this.thumbnailPanel.update(thumbnails, (index) => {
850
871
  instance.goToPage?.(index + 1);
872
+ this.toolbar?.setPage(index + 1);
851
873
  });
852
874
  if (options.showThumbnails) {
853
875
  this.thumbnailPanel.show();
@@ -973,9 +995,13 @@ var FilePreviewViewer = class {
973
995
  if (e.key === "ArrowRight" || e.key === "PageDown") {
974
996
  const cur = this.activeInstance.getCurrentPage?.() ?? 1;
975
997
  this.activeInstance.goToPage?.(cur + 1);
998
+ const nextCur = this.activeInstance.getCurrentPage?.() ?? cur + 1;
999
+ this.toolbar?.setPage(nextCur);
976
1000
  } else if (e.key === "ArrowLeft" || e.key === "PageUp") {
977
1001
  const cur = this.activeInstance.getCurrentPage?.() ?? 1;
978
1002
  this.activeInstance.goToPage?.(Math.max(1, cur - 1));
1003
+ const prevCur = this.activeInstance.getCurrentPage?.() ?? Math.max(1, cur - 1);
1004
+ this.toolbar?.setPage(prevCur);
979
1005
  } else if (e.key === "+" || e.key === "=") {
980
1006
  this.activeInstance.zoomIn?.();
981
1007
  } else if (e.key === "-" || e.key === "_") {
@@ -1241,30 +1267,62 @@ var CfbfReader = class {
1241
1267
  return result.subarray(0, targetSize);
1242
1268
  }
1243
1269
  };
1244
-
1245
- // ../plugins/pdf/dist/index.js
1270
+ if (typeof window !== "undefined" && pdfjsLib__namespace.GlobalWorkerOptions) {
1271
+ if (!pdfjsLib__namespace.GlobalWorkerOptions.workerSrc) {
1272
+ pdfjsLib__namespace.GlobalWorkerOptions.workerSrc = `https://unpkg.com/pdfjs-dist@${pdfjsLib__namespace.version || "4.10.38"}/build/pdf.worker.min.mjs`;
1273
+ }
1274
+ }
1246
1275
  var PdfPlugin = class {
1247
1276
  id = "pdf";
1248
- name = "PDF Preview";
1277
+ name = "PDF Document Preview";
1249
1278
  extensions = [".pdf"];
1250
1279
  mimeTypes = ["application/pdf"];
1251
1280
  weight = 100;
1252
1281
  supports(file) {
1253
1282
  const ext = file.metadata.extension?.toLowerCase();
1254
1283
  const mime = file.metadata.mimeType?.toLowerCase();
1255
- return ext === ".pdf" || mime === "application/pdf";
1284
+ if (ext) return this.extensions.includes(ext);
1285
+ return this.mimeTypes.includes(mime || "");
1256
1286
  }
1257
1287
  getToolbarActions(instance) {
1288
+ const totalPages = instance.getPageCount?.() ?? 1;
1289
+ const curPage = instance.getCurrentPage?.() ?? 1;
1258
1290
  return [
1291
+ {
1292
+ id: "thumbnails",
1293
+ icon: "thumbnails",
1294
+ label: "Page Thumbnails",
1295
+ type: "button",
1296
+ group: "navigation",
1297
+ execute: () => instance.toggleThumbnails?.()
1298
+ },
1299
+ {
1300
+ id: "page-nav",
1301
+ icon: "",
1302
+ label: "Page Navigation",
1303
+ type: "page-nav",
1304
+ group: "navigation",
1305
+ value: curPage,
1306
+ max: totalPages,
1307
+ execute: (action, page) => {
1308
+ const cur = instance.getCurrentPage?.() ?? 1;
1309
+ const max = instance.getPageCount?.() ?? 1;
1310
+ if (action === "prev") {
1311
+ if (cur > 1) instance.goToPage?.(cur - 1);
1312
+ } else if (action === "next") {
1313
+ if (cur < max) instance.goToPage?.(cur + 1);
1314
+ } else if (typeof page === "number") {
1315
+ instance.goToPage?.(page);
1316
+ }
1317
+ }
1318
+ },
1259
1319
  {
1260
1320
  id: "zoom-out",
1261
1321
  icon: "zoom-out",
1262
1322
  label: "Zoom Out",
1263
1323
  type: "button",
1264
1324
  group: "zoom",
1265
- execute: () => {
1266
- instance.zoomOut?.();
1267
- }
1325
+ execute: () => instance.zoomOut?.()
1268
1326
  },
1269
1327
  {
1270
1328
  id: "zoom-in",
@@ -1272,9 +1330,7 @@ var PdfPlugin = class {
1272
1330
  label: "Zoom In",
1273
1331
  type: "button",
1274
1332
  group: "zoom",
1275
- execute: () => {
1276
- instance.zoomIn?.();
1277
- }
1333
+ execute: () => instance.zoomIn?.()
1278
1334
  },
1279
1335
  {
1280
1336
  id: "fit-page",
@@ -1282,39 +1338,23 @@ var PdfPlugin = class {
1282
1338
  label: "Fit to Page",
1283
1339
  type: "button",
1284
1340
  group: "zoom",
1285
- execute: () => {
1286
- instance.fitToPage?.();
1287
- }
1341
+ execute: () => instance.fitToPage?.()
1288
1342
  },
1289
1343
  {
1290
1344
  id: "rotate-cw",
1291
1345
  icon: "rotate-cw",
1292
- label: "Rotate",
1346
+ label: "Rotate Clockwise",
1293
1347
  type: "button",
1294
1348
  group: "view",
1295
- execute: () => {
1296
- instance.rotateCW?.();
1297
- }
1298
- },
1299
- {
1300
- id: "page-nav",
1301
- icon: "page-nav",
1302
- label: "Page Navigation",
1303
- type: "page-nav",
1304
- group: "navigation",
1305
- execute: (page) => {
1306
- if (typeof page === "number") instance.goToPage?.(page);
1307
- }
1349
+ execute: () => instance.rotateCW?.()
1308
1350
  },
1309
1351
  {
1310
1352
  id: "download",
1311
1353
  icon: "download",
1312
- label: "Download",
1354
+ label: "Download PDF",
1313
1355
  type: "button",
1314
1356
  group: "actions",
1315
- execute: () => {
1316
- instance.download?.();
1317
- }
1357
+ execute: () => instance.download?.()
1318
1358
  },
1319
1359
  {
1320
1360
  id: "print",
@@ -1322,99 +1362,213 @@ var PdfPlugin = class {
1322
1362
  label: "Print",
1323
1363
  type: "button",
1324
1364
  group: "actions",
1325
- execute: () => {
1326
- instance.print?.();
1327
- }
1365
+ execute: () => instance.print?.()
1328
1366
  }
1329
1367
  ];
1330
1368
  }
1331
1369
  async render(ctx) {
1332
- const blob = new Blob([ctx.buffer], { type: "application/pdf" });
1333
- const url = URL.createObjectURL(blob);
1334
- const wrapper = document.createElement("div");
1335
- wrapper.style.width = "100%";
1336
- wrapper.style.height = "100%";
1337
- wrapper.style.overflow = "hidden";
1338
- wrapper.style.display = "flex";
1339
- wrapper.style.justifyContent = "center";
1340
- wrapper.style.alignItems = "center";
1341
- const iframe = document.createElement("iframe");
1342
- iframe.src = url;
1343
- iframe.style.width = "100%";
1344
- iframe.style.height = "100%";
1345
- iframe.style.border = "none";
1346
- wrapper.appendChild(iframe);
1347
- ctx.container.appendChild(wrapper);
1370
+ const container = document.createElement("div");
1371
+ container.className = "fp-pdf-container";
1372
+ container.style.width = "100%";
1373
+ container.style.height = "100%";
1374
+ container.style.overflow = "auto";
1375
+ container.style.display = "flex";
1376
+ container.style.flexDirection = "column";
1377
+ container.style.alignItems = "center";
1378
+ container.style.padding = "24px 16px";
1379
+ container.style.backgroundColor = "#0f172a";
1380
+ container.style.boxSizing = "border-box";
1381
+ container.style.position = "relative";
1382
+ const pageCard = document.createElement("div");
1383
+ pageCard.className = "fp-pdf-page-card";
1384
+ pageCard.style.boxShadow = "0 10px 35px rgba(0, 0, 0, 0.5)";
1385
+ pageCard.style.backgroundColor = "#ffffff";
1386
+ pageCard.style.borderRadius = "4px";
1387
+ pageCard.style.overflow = "hidden";
1388
+ pageCard.style.lineHeight = "0";
1389
+ pageCard.style.transition = "transform 0.15s ease";
1390
+ pageCard.style.position = "relative";
1391
+ const canvas = document.createElement("canvas");
1392
+ pageCard.appendChild(canvas);
1393
+ container.appendChild(pageCard);
1394
+ const indicator = document.createElement("div");
1395
+ indicator.className = "fp-pdf-page-indicator";
1396
+ indicator.style.position = "sticky";
1397
+ indicator.style.bottom = "16px";
1398
+ indicator.style.marginTop = "16px";
1399
+ indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
1400
+ indicator.style.backdropFilter = "blur(8px)";
1401
+ indicator.style.color = "#f8fafc";
1402
+ indicator.style.fontSize = "12px";
1403
+ indicator.style.fontWeight = "600";
1404
+ indicator.style.padding = "5px 14px";
1405
+ indicator.style.borderRadius = "20px";
1406
+ indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
1407
+ indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
1408
+ indicator.style.zIndex = "10";
1409
+ indicator.style.userSelect = "none";
1410
+ indicator.style.pointerEvents = "none";
1411
+ container.appendChild(indicator);
1412
+ ctx.container.appendChild(container);
1413
+ const loadingTask = pdfjsLib__namespace.getDocument({
1414
+ data: new Uint8Array(ctx.buffer),
1415
+ cMapUrl: `https://unpkg.com/pdfjs-dist@${pdfjsLib__namespace.version || "4.10.38"}/cmaps/`,
1416
+ cMapPacked: true,
1417
+ standardFontDataUrl: `https://unpkg.com/pdfjs-dist@${pdfjsLib__namespace.version || "4.10.38"}/standard_fonts/`
1418
+ });
1419
+ const pdfDoc = await loadingTask.promise;
1420
+ const totalPages = Math.max(1, pdfDoc.numPages);
1348
1421
  let currentPage = 1;
1349
- let currentZoom = 1;
1422
+ let zoomScale = 1;
1350
1423
  let rotation = 0;
1424
+ let currentRenderTask = null;
1425
+ const renderPage = async (pageNum) => {
1426
+ if (currentRenderTask) {
1427
+ try {
1428
+ currentRenderTask.cancel();
1429
+ } catch {
1430
+ }
1431
+ currentRenderTask = null;
1432
+ }
1433
+ currentPage = Math.max(1, Math.min(totalPages, pageNum));
1434
+ indicator.textContent = `Page ${currentPage} of ${totalPages}`;
1435
+ ctx.emit("page-change", { page: currentPage, total: totalPages });
1436
+ const page = await pdfDoc.getPage(currentPage);
1437
+ const containerWidth = container.clientWidth || 900;
1438
+ const unscaledVp = page.getViewport({ scale: 1, rotation });
1439
+ const baseScale = Math.min((containerWidth - 64) / unscaledVp.width, 1.6);
1440
+ const effectiveScale = (baseScale > 0 ? baseScale : 1) * zoomScale;
1441
+ const pixelRatio = window.devicePixelRatio || 1;
1442
+ const viewport = page.getViewport({ scale: effectiveScale, rotation });
1443
+ canvas.width = Math.floor(viewport.width * pixelRatio);
1444
+ canvas.height = Math.floor(viewport.height * pixelRatio);
1445
+ canvas.style.width = `${Math.floor(viewport.width)}px`;
1446
+ canvas.style.height = `${Math.floor(viewport.height)}px`;
1447
+ const canvasCtx = canvas.getContext("2d");
1448
+ if (!canvasCtx) return;
1449
+ canvasCtx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
1450
+ currentRenderTask = page.render({
1451
+ canvasContext: canvasCtx,
1452
+ viewport
1453
+ });
1454
+ try {
1455
+ await currentRenderTask.promise;
1456
+ } catch (err) {
1457
+ if (err?.name !== "RenderingCancelledException") {
1458
+ console.warn("[PdfPlugin] Page render warning:", err);
1459
+ }
1460
+ } finally {
1461
+ currentRenderTask = null;
1462
+ }
1463
+ };
1464
+ await renderPage(1);
1351
1465
  const cleanup = () => {
1352
- URL.revokeObjectURL(url);
1353
- wrapper.remove();
1466
+ if (currentRenderTask) {
1467
+ try {
1468
+ currentRenderTask.cancel();
1469
+ } catch {
1470
+ }
1471
+ }
1472
+ try {
1473
+ pdfDoc.destroy();
1474
+ } catch {
1475
+ }
1476
+ container.remove();
1354
1477
  ctx.container.innerHTML = "";
1355
1478
  };
1356
1479
  ctx.signal.addEventListener("abort", cleanup);
1357
- return {
1480
+ const instance = {
1358
1481
  destroy: cleanup,
1359
1482
  zoomIn: () => {
1360
- currentZoom += 0.1;
1361
- iframe.style.transform = `scale(${currentZoom}) rotate(${rotation}deg)`;
1483
+ zoomScale = Math.min(3.5, zoomScale + 0.2);
1484
+ renderPage(currentPage);
1362
1485
  },
1363
1486
  zoomOut: () => {
1364
- currentZoom = Math.max(0.2, currentZoom - 0.1);
1365
- iframe.style.transform = `scale(${currentZoom}) rotate(${rotation}deg)`;
1487
+ zoomScale = Math.max(0.3, zoomScale - 0.2);
1488
+ renderPage(currentPage);
1366
1489
  },
1367
- getZoom: () => currentZoom,
1490
+ getZoom: () => zoomScale,
1368
1491
  setZoom: (level) => {
1369
- currentZoom = level;
1370
- iframe.style.transform = `scale(${currentZoom}) rotate(${rotation}deg)`;
1492
+ zoomScale = Math.max(0.3, Math.min(3.5, level));
1493
+ renderPage(currentPage);
1371
1494
  },
1372
1495
  fitToPage: () => {
1373
- currentZoom = 1;
1374
- iframe.style.transform = `scale(1) rotate(${rotation}deg)`;
1496
+ zoomScale = 1;
1497
+ renderPage(currentPage);
1375
1498
  },
1376
1499
  rotateCW: () => {
1377
1500
  rotation = (rotation + 90) % 360;
1378
- iframe.style.transform = `scale(${currentZoom}) rotate(${rotation}deg)`;
1501
+ renderPage(currentPage);
1379
1502
  },
1380
1503
  rotateCCW: () => {
1381
1504
  rotation = (rotation - 90 + 360) % 360;
1382
- iframe.style.transform = `scale(${currentZoom}) rotate(${rotation}deg)`;
1505
+ renderPage(currentPage);
1383
1506
  },
1384
1507
  getRotation: () => rotation,
1508
+ getPageCount: () => totalPages,
1509
+ getCurrentPage: () => currentPage,
1385
1510
  goToPage: (page) => {
1386
- currentPage = page;
1387
- iframe.src = `${url}#page=${page}`;
1511
+ renderPage(page);
1388
1512
  },
1389
- getCurrentPage: () => currentPage,
1390
1513
  download: () => {
1514
+ const blob = new Blob([ctx.buffer], { type: "application/pdf" });
1515
+ const url = URL.createObjectURL(blob);
1391
1516
  const a = document.createElement("a");
1392
1517
  a.href = url;
1393
1518
  a.download = ctx.metadata.name || "document.pdf";
1394
1519
  a.click();
1520
+ URL.revokeObjectURL(url);
1395
1521
  },
1396
1522
  print: () => {
1397
- iframe.contentWindow?.print();
1523
+ const blob = new Blob([ctx.buffer], { type: "application/pdf" });
1524
+ const url = URL.createObjectURL(blob);
1525
+ const hiddenIframe = document.createElement("iframe");
1526
+ hiddenIframe.style.position = "fixed";
1527
+ hiddenIframe.style.right = "0";
1528
+ hiddenIframe.style.bottom = "0";
1529
+ hiddenIframe.style.width = "0";
1530
+ hiddenIframe.style.height = "0";
1531
+ hiddenIframe.style.border = "0";
1532
+ document.body.appendChild(hiddenIframe);
1533
+ hiddenIframe.src = url;
1534
+ hiddenIframe.onload = () => {
1535
+ setTimeout(() => {
1536
+ hiddenIframe.contentWindow?.print();
1537
+ setTimeout(() => {
1538
+ hiddenIframe.remove();
1539
+ URL.revokeObjectURL(url);
1540
+ }, 1e3);
1541
+ }, 300);
1542
+ };
1398
1543
  },
1399
1544
  getThumbnails: async () => {
1400
- return [
1401
- {
1402
- index: 1,
1403
- label: "Page 1",
1404
- render: async (canvas) => {
1405
- const context = canvas.getContext("2d");
1406
- if (context) {
1407
- context.fillStyle = "#fff";
1408
- context.fillRect(0, 0, canvas.width, canvas.height);
1409
- context.fillStyle = "#333";
1410
- context.font = "12px sans-serif";
1411
- context.fillText("PDF Preview", 10, 20);
1545
+ const thumbnails = [];
1546
+ const count = Math.min(totalPages, 50);
1547
+ for (let i = 1; i <= count; i++) {
1548
+ thumbnails.push({
1549
+ index: i,
1550
+ label: `Page ${i}`,
1551
+ render: async (thumbCanvas) => {
1552
+ try {
1553
+ const p = await pdfDoc.getPage(i);
1554
+ const baseVp = p.getViewport({ scale: 1 });
1555
+ const thumbScale = (thumbCanvas.width || 120) / baseVp.width;
1556
+ const thumbVp = p.getViewport({ scale: thumbScale });
1557
+ thumbCanvas.height = Math.floor(thumbVp.height);
1558
+ const tCtx = thumbCanvas.getContext("2d");
1559
+ if (tCtx) {
1560
+ await p.render({ canvasContext: tCtx, viewport: thumbVp }).promise;
1561
+ }
1562
+ } catch (e) {
1563
+ console.warn(`[PdfPlugin] Error generating thumbnail for page ${i}:`, e);
1412
1564
  }
1413
1565
  }
1414
- }
1415
- ];
1566
+ });
1567
+ }
1568
+ return thumbnails;
1416
1569
  }
1417
1570
  };
1571
+ return instance;
1418
1572
  }
1419
1573
  };
1420
1574
  function pdfPlugin() {
@@ -1741,7 +1895,31 @@ var DocxPlugin = class {
1741
1895
  return this.mimeTypes.includes(mime || "");
1742
1896
  }
1743
1897
  getToolbarActions(instance) {
1744
- return [
1898
+ const totalPages = instance.getPageCount?.() ?? 1;
1899
+ const actions = [];
1900
+ if (totalPages > 1) {
1901
+ actions.push({
1902
+ id: "page-nav",
1903
+ icon: "",
1904
+ label: "Page Navigation",
1905
+ type: "page-nav",
1906
+ group: "navigation",
1907
+ value: instance.getCurrentPage?.() ?? 1,
1908
+ max: totalPages,
1909
+ execute: (action, page) => {
1910
+ const cur = instance.getCurrentPage?.() ?? 1;
1911
+ const max = instance.getPageCount?.() ?? 1;
1912
+ if (action === "prev") {
1913
+ if (cur > 1) instance.goToPage?.(cur - 1);
1914
+ } else if (action === "next") {
1915
+ if (cur < max) instance.goToPage?.(cur + 1);
1916
+ } else if (typeof page === "number") {
1917
+ instance.goToPage?.(page);
1918
+ }
1919
+ }
1920
+ });
1921
+ }
1922
+ actions.push(
1745
1923
  {
1746
1924
  id: "zoom-out",
1747
1925
  icon: "zoom-out",
@@ -1782,7 +1960,8 @@ var DocxPlugin = class {
1782
1960
  group: "actions",
1783
1961
  execute: () => instance.print?.()
1784
1962
  }
1785
- ];
1963
+ );
1964
+ return actions;
1786
1965
  }
1787
1966
  async render(ctx) {
1788
1967
  const wrapper = document.createElement("div");
@@ -1858,7 +2037,51 @@ var DocxPlugin = class {
1858
2037
  }
1859
2038
  }
1860
2039
  }
2040
+ const sections = wrapper.querySelectorAll("section.docx");
2041
+ const cards = wrapper.querySelectorAll(".fp-docx-page-card");
2042
+ const pageElements = sections.length > 0 ? sections : cards;
2043
+ const totalPages = Math.max(1, pageElements.length);
2044
+ let currentPage = 1;
2045
+ let indicator = null;
2046
+ if (totalPages > 1) {
2047
+ indicator = document.createElement("div");
2048
+ indicator.className = "fp-docx-page-indicator";
2049
+ indicator.style.position = "sticky";
2050
+ indicator.style.bottom = "16px";
2051
+ indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
2052
+ indicator.style.backdropFilter = "blur(8px)";
2053
+ indicator.style.color = "#f8fafc";
2054
+ indicator.style.fontSize = "12px";
2055
+ indicator.style.fontWeight = "600";
2056
+ indicator.style.padding = "5px 14px";
2057
+ indicator.style.borderRadius = "20px";
2058
+ indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
2059
+ indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
2060
+ indicator.style.zIndex = "10";
2061
+ indicator.style.userSelect = "none";
2062
+ indicator.style.pointerEvents = "none";
2063
+ indicator.style.textAlign = "center";
2064
+ indicator.style.width = "fit-content";
2065
+ indicator.style.margin = "16px auto 0";
2066
+ ctx.container.appendChild(indicator);
2067
+ }
2068
+ const showPage = (pageNum) => {
2069
+ currentPage = Math.max(1, Math.min(totalPages, pageNum));
2070
+ if (pageElements.length > 1) {
2071
+ pageElements.forEach((sec, idx) => {
2072
+ sec.style.display = idx + 1 === currentPage ? "block" : "none";
2073
+ });
2074
+ }
2075
+ if (indicator) {
2076
+ indicator.textContent = `Page ${currentPage} of ${totalPages}`;
2077
+ }
2078
+ ctx.emit("page-change", { page: currentPage, total: totalPages });
2079
+ };
2080
+ if (totalPages > 1) {
2081
+ showPage(1);
2082
+ }
1861
2083
  const cleanup = () => {
2084
+ indicator?.remove();
1862
2085
  for (const url of createdBlobUrls) {
1863
2086
  URL.revokeObjectURL(url);
1864
2087
  }
@@ -1869,6 +2092,11 @@ var DocxPlugin = class {
1869
2092
  ctx.signal.addEventListener("abort", cleanup);
1870
2093
  return {
1871
2094
  destroy: cleanup,
2095
+ getPageCount: () => totalPages,
2096
+ getCurrentPage: () => currentPage,
2097
+ goToPage: (page) => {
2098
+ showPage(page);
2099
+ },
1872
2100
  zoomIn: () => {
1873
2101
  scale += 0.1;
1874
2102
  wrapper.style.transform = `scale(${scale})`;
@@ -2133,7 +2361,31 @@ var ExcelPlugin = class {
2133
2361
  return this.mimeTypes.includes(mime || "");
2134
2362
  }
2135
2363
  getToolbarActions(instance) {
2136
- return [
2364
+ const totalSheets = instance.getPageCount?.() ?? 1;
2365
+ const actions = [];
2366
+ if (totalSheets > 1) {
2367
+ actions.push({
2368
+ id: "page-nav",
2369
+ icon: "",
2370
+ label: "Sheet Navigation",
2371
+ type: "page-nav",
2372
+ group: "navigation",
2373
+ value: instance.getCurrentPage?.() ?? 1,
2374
+ max: totalSheets,
2375
+ execute: (action, sheet) => {
2376
+ const cur = instance.getCurrentPage?.() ?? 1;
2377
+ const max = instance.getPageCount?.() ?? 1;
2378
+ if (action === "prev") {
2379
+ if (cur > 1) instance.goToPage?.(cur - 1);
2380
+ } else if (action === "next") {
2381
+ if (cur < max) instance.goToPage?.(cur + 1);
2382
+ } else if (typeof sheet === "number") {
2383
+ instance.goToPage?.(sheet);
2384
+ }
2385
+ }
2386
+ });
2387
+ }
2388
+ actions.push(
2137
2389
  {
2138
2390
  id: "zoom-out",
2139
2391
  icon: "zoom-out",
@@ -2154,16 +2406,6 @@ var ExcelPlugin = class {
2154
2406
  instance.zoomIn?.();
2155
2407
  }
2156
2408
  },
2157
- {
2158
- id: "page-nav",
2159
- icon: "page-nav",
2160
- label: "Sheet Navigation",
2161
- type: "page-nav",
2162
- group: "navigation",
2163
- execute: (sheet) => {
2164
- if (typeof sheet === "number") instance.goToPage?.(sheet);
2165
- }
2166
- },
2167
2409
  {
2168
2410
  id: "download",
2169
2411
  icon: "download",
@@ -2184,7 +2426,8 @@ var ExcelPlugin = class {
2184
2426
  instance.print?.();
2185
2427
  }
2186
2428
  }
2187
- ];
2429
+ );
2430
+ return actions;
2188
2431
  }
2189
2432
  async render(ctx) {
2190
2433
  const container = document.createElement("div");
@@ -2268,6 +2511,7 @@ var ExcelPlugin = class {
2268
2511
  b.style.fontWeight = "normal";
2269
2512
  }
2270
2513
  });
2514
+ ctx.emit("page-change", { page: currentSheetIndex, total: sheetNames.length });
2271
2515
  };
2272
2516
  if (sheetNames.length > 0) {
2273
2517
  sheetNames.forEach((name, idx) => {
@@ -2517,7 +2761,31 @@ var CodePlugin = class {
2517
2761
  return false;
2518
2762
  }
2519
2763
  getToolbarActions(instance) {
2520
- return [
2764
+ const totalPages = instance.getPageCount?.() ?? 1;
2765
+ const actions = [];
2766
+ if (totalPages > 1) {
2767
+ actions.push({
2768
+ id: "page-nav",
2769
+ icon: "",
2770
+ label: "Page Navigation",
2771
+ type: "page-nav",
2772
+ group: "navigation",
2773
+ value: instance.getCurrentPage?.() ?? 1,
2774
+ max: totalPages,
2775
+ execute: (action, page) => {
2776
+ const cur = instance.getCurrentPage?.() ?? 1;
2777
+ const max = instance.getPageCount?.() ?? 1;
2778
+ if (action === "prev") {
2779
+ if (cur > 1) instance.goToPage?.(cur - 1);
2780
+ } else if (action === "next") {
2781
+ if (cur < max) instance.goToPage?.(cur + 1);
2782
+ } else if (typeof page === "number") {
2783
+ instance.goToPage?.(page);
2784
+ }
2785
+ }
2786
+ });
2787
+ }
2788
+ actions.push(
2521
2789
  {
2522
2790
  id: "zoom-out",
2523
2791
  icon: "zoom-out",
@@ -2568,11 +2836,16 @@ var CodePlugin = class {
2568
2836
  instance.print?.();
2569
2837
  }
2570
2838
  }
2571
- ];
2839
+ );
2840
+ return actions;
2572
2841
  }
2573
2842
  async render(ctx) {
2574
2843
  const decoder = new TextDecoder("utf-8");
2575
- const text = decoder.decode(ctx.buffer);
2844
+ const fullText = decoder.decode(ctx.buffer);
2845
+ const pageSplitRegex = /(?:\f|\x0C|(?:\r?\n|^)\s*[-=_]{3,}\s*(?:PAGE|Page|page break|Page Break)[\s\d\w-]*[-=_]{3,}\s*(?:\r?\n|$))/i;
2846
+ const rawPages = fullText.split(pageSplitRegex).map((p) => p.trim()).filter((p) => p.length > 0);
2847
+ const totalPages = Math.max(1, rawPages.length);
2848
+ let currentPage = 1;
2576
2849
  const container = document.createElement("div");
2577
2850
  container.style.width = "100%";
2578
2851
  container.style.height = "100%";
@@ -2591,25 +2864,66 @@ var CodePlugin = class {
2591
2864
  pre.style.wordBreak = "break-all";
2592
2865
  const code = document.createElement("code");
2593
2866
  const ext = (ctx.metadata.extension || "").replace(".", "");
2594
- try {
2595
- if (ext && hljs__default.default.getLanguage(ext)) {
2596
- code.innerHTML = hljs__default.default.highlight(text, { language: ext }).value;
2597
- } else {
2598
- code.innerHTML = hljs__default.default.highlightAuto(text).value;
2867
+ const renderCodePage = (text) => {
2868
+ try {
2869
+ if (ext && hljs__default.default.getLanguage(ext)) {
2870
+ code.innerHTML = hljs__default.default.highlight(text, { language: ext }).value;
2871
+ } else {
2872
+ code.innerHTML = hljs__default.default.highlightAuto(text).value;
2873
+ }
2874
+ } catch {
2875
+ code.textContent = text;
2599
2876
  }
2600
- } catch {
2601
- code.textContent = text;
2602
- }
2877
+ };
2878
+ renderCodePage(rawPages[0] || fullText);
2603
2879
  pre.appendChild(code);
2604
2880
  container.appendChild(pre);
2881
+ let indicator = null;
2882
+ if (totalPages > 1) {
2883
+ indicator = document.createElement("div");
2884
+ indicator.className = "fp-code-page-indicator";
2885
+ indicator.style.position = "sticky";
2886
+ indicator.style.bottom = "16px";
2887
+ indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
2888
+ indicator.style.backdropFilter = "blur(8px)";
2889
+ indicator.style.color = "#f8fafc";
2890
+ indicator.style.fontSize = "12px";
2891
+ indicator.style.fontWeight = "600";
2892
+ indicator.style.padding = "5px 14px";
2893
+ indicator.style.borderRadius = "20px";
2894
+ indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
2895
+ indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
2896
+ indicator.style.zIndex = "10";
2897
+ indicator.style.userSelect = "none";
2898
+ indicator.style.pointerEvents = "none";
2899
+ indicator.style.textAlign = "center";
2900
+ indicator.style.width = "fit-content";
2901
+ indicator.style.margin = "16px auto 0";
2902
+ container.appendChild(indicator);
2903
+ }
2904
+ const showPage = (pageNum) => {
2905
+ currentPage = Math.max(1, Math.min(totalPages, pageNum));
2906
+ renderCodePage(rawPages[currentPage - 1] || fullText);
2907
+ if (indicator) {
2908
+ indicator.textContent = `Page ${currentPage} of ${totalPages}`;
2909
+ }
2910
+ ctx.emit("page-change", { page: currentPage, total: totalPages });
2911
+ };
2912
+ if (totalPages > 1) {
2913
+ showPage(1);
2914
+ }
2605
2915
  ctx.container.appendChild(container);
2606
2916
  const cleanup = () => {
2917
+ indicator?.remove();
2607
2918
  container.remove();
2608
2919
  ctx.container.innerHTML = "";
2609
2920
  };
2610
2921
  ctx.signal.addEventListener("abort", cleanup);
2611
2922
  return {
2612
2923
  destroy: cleanup,
2924
+ getPageCount: () => totalPages,
2925
+ getCurrentPage: () => currentPage,
2926
+ goToPage: (page) => showPage(page),
2613
2927
  zoomIn: () => {
2614
2928
  fontSize = Math.min(32, fontSize + 2);
2615
2929
  pre.style.fontSize = `${fontSize}px`;
@@ -2637,7 +2951,7 @@ var CodePlugin = class {
2637
2951
  window.print();
2638
2952
  },
2639
2953
  copy: () => {
2640
- navigator.clipboard?.writeText(text);
2954
+ navigator.clipboard?.writeText(fullText);
2641
2955
  }
2642
2956
  };
2643
2957
  }
@@ -3459,7 +3773,31 @@ var RtfPlugin = class {
3459
3773
  return this.extensions.includes(ext || "") || this.mimeTypes.includes(mime || "");
3460
3774
  }
3461
3775
  getToolbarActions(instance) {
3462
- return [
3776
+ const totalPages = instance.getPageCount?.() ?? 1;
3777
+ const actions = [];
3778
+ if (totalPages > 1) {
3779
+ actions.push({
3780
+ id: "page-nav",
3781
+ icon: "",
3782
+ label: "Page Navigation",
3783
+ type: "page-nav",
3784
+ group: "navigation",
3785
+ value: instance.getCurrentPage?.() ?? 1,
3786
+ max: totalPages,
3787
+ execute: (action, page) => {
3788
+ const cur = instance.getCurrentPage?.() ?? 1;
3789
+ const max = instance.getPageCount?.() ?? 1;
3790
+ if (action === "prev") {
3791
+ if (cur > 1) instance.goToPage?.(cur - 1);
3792
+ } else if (action === "next") {
3793
+ if (cur < max) instance.goToPage?.(cur + 1);
3794
+ } else if (typeof page === "number") {
3795
+ instance.goToPage?.(page);
3796
+ }
3797
+ }
3798
+ });
3799
+ }
3800
+ actions.push(
3463
3801
  {
3464
3802
  id: "zoom-out",
3465
3803
  icon: "zoom-out",
@@ -3500,7 +3838,8 @@ var RtfPlugin = class {
3500
3838
  group: "actions",
3501
3839
  execute: () => instance.print?.()
3502
3840
  }
3503
- ];
3841
+ );
3842
+ return actions;
3504
3843
  }
3505
3844
  async render(ctx) {
3506
3845
  const wrapper = document.createElement("div");
@@ -3519,28 +3858,82 @@ var RtfPlugin = class {
3519
3858
  ctx.container.style.backgroundColor = "#f1f5f9";
3520
3859
  ctx.container.appendChild(wrapper);
3521
3860
  let scale = 1;
3861
+ let pageElements = [];
3522
3862
  try {
3523
3863
  if (typeof RTFJS__namespace.loggingEnabled === "function") {
3524
3864
  RTFJS__namespace.loggingEnabled(false);
3525
3865
  }
3526
3866
  const doc = new RTFJS__namespace.Document(ctx.buffer, {});
3527
3867
  const htmlElements = await doc.render();
3528
- for (const el of htmlElements) {
3868
+ pageElements = htmlElements;
3869
+ for (let i = 0; i < htmlElements.length; i++) {
3870
+ const el = htmlElements[i];
3871
+ el.style.display = i === 0 ? "block" : "none";
3529
3872
  wrapper.appendChild(el);
3530
3873
  }
3531
3874
  } catch (err) {
3532
3875
  console.warn("[RtfPlugin] RTF render error, fallback text:", err);
3533
3876
  const text = new TextDecoder("latin1").decode(ctx.buffer);
3534
3877
  const clean = text.replace(/\\par[d]?/g, "\n").replace(/\\[a-zA-Z0-9\-]+/g, "").replace(/[{}]/g, "");
3535
- wrapper.innerHTML = `<pre style="white-space: pre-wrap; font-family: serif; color: #333;">${clean}</pre>`;
3878
+ const pre = document.createElement("pre");
3879
+ pre.style.whiteSpace = "pre-wrap";
3880
+ pre.style.fontFamily = "serif";
3881
+ pre.style.color = "#333";
3882
+ pre.textContent = clean;
3883
+ wrapper.appendChild(pre);
3884
+ pageElements = [pre];
3885
+ }
3886
+ const totalPages = Math.max(1, pageElements.length);
3887
+ let currentPage = 1;
3888
+ let indicator = null;
3889
+ if (totalPages > 1) {
3890
+ indicator = document.createElement("div");
3891
+ indicator.className = "fp-rtf-page-indicator";
3892
+ indicator.style.position = "sticky";
3893
+ indicator.style.bottom = "16px";
3894
+ indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
3895
+ indicator.style.backdropFilter = "blur(8px)";
3896
+ indicator.style.color = "#f8fafc";
3897
+ indicator.style.fontSize = "12px";
3898
+ indicator.style.fontWeight = "600";
3899
+ indicator.style.padding = "5px 14px";
3900
+ indicator.style.borderRadius = "20px";
3901
+ indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
3902
+ indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
3903
+ indicator.style.zIndex = "10";
3904
+ indicator.style.userSelect = "none";
3905
+ indicator.style.pointerEvents = "none";
3906
+ indicator.style.textAlign = "center";
3907
+ indicator.style.width = "fit-content";
3908
+ indicator.style.margin = "16px auto 0";
3909
+ ctx.container.appendChild(indicator);
3910
+ }
3911
+ const showPage = (pageNum) => {
3912
+ currentPage = Math.max(1, Math.min(totalPages, pageNum));
3913
+ if (totalPages > 1) {
3914
+ pageElements.forEach((el, idx) => {
3915
+ el.style.display = idx + 1 === currentPage ? "block" : "none";
3916
+ });
3917
+ }
3918
+ if (indicator) {
3919
+ indicator.textContent = `Page ${currentPage} of ${totalPages}`;
3920
+ }
3921
+ ctx.emit("page-change", { page: currentPage, total: totalPages });
3922
+ };
3923
+ if (totalPages > 1) {
3924
+ showPage(1);
3536
3925
  }
3537
3926
  const cleanup = () => {
3927
+ indicator?.remove();
3538
3928
  wrapper.remove();
3539
3929
  ctx.container.innerHTML = "";
3540
3930
  };
3541
3931
  ctx.signal.addEventListener("abort", cleanup);
3542
3932
  return {
3543
3933
  destroy: cleanup,
3934
+ getPageCount: () => totalPages,
3935
+ getCurrentPage: () => currentPage,
3936
+ goToPage: (page) => showPage(page),
3544
3937
  zoomIn: () => {
3545
3938
  scale += 0.1;
3546
3939
  wrapper.style.transform = `scale(${scale})`;
@@ -3728,41 +4121,41 @@ var OpenDocumentPlugin = class {
3728
4121
  }
3729
4122
  getToolbarActions(instance) {
3730
4123
  const isPresentation = instance.isPresentation;
4124
+ const totalPages = instance.getPageCount?.() ?? 1;
3731
4125
  const actions = [];
3732
- if (isPresentation) {
3733
- actions.push(
3734
- {
4126
+ if (isPresentation || totalPages > 1) {
4127
+ if (isPresentation) {
4128
+ actions.push({
3735
4129
  id: "thumbnails",
3736
4130
  icon: "thumbnails",
3737
4131
  label: "Slide Thumbnails",
3738
4132
  type: "button",
3739
4133
  group: "navigation",
3740
4134
  execute: () => instance.toggleThumbnails?.()
3741
- },
3742
- {
3743
- id: "page-nav",
3744
- icon: "",
3745
- label: "Slide Navigation",
3746
- type: "page-nav",
3747
- group: "navigation",
3748
- value: instance.getCurrentPage?.() ?? 1,
3749
- max: instance.getPageCount?.() ?? 1,
3750
- execute: (action, page) => {
3751
- if (action === "prev") {
3752
- const cur = instance.getCurrentPage?.() ?? 1;
3753
- if (cur > 1) instance.goToPage?.(cur - 1);
3754
- } else if (action === "next") {
3755
- const cur = instance.getCurrentPage?.() ?? 1;
3756
- const total = instance.getPageCount?.() ?? 1;
3757
- if (cur < total) instance.goToPage?.(cur + 1);
3758
- } else if (typeof page === "number") {
3759
- instance.goToPage?.(page);
3760
- } else if (typeof action === "number") {
3761
- instance.goToPage?.(action);
3762
- }
4135
+ });
4136
+ }
4137
+ actions.push({
4138
+ id: "page-nav",
4139
+ icon: "",
4140
+ label: isPresentation ? "Slide Navigation" : "Page Navigation",
4141
+ type: "page-nav",
4142
+ group: "navigation",
4143
+ value: instance.getCurrentPage?.() ?? 1,
4144
+ max: totalPages,
4145
+ execute: (action, page) => {
4146
+ const cur = instance.getCurrentPage?.() ?? 1;
4147
+ const max = instance.getPageCount?.() ?? 1;
4148
+ if (action === "prev") {
4149
+ if (cur > 1) instance.goToPage?.(cur - 1);
4150
+ } else if (action === "next") {
4151
+ if (cur < max) instance.goToPage?.(cur + 1);
4152
+ } else if (typeof page === "number") {
4153
+ instance.goToPage?.(page);
4154
+ } else if (typeof action === "number") {
4155
+ instance.goToPage?.(action);
3763
4156
  }
3764
4157
  }
3765
- );
4158
+ });
3766
4159
  }
3767
4160
  actions.push(
3768
4161
  {
@@ -4145,7 +4538,31 @@ var DocPlugin = class {
4145
4538
  return this.mimeTypes.includes(mime || "");
4146
4539
  }
4147
4540
  getToolbarActions(instance) {
4148
- return [
4541
+ const totalPages = instance.getPageCount?.() ?? 1;
4542
+ const actions = [];
4543
+ if (totalPages > 1) {
4544
+ actions.push({
4545
+ id: "page-nav",
4546
+ icon: "",
4547
+ label: "Page Navigation",
4548
+ type: "page-nav",
4549
+ group: "navigation",
4550
+ value: instance.getCurrentPage?.() ?? 1,
4551
+ max: totalPages,
4552
+ execute: (action, page) => {
4553
+ const cur = instance.getCurrentPage?.() ?? 1;
4554
+ const max = instance.getPageCount?.() ?? 1;
4555
+ if (action === "prev") {
4556
+ if (cur > 1) instance.goToPage?.(cur - 1);
4557
+ } else if (action === "next") {
4558
+ if (cur < max) instance.goToPage?.(cur + 1);
4559
+ } else if (typeof page === "number") {
4560
+ instance.goToPage?.(page);
4561
+ }
4562
+ }
4563
+ });
4564
+ }
4565
+ actions.push(
4149
4566
  {
4150
4567
  id: "zoom-out",
4151
4568
  icon: "zoom-out",
@@ -4194,7 +4611,8 @@ var DocPlugin = class {
4194
4611
  group: "actions",
4195
4612
  execute: () => instance.print?.()
4196
4613
  }
4197
- ];
4614
+ );
4615
+ return actions;
4198
4616
  }
4199
4617
  async render(ctx) {
4200
4618
  const container = document.createElement("div");
@@ -4221,6 +4639,7 @@ var DocPlugin = class {
4221
4639
  ctx.container.appendChild(container);
4222
4640
  let scale = 1;
4223
4641
  let extractedRawText = "";
4642
+ let isFallback = false;
4224
4643
  try {
4225
4644
  const cfbf = new CfbfReader(ctx.buffer);
4226
4645
  const wordDocStream = cfbf.readStream("WordDocument");
@@ -4234,26 +4653,89 @@ var DocPlugin = class {
4234
4653
  const tableStream = cfbf.readStream(tableName);
4235
4654
  const text = this.extractDocText(wordDocStream, tableStream);
4236
4655
  extractedRawText = text;
4237
- wrapper.innerHTML = this.formatDocToHtml(text, ctx.metadata.name || "Document");
4238
4656
  } catch (err) {
4239
4657
  console.warn("[DocPlugin] Binary parsing error, fallback text:", err);
4240
4658
  const fallback = this.heuristicTextExtraction(ctx.buffer);
4241
4659
  extractedRawText = fallback;
4242
- wrapper.innerHTML = `
4243
- <div style="border-bottom: 1px solid #e2e8f0; padding-bottom: 12px; margin-bottom: 24px;">
4244
- <h2 style="margin: 0 0 6px; font-size: 20px; color: #334155;">${ctx.metadata.name || "Word Document (.doc)"}</h2>
4245
- <span style="font-size: 12px; color: #64748b; background: #f1f5f9; padding: 2px 8px; border-radius: 4px;">Legacy Word 97-2003 Binary Preview</span>
4246
- </div>
4247
- ${this.formatDocToHtml(fallback, ctx.metadata.name || "Document")}
4248
- `;
4660
+ isFallback = true;
4661
+ }
4662
+ const rawPages = this.splitIntoPages(extractedRawText);
4663
+ const totalPages = Math.max(1, rawPages.length);
4664
+ let currentPage = 1;
4665
+ wrapper.innerHTML = "";
4666
+ const pageCards = [];
4667
+ for (let i = 0; i < totalPages; i++) {
4668
+ const pageCard = document.createElement("div");
4669
+ pageCard.className = "fp-doc-page-card";
4670
+ pageCard.style.backgroundColor = "#ffffff";
4671
+ pageCard.style.boxShadow = "0 2px 12px rgba(0,0,0,0.08)";
4672
+ pageCard.style.borderRadius = "4px";
4673
+ pageCard.style.padding = "56px 48px";
4674
+ pageCard.style.minHeight = "100%";
4675
+ pageCard.style.display = i === 0 ? "block" : "none";
4676
+ if (isFallback && i === 0) {
4677
+ pageCard.innerHTML = `
4678
+ <div style="border-bottom: 1px solid #e2e8f0; padding-bottom: 12px; margin-bottom: 24px;">
4679
+ <h2 style="margin: 0 0 6px; font-size: 20px; color: #334155;">${DOMPurify2__default.default.sanitize(ctx.metadata.name || "Word Document (.doc)")}</h2>
4680
+ <span style="font-size: 12px; color: #64748b; background: #f1f5f9; padding: 2px 8px; border-radius: 4px;">Legacy Word 97-2003 Binary Preview</span>
4681
+ </div>
4682
+ ${this.formatDocToHtml(rawPages[i], ctx.metadata.name || "Document")}
4683
+ `;
4684
+ } else {
4685
+ pageCard.innerHTML = this.formatDocToHtml(rawPages[i], ctx.metadata.name || "Document");
4686
+ }
4687
+ wrapper.appendChild(pageCard);
4688
+ pageCards.push(pageCard);
4689
+ }
4690
+ let indicator = null;
4691
+ if (totalPages > 1) {
4692
+ indicator = document.createElement("div");
4693
+ indicator.className = "fp-doc-page-indicator";
4694
+ indicator.style.position = "sticky";
4695
+ indicator.style.bottom = "16px";
4696
+ indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
4697
+ indicator.style.backdropFilter = "blur(8px)";
4698
+ indicator.style.color = "#f8fafc";
4699
+ indicator.style.fontSize = "12px";
4700
+ indicator.style.fontWeight = "600";
4701
+ indicator.style.padding = "5px 14px";
4702
+ indicator.style.borderRadius = "20px";
4703
+ indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
4704
+ indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
4705
+ indicator.style.zIndex = "10";
4706
+ indicator.style.userSelect = "none";
4707
+ indicator.style.pointerEvents = "none";
4708
+ indicator.style.textAlign = "center";
4709
+ indicator.style.width = "fit-content";
4710
+ indicator.style.margin = "16px auto 0";
4711
+ container.appendChild(indicator);
4712
+ }
4713
+ const showPage = (pageNum) => {
4714
+ currentPage = Math.max(1, Math.min(totalPages, pageNum));
4715
+ if (totalPages > 1) {
4716
+ pageCards.forEach((card, idx) => {
4717
+ card.style.display = idx + 1 === currentPage ? "block" : "none";
4718
+ });
4719
+ }
4720
+ if (indicator) {
4721
+ indicator.textContent = `Page ${currentPage} of ${totalPages}`;
4722
+ }
4723
+ ctx.emit("page-change", { page: currentPage, total: totalPages });
4724
+ };
4725
+ if (totalPages > 1) {
4726
+ showPage(1);
4249
4727
  }
4250
4728
  const cleanup = () => {
4729
+ indicator?.remove();
4251
4730
  container.remove();
4252
4731
  ctx.container.innerHTML = "";
4253
4732
  };
4254
4733
  ctx.signal.addEventListener("abort", cleanup);
4255
4734
  return {
4256
4735
  destroy: cleanup,
4736
+ getPageCount: () => totalPages,
4737
+ getCurrentPage: () => currentPage,
4738
+ goToPage: (page) => showPage(page),
4257
4739
  zoomIn: () => {
4258
4740
  scale += 0.1;
4259
4741
  wrapper.style.transform = `scale(${scale})`;
@@ -4385,11 +4867,16 @@ var DocPlugin = class {
4385
4867
  heuristicTextExtraction(buffer) {
4386
4868
  return this.extractStringsFromBytes(new Uint8Array(buffer));
4387
4869
  }
4870
+ splitIntoPages(text) {
4871
+ if (!text) return [""];
4872
+ 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);
4873
+ return parts.length > 0 ? parts : [text];
4874
+ }
4388
4875
  /**
4389
4876
  * Format cleaned extracted text into attractive HTML paragraphs, headings, and lists
4390
4877
  */
4391
4878
  formatDocToHtml(text, filename) {
4392
- const lines = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\x0B/g, "\n").replace(/\x0C/g, "\n\n").split("\n");
4879
+ const lines = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\x0B/g, "\n").split("\n");
4393
4880
  let html = "";
4394
4881
  let inList = false;
4395
4882
  for (const rawLine of lines) {