@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/react.cjs CHANGED
@@ -4,6 +4,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var react = require('react');
6
6
  var DOMPurify2 = require('dompurify');
7
+ var pdfjsLib = require('pdfjs-dist');
7
8
  var docx = require('docx-preview');
8
9
  var fflate = require('fflate');
9
10
  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);
@@ -511,11 +513,21 @@ var ToolbarController = class {
511
513
  el;
512
514
  toolbarEl;
513
515
  actions = [];
516
+ pageInputEl = null;
517
+ pageLabelEl = null;
514
518
  constructor(container) {
515
519
  this.el = container;
516
520
  this.toolbarEl = createElement("div", { className: "fp-toolbar" });
517
521
  this.el.appendChild(this.toolbarEl);
518
522
  }
523
+ setPage(page, max) {
524
+ if (this.pageInputEl) {
525
+ this.pageInputEl.value = page.toString();
526
+ }
527
+ if (max !== void 0 && this.pageLabelEl) {
528
+ this.pageLabelEl.textContent = ` / ${max}`;
529
+ }
530
+ }
519
531
  update(actions) {
520
532
  this.actions = actions;
521
533
  this.render();
@@ -558,6 +570,7 @@ var ToolbarController = class {
558
570
  if (action.type === "separator") {
559
571
  groupEl.appendChild(createElement("div", { className: "fp-toolbar-separator" }));
560
572
  } else if (action.type === "page-nav") {
573
+ const max = action.max ?? 1;
561
574
  const prevBtn = this.createButton(
562
575
  "prev",
563
576
  ICON_PAGE_PREV,
@@ -576,7 +589,6 @@ var ToolbarController = class {
576
589
  "Next Page",
577
590
  () => {
578
591
  const cur = parseInt(input.value, 10) || 1;
579
- const max = action.max ?? 1;
580
592
  if (cur < max) {
581
593
  input.value = (cur + 1).toString();
582
594
  action.execute("next", cur + 1);
@@ -588,15 +600,18 @@ var ToolbarController = class {
588
600
  type: "number",
589
601
  value: (action.value ?? 1).toString(),
590
602
  min: "1",
591
- max: (action.max ?? 1).toString()
603
+ max: max.toString()
592
604
  });
593
605
  input.addEventListener("change", () => {
594
- const val = parseInt(input.value, 10);
595
- if (!isNaN(val)) {
596
- action.execute("go", val);
597
- }
606
+ let val = parseInt(input.value, 10);
607
+ if (isNaN(val)) val = 1;
608
+ val = Math.max(1, Math.min(max, val));
609
+ input.value = val.toString();
610
+ action.execute("go", val);
598
611
  });
599
- const label = createElement("span", { className: "fp-toolbar-label" }, ` / ${action.max ?? 1}`);
612
+ const label = createElement("span", { className: "fp-toolbar-label" }, ` / ${max}`);
613
+ this.pageInputEl = input;
614
+ this.pageLabelEl = label;
600
615
  groupEl.appendChild(prevBtn);
601
616
  groupEl.appendChild(input);
602
617
  groupEl.appendChild(label);
@@ -774,7 +789,13 @@ var FilePreviewViewer = class {
774
789
  buffer,
775
790
  options,
776
791
  signal,
777
- emit: (event, payload) => this.eventEmitter.emit(event, payload)
792
+ emit: (event, payload) => {
793
+ if (event === "page-change" && payload && typeof payload.page === "number") {
794
+ const total = payload.total ?? payload.totalPages;
795
+ this.toolbar?.setPage(payload.page, total);
796
+ }
797
+ this.eventEmitter.emit(event, payload);
798
+ }
778
799
  });
779
800
  this.activeInstance = instance;
780
801
  this.hideLoading();
@@ -808,6 +829,7 @@ var FilePreviewViewer = class {
808
829
  if (thumbnails && thumbnails.length > 0 && this.thumbnailPanel) {
809
830
  this.thumbnailPanel.update(thumbnails, (index) => {
810
831
  instance.goToPage?.(index + 1);
832
+ this.toolbar?.setPage(index + 1);
811
833
  });
812
834
  if (options.showThumbnails) {
813
835
  this.thumbnailPanel.show();
@@ -933,9 +955,13 @@ var FilePreviewViewer = class {
933
955
  if (e.key === "ArrowRight" || e.key === "PageDown") {
934
956
  const cur = this.activeInstance.getCurrentPage?.() ?? 1;
935
957
  this.activeInstance.goToPage?.(cur + 1);
958
+ const nextCur = this.activeInstance.getCurrentPage?.() ?? cur + 1;
959
+ this.toolbar?.setPage(nextCur);
936
960
  } else if (e.key === "ArrowLeft" || e.key === "PageUp") {
937
961
  const cur = this.activeInstance.getCurrentPage?.() ?? 1;
938
962
  this.activeInstance.goToPage?.(Math.max(1, cur - 1));
963
+ const prevCur = this.activeInstance.getCurrentPage?.() ?? Math.max(1, cur - 1);
964
+ this.toolbar?.setPage(prevCur);
939
965
  } else if (e.key === "+" || e.key === "=") {
940
966
  this.activeInstance.zoomIn?.();
941
967
  } else if (e.key === "-" || e.key === "_") {
@@ -1201,30 +1227,62 @@ var CfbfReader = class {
1201
1227
  return result.subarray(0, targetSize);
1202
1228
  }
1203
1229
  };
1204
-
1205
- // ../plugins/pdf/dist/index.js
1230
+ if (typeof window !== "undefined" && pdfjsLib__namespace.GlobalWorkerOptions) {
1231
+ if (!pdfjsLib__namespace.GlobalWorkerOptions.workerSrc) {
1232
+ pdfjsLib__namespace.GlobalWorkerOptions.workerSrc = `https://unpkg.com/pdfjs-dist@${pdfjsLib__namespace.version || "4.10.38"}/build/pdf.worker.min.mjs`;
1233
+ }
1234
+ }
1206
1235
  var PdfPlugin = class {
1207
1236
  id = "pdf";
1208
- name = "PDF Preview";
1237
+ name = "PDF Document Preview";
1209
1238
  extensions = [".pdf"];
1210
1239
  mimeTypes = ["application/pdf"];
1211
1240
  weight = 100;
1212
1241
  supports(file) {
1213
1242
  const ext = file.metadata.extension?.toLowerCase();
1214
1243
  const mime = file.metadata.mimeType?.toLowerCase();
1215
- return ext === ".pdf" || mime === "application/pdf";
1244
+ if (ext) return this.extensions.includes(ext);
1245
+ return this.mimeTypes.includes(mime || "");
1216
1246
  }
1217
1247
  getToolbarActions(instance) {
1248
+ const totalPages = instance.getPageCount?.() ?? 1;
1249
+ const curPage = instance.getCurrentPage?.() ?? 1;
1218
1250
  return [
1251
+ {
1252
+ id: "thumbnails",
1253
+ icon: "thumbnails",
1254
+ label: "Page Thumbnails",
1255
+ type: "button",
1256
+ group: "navigation",
1257
+ execute: () => instance.toggleThumbnails?.()
1258
+ },
1259
+ {
1260
+ id: "page-nav",
1261
+ icon: "",
1262
+ label: "Page Navigation",
1263
+ type: "page-nav",
1264
+ group: "navigation",
1265
+ value: curPage,
1266
+ max: totalPages,
1267
+ execute: (action, page) => {
1268
+ const cur = instance.getCurrentPage?.() ?? 1;
1269
+ const max = instance.getPageCount?.() ?? 1;
1270
+ if (action === "prev") {
1271
+ if (cur > 1) instance.goToPage?.(cur - 1);
1272
+ } else if (action === "next") {
1273
+ if (cur < max) instance.goToPage?.(cur + 1);
1274
+ } else if (typeof page === "number") {
1275
+ instance.goToPage?.(page);
1276
+ }
1277
+ }
1278
+ },
1219
1279
  {
1220
1280
  id: "zoom-out",
1221
1281
  icon: "zoom-out",
1222
1282
  label: "Zoom Out",
1223
1283
  type: "button",
1224
1284
  group: "zoom",
1225
- execute: () => {
1226
- instance.zoomOut?.();
1227
- }
1285
+ execute: () => instance.zoomOut?.()
1228
1286
  },
1229
1287
  {
1230
1288
  id: "zoom-in",
@@ -1232,9 +1290,7 @@ var PdfPlugin = class {
1232
1290
  label: "Zoom In",
1233
1291
  type: "button",
1234
1292
  group: "zoom",
1235
- execute: () => {
1236
- instance.zoomIn?.();
1237
- }
1293
+ execute: () => instance.zoomIn?.()
1238
1294
  },
1239
1295
  {
1240
1296
  id: "fit-page",
@@ -1242,39 +1298,23 @@ var PdfPlugin = class {
1242
1298
  label: "Fit to Page",
1243
1299
  type: "button",
1244
1300
  group: "zoom",
1245
- execute: () => {
1246
- instance.fitToPage?.();
1247
- }
1301
+ execute: () => instance.fitToPage?.()
1248
1302
  },
1249
1303
  {
1250
1304
  id: "rotate-cw",
1251
1305
  icon: "rotate-cw",
1252
- label: "Rotate",
1306
+ label: "Rotate Clockwise",
1253
1307
  type: "button",
1254
1308
  group: "view",
1255
- execute: () => {
1256
- instance.rotateCW?.();
1257
- }
1258
- },
1259
- {
1260
- id: "page-nav",
1261
- icon: "page-nav",
1262
- label: "Page Navigation",
1263
- type: "page-nav",
1264
- group: "navigation",
1265
- execute: (page) => {
1266
- if (typeof page === "number") instance.goToPage?.(page);
1267
- }
1309
+ execute: () => instance.rotateCW?.()
1268
1310
  },
1269
1311
  {
1270
1312
  id: "download",
1271
1313
  icon: "download",
1272
- label: "Download",
1314
+ label: "Download PDF",
1273
1315
  type: "button",
1274
1316
  group: "actions",
1275
- execute: () => {
1276
- instance.download?.();
1277
- }
1317
+ execute: () => instance.download?.()
1278
1318
  },
1279
1319
  {
1280
1320
  id: "print",
@@ -1282,99 +1322,213 @@ var PdfPlugin = class {
1282
1322
  label: "Print",
1283
1323
  type: "button",
1284
1324
  group: "actions",
1285
- execute: () => {
1286
- instance.print?.();
1287
- }
1325
+ execute: () => instance.print?.()
1288
1326
  }
1289
1327
  ];
1290
1328
  }
1291
1329
  async render(ctx) {
1292
- const blob = new Blob([ctx.buffer], { type: "application/pdf" });
1293
- const url = URL.createObjectURL(blob);
1294
- const wrapper = document.createElement("div");
1295
- wrapper.style.width = "100%";
1296
- wrapper.style.height = "100%";
1297
- wrapper.style.overflow = "hidden";
1298
- wrapper.style.display = "flex";
1299
- wrapper.style.justifyContent = "center";
1300
- wrapper.style.alignItems = "center";
1301
- const iframe = document.createElement("iframe");
1302
- iframe.src = url;
1303
- iframe.style.width = "100%";
1304
- iframe.style.height = "100%";
1305
- iframe.style.border = "none";
1306
- wrapper.appendChild(iframe);
1307
- ctx.container.appendChild(wrapper);
1330
+ const container = document.createElement("div");
1331
+ container.className = "fp-pdf-container";
1332
+ container.style.width = "100%";
1333
+ container.style.height = "100%";
1334
+ container.style.overflow = "auto";
1335
+ container.style.display = "flex";
1336
+ container.style.flexDirection = "column";
1337
+ container.style.alignItems = "center";
1338
+ container.style.padding = "24px 16px";
1339
+ container.style.backgroundColor = "#0f172a";
1340
+ container.style.boxSizing = "border-box";
1341
+ container.style.position = "relative";
1342
+ const pageCard = document.createElement("div");
1343
+ pageCard.className = "fp-pdf-page-card";
1344
+ pageCard.style.boxShadow = "0 10px 35px rgba(0, 0, 0, 0.5)";
1345
+ pageCard.style.backgroundColor = "#ffffff";
1346
+ pageCard.style.borderRadius = "4px";
1347
+ pageCard.style.overflow = "hidden";
1348
+ pageCard.style.lineHeight = "0";
1349
+ pageCard.style.transition = "transform 0.15s ease";
1350
+ pageCard.style.position = "relative";
1351
+ const canvas = document.createElement("canvas");
1352
+ pageCard.appendChild(canvas);
1353
+ container.appendChild(pageCard);
1354
+ const indicator = document.createElement("div");
1355
+ indicator.className = "fp-pdf-page-indicator";
1356
+ indicator.style.position = "sticky";
1357
+ indicator.style.bottom = "16px";
1358
+ indicator.style.marginTop = "16px";
1359
+ indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
1360
+ indicator.style.backdropFilter = "blur(8px)";
1361
+ indicator.style.color = "#f8fafc";
1362
+ indicator.style.fontSize = "12px";
1363
+ indicator.style.fontWeight = "600";
1364
+ indicator.style.padding = "5px 14px";
1365
+ indicator.style.borderRadius = "20px";
1366
+ indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
1367
+ indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
1368
+ indicator.style.zIndex = "10";
1369
+ indicator.style.userSelect = "none";
1370
+ indicator.style.pointerEvents = "none";
1371
+ container.appendChild(indicator);
1372
+ ctx.container.appendChild(container);
1373
+ const loadingTask = pdfjsLib__namespace.getDocument({
1374
+ data: new Uint8Array(ctx.buffer),
1375
+ cMapUrl: `https://unpkg.com/pdfjs-dist@${pdfjsLib__namespace.version || "4.10.38"}/cmaps/`,
1376
+ cMapPacked: true,
1377
+ standardFontDataUrl: `https://unpkg.com/pdfjs-dist@${pdfjsLib__namespace.version || "4.10.38"}/standard_fonts/`
1378
+ });
1379
+ const pdfDoc = await loadingTask.promise;
1380
+ const totalPages = Math.max(1, pdfDoc.numPages);
1308
1381
  let currentPage = 1;
1309
- let currentZoom = 1;
1382
+ let zoomScale = 1;
1310
1383
  let rotation = 0;
1384
+ let currentRenderTask = null;
1385
+ const renderPage = async (pageNum) => {
1386
+ if (currentRenderTask) {
1387
+ try {
1388
+ currentRenderTask.cancel();
1389
+ } catch {
1390
+ }
1391
+ currentRenderTask = null;
1392
+ }
1393
+ currentPage = Math.max(1, Math.min(totalPages, pageNum));
1394
+ indicator.textContent = `Page ${currentPage} of ${totalPages}`;
1395
+ ctx.emit("page-change", { page: currentPage, total: totalPages });
1396
+ const page = await pdfDoc.getPage(currentPage);
1397
+ const containerWidth = container.clientWidth || 900;
1398
+ const unscaledVp = page.getViewport({ scale: 1, rotation });
1399
+ const baseScale = Math.min((containerWidth - 64) / unscaledVp.width, 1.6);
1400
+ const effectiveScale = (baseScale > 0 ? baseScale : 1) * zoomScale;
1401
+ const pixelRatio = window.devicePixelRatio || 1;
1402
+ const viewport = page.getViewport({ scale: effectiveScale, rotation });
1403
+ canvas.width = Math.floor(viewport.width * pixelRatio);
1404
+ canvas.height = Math.floor(viewport.height * pixelRatio);
1405
+ canvas.style.width = `${Math.floor(viewport.width)}px`;
1406
+ canvas.style.height = `${Math.floor(viewport.height)}px`;
1407
+ const canvasCtx = canvas.getContext("2d");
1408
+ if (!canvasCtx) return;
1409
+ canvasCtx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
1410
+ currentRenderTask = page.render({
1411
+ canvasContext: canvasCtx,
1412
+ viewport
1413
+ });
1414
+ try {
1415
+ await currentRenderTask.promise;
1416
+ } catch (err) {
1417
+ if (err?.name !== "RenderingCancelledException") {
1418
+ console.warn("[PdfPlugin] Page render warning:", err);
1419
+ }
1420
+ } finally {
1421
+ currentRenderTask = null;
1422
+ }
1423
+ };
1424
+ await renderPage(1);
1311
1425
  const cleanup = () => {
1312
- URL.revokeObjectURL(url);
1313
- wrapper.remove();
1426
+ if (currentRenderTask) {
1427
+ try {
1428
+ currentRenderTask.cancel();
1429
+ } catch {
1430
+ }
1431
+ }
1432
+ try {
1433
+ pdfDoc.destroy();
1434
+ } catch {
1435
+ }
1436
+ container.remove();
1314
1437
  ctx.container.innerHTML = "";
1315
1438
  };
1316
1439
  ctx.signal.addEventListener("abort", cleanup);
1317
- return {
1440
+ const instance = {
1318
1441
  destroy: cleanup,
1319
1442
  zoomIn: () => {
1320
- currentZoom += 0.1;
1321
- iframe.style.transform = `scale(${currentZoom}) rotate(${rotation}deg)`;
1443
+ zoomScale = Math.min(3.5, zoomScale + 0.2);
1444
+ renderPage(currentPage);
1322
1445
  },
1323
1446
  zoomOut: () => {
1324
- currentZoom = Math.max(0.2, currentZoom - 0.1);
1325
- iframe.style.transform = `scale(${currentZoom}) rotate(${rotation}deg)`;
1447
+ zoomScale = Math.max(0.3, zoomScale - 0.2);
1448
+ renderPage(currentPage);
1326
1449
  },
1327
- getZoom: () => currentZoom,
1450
+ getZoom: () => zoomScale,
1328
1451
  setZoom: (level) => {
1329
- currentZoom = level;
1330
- iframe.style.transform = `scale(${currentZoom}) rotate(${rotation}deg)`;
1452
+ zoomScale = Math.max(0.3, Math.min(3.5, level));
1453
+ renderPage(currentPage);
1331
1454
  },
1332
1455
  fitToPage: () => {
1333
- currentZoom = 1;
1334
- iframe.style.transform = `scale(1) rotate(${rotation}deg)`;
1456
+ zoomScale = 1;
1457
+ renderPage(currentPage);
1335
1458
  },
1336
1459
  rotateCW: () => {
1337
1460
  rotation = (rotation + 90) % 360;
1338
- iframe.style.transform = `scale(${currentZoom}) rotate(${rotation}deg)`;
1461
+ renderPage(currentPage);
1339
1462
  },
1340
1463
  rotateCCW: () => {
1341
1464
  rotation = (rotation - 90 + 360) % 360;
1342
- iframe.style.transform = `scale(${currentZoom}) rotate(${rotation}deg)`;
1465
+ renderPage(currentPage);
1343
1466
  },
1344
1467
  getRotation: () => rotation,
1468
+ getPageCount: () => totalPages,
1469
+ getCurrentPage: () => currentPage,
1345
1470
  goToPage: (page) => {
1346
- currentPage = page;
1347
- iframe.src = `${url}#page=${page}`;
1471
+ renderPage(page);
1348
1472
  },
1349
- getCurrentPage: () => currentPage,
1350
1473
  download: () => {
1474
+ const blob = new Blob([ctx.buffer], { type: "application/pdf" });
1475
+ const url = URL.createObjectURL(blob);
1351
1476
  const a = document.createElement("a");
1352
1477
  a.href = url;
1353
1478
  a.download = ctx.metadata.name || "document.pdf";
1354
1479
  a.click();
1480
+ URL.revokeObjectURL(url);
1355
1481
  },
1356
1482
  print: () => {
1357
- iframe.contentWindow?.print();
1483
+ const blob = new Blob([ctx.buffer], { type: "application/pdf" });
1484
+ const url = URL.createObjectURL(blob);
1485
+ const hiddenIframe = document.createElement("iframe");
1486
+ hiddenIframe.style.position = "fixed";
1487
+ hiddenIframe.style.right = "0";
1488
+ hiddenIframe.style.bottom = "0";
1489
+ hiddenIframe.style.width = "0";
1490
+ hiddenIframe.style.height = "0";
1491
+ hiddenIframe.style.border = "0";
1492
+ document.body.appendChild(hiddenIframe);
1493
+ hiddenIframe.src = url;
1494
+ hiddenIframe.onload = () => {
1495
+ setTimeout(() => {
1496
+ hiddenIframe.contentWindow?.print();
1497
+ setTimeout(() => {
1498
+ hiddenIframe.remove();
1499
+ URL.revokeObjectURL(url);
1500
+ }, 1e3);
1501
+ }, 300);
1502
+ };
1358
1503
  },
1359
1504
  getThumbnails: async () => {
1360
- return [
1361
- {
1362
- index: 1,
1363
- label: "Page 1",
1364
- render: async (canvas) => {
1365
- const context = canvas.getContext("2d");
1366
- if (context) {
1367
- context.fillStyle = "#fff";
1368
- context.fillRect(0, 0, canvas.width, canvas.height);
1369
- context.fillStyle = "#333";
1370
- context.font = "12px sans-serif";
1371
- context.fillText("PDF Preview", 10, 20);
1505
+ const thumbnails = [];
1506
+ const count = Math.min(totalPages, 50);
1507
+ for (let i = 1; i <= count; i++) {
1508
+ thumbnails.push({
1509
+ index: i,
1510
+ label: `Page ${i}`,
1511
+ render: async (thumbCanvas) => {
1512
+ try {
1513
+ const p = await pdfDoc.getPage(i);
1514
+ const baseVp = p.getViewport({ scale: 1 });
1515
+ const thumbScale = (thumbCanvas.width || 120) / baseVp.width;
1516
+ const thumbVp = p.getViewport({ scale: thumbScale });
1517
+ thumbCanvas.height = Math.floor(thumbVp.height);
1518
+ const tCtx = thumbCanvas.getContext("2d");
1519
+ if (tCtx) {
1520
+ await p.render({ canvasContext: tCtx, viewport: thumbVp }).promise;
1521
+ }
1522
+ } catch (e) {
1523
+ console.warn(`[PdfPlugin] Error generating thumbnail for page ${i}:`, e);
1372
1524
  }
1373
1525
  }
1374
- }
1375
- ];
1526
+ });
1527
+ }
1528
+ return thumbnails;
1376
1529
  }
1377
1530
  };
1531
+ return instance;
1378
1532
  }
1379
1533
  };
1380
1534
  function pdfPlugin() {
@@ -1701,7 +1855,31 @@ var DocxPlugin = class {
1701
1855
  return this.mimeTypes.includes(mime || "");
1702
1856
  }
1703
1857
  getToolbarActions(instance) {
1704
- return [
1858
+ const totalPages = instance.getPageCount?.() ?? 1;
1859
+ const actions = [];
1860
+ if (totalPages > 1) {
1861
+ actions.push({
1862
+ id: "page-nav",
1863
+ icon: "",
1864
+ label: "Page Navigation",
1865
+ type: "page-nav",
1866
+ group: "navigation",
1867
+ value: instance.getCurrentPage?.() ?? 1,
1868
+ max: totalPages,
1869
+ execute: (action, page) => {
1870
+ const cur = instance.getCurrentPage?.() ?? 1;
1871
+ const max = instance.getPageCount?.() ?? 1;
1872
+ if (action === "prev") {
1873
+ if (cur > 1) instance.goToPage?.(cur - 1);
1874
+ } else if (action === "next") {
1875
+ if (cur < max) instance.goToPage?.(cur + 1);
1876
+ } else if (typeof page === "number") {
1877
+ instance.goToPage?.(page);
1878
+ }
1879
+ }
1880
+ });
1881
+ }
1882
+ actions.push(
1705
1883
  {
1706
1884
  id: "zoom-out",
1707
1885
  icon: "zoom-out",
@@ -1742,7 +1920,8 @@ var DocxPlugin = class {
1742
1920
  group: "actions",
1743
1921
  execute: () => instance.print?.()
1744
1922
  }
1745
- ];
1923
+ );
1924
+ return actions;
1746
1925
  }
1747
1926
  async render(ctx) {
1748
1927
  const wrapper = document.createElement("div");
@@ -1818,7 +1997,51 @@ var DocxPlugin = class {
1818
1997
  }
1819
1998
  }
1820
1999
  }
2000
+ const sections = wrapper.querySelectorAll("section.docx");
2001
+ const cards = wrapper.querySelectorAll(".fp-docx-page-card");
2002
+ const pageElements = sections.length > 0 ? sections : cards;
2003
+ const totalPages = Math.max(1, pageElements.length);
2004
+ let currentPage = 1;
2005
+ let indicator = null;
2006
+ if (totalPages > 1) {
2007
+ indicator = document.createElement("div");
2008
+ indicator.className = "fp-docx-page-indicator";
2009
+ indicator.style.position = "sticky";
2010
+ indicator.style.bottom = "16px";
2011
+ indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
2012
+ indicator.style.backdropFilter = "blur(8px)";
2013
+ indicator.style.color = "#f8fafc";
2014
+ indicator.style.fontSize = "12px";
2015
+ indicator.style.fontWeight = "600";
2016
+ indicator.style.padding = "5px 14px";
2017
+ indicator.style.borderRadius = "20px";
2018
+ indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
2019
+ indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
2020
+ indicator.style.zIndex = "10";
2021
+ indicator.style.userSelect = "none";
2022
+ indicator.style.pointerEvents = "none";
2023
+ indicator.style.textAlign = "center";
2024
+ indicator.style.width = "fit-content";
2025
+ indicator.style.margin = "16px auto 0";
2026
+ ctx.container.appendChild(indicator);
2027
+ }
2028
+ const showPage = (pageNum) => {
2029
+ currentPage = Math.max(1, Math.min(totalPages, pageNum));
2030
+ if (pageElements.length > 1) {
2031
+ pageElements.forEach((sec, idx) => {
2032
+ sec.style.display = idx + 1 === currentPage ? "block" : "none";
2033
+ });
2034
+ }
2035
+ if (indicator) {
2036
+ indicator.textContent = `Page ${currentPage} of ${totalPages}`;
2037
+ }
2038
+ ctx.emit("page-change", { page: currentPage, total: totalPages });
2039
+ };
2040
+ if (totalPages > 1) {
2041
+ showPage(1);
2042
+ }
1821
2043
  const cleanup = () => {
2044
+ indicator?.remove();
1822
2045
  for (const url of createdBlobUrls) {
1823
2046
  URL.revokeObjectURL(url);
1824
2047
  }
@@ -1829,6 +2052,11 @@ var DocxPlugin = class {
1829
2052
  ctx.signal.addEventListener("abort", cleanup);
1830
2053
  return {
1831
2054
  destroy: cleanup,
2055
+ getPageCount: () => totalPages,
2056
+ getCurrentPage: () => currentPage,
2057
+ goToPage: (page) => {
2058
+ showPage(page);
2059
+ },
1832
2060
  zoomIn: () => {
1833
2061
  scale += 0.1;
1834
2062
  wrapper.style.transform = `scale(${scale})`;
@@ -2093,7 +2321,31 @@ var ExcelPlugin = class {
2093
2321
  return this.mimeTypes.includes(mime || "");
2094
2322
  }
2095
2323
  getToolbarActions(instance) {
2096
- return [
2324
+ const totalSheets = instance.getPageCount?.() ?? 1;
2325
+ const actions = [];
2326
+ if (totalSheets > 1) {
2327
+ actions.push({
2328
+ id: "page-nav",
2329
+ icon: "",
2330
+ label: "Sheet Navigation",
2331
+ type: "page-nav",
2332
+ group: "navigation",
2333
+ value: instance.getCurrentPage?.() ?? 1,
2334
+ max: totalSheets,
2335
+ execute: (action, sheet) => {
2336
+ const cur = instance.getCurrentPage?.() ?? 1;
2337
+ const max = instance.getPageCount?.() ?? 1;
2338
+ if (action === "prev") {
2339
+ if (cur > 1) instance.goToPage?.(cur - 1);
2340
+ } else if (action === "next") {
2341
+ if (cur < max) instance.goToPage?.(cur + 1);
2342
+ } else if (typeof sheet === "number") {
2343
+ instance.goToPage?.(sheet);
2344
+ }
2345
+ }
2346
+ });
2347
+ }
2348
+ actions.push(
2097
2349
  {
2098
2350
  id: "zoom-out",
2099
2351
  icon: "zoom-out",
@@ -2114,16 +2366,6 @@ var ExcelPlugin = class {
2114
2366
  instance.zoomIn?.();
2115
2367
  }
2116
2368
  },
2117
- {
2118
- id: "page-nav",
2119
- icon: "page-nav",
2120
- label: "Sheet Navigation",
2121
- type: "page-nav",
2122
- group: "navigation",
2123
- execute: (sheet) => {
2124
- if (typeof sheet === "number") instance.goToPage?.(sheet);
2125
- }
2126
- },
2127
2369
  {
2128
2370
  id: "download",
2129
2371
  icon: "download",
@@ -2144,7 +2386,8 @@ var ExcelPlugin = class {
2144
2386
  instance.print?.();
2145
2387
  }
2146
2388
  }
2147
- ];
2389
+ );
2390
+ return actions;
2148
2391
  }
2149
2392
  async render(ctx) {
2150
2393
  const container = document.createElement("div");
@@ -2228,6 +2471,7 @@ var ExcelPlugin = class {
2228
2471
  b.style.fontWeight = "normal";
2229
2472
  }
2230
2473
  });
2474
+ ctx.emit("page-change", { page: currentSheetIndex, total: sheetNames.length });
2231
2475
  };
2232
2476
  if (sheetNames.length > 0) {
2233
2477
  sheetNames.forEach((name, idx) => {
@@ -2477,7 +2721,31 @@ var CodePlugin = class {
2477
2721
  return false;
2478
2722
  }
2479
2723
  getToolbarActions(instance) {
2480
- return [
2724
+ const totalPages = instance.getPageCount?.() ?? 1;
2725
+ const actions = [];
2726
+ if (totalPages > 1) {
2727
+ actions.push({
2728
+ id: "page-nav",
2729
+ icon: "",
2730
+ label: "Page Navigation",
2731
+ type: "page-nav",
2732
+ group: "navigation",
2733
+ value: instance.getCurrentPage?.() ?? 1,
2734
+ max: totalPages,
2735
+ execute: (action, page) => {
2736
+ const cur = instance.getCurrentPage?.() ?? 1;
2737
+ const max = instance.getPageCount?.() ?? 1;
2738
+ if (action === "prev") {
2739
+ if (cur > 1) instance.goToPage?.(cur - 1);
2740
+ } else if (action === "next") {
2741
+ if (cur < max) instance.goToPage?.(cur + 1);
2742
+ } else if (typeof page === "number") {
2743
+ instance.goToPage?.(page);
2744
+ }
2745
+ }
2746
+ });
2747
+ }
2748
+ actions.push(
2481
2749
  {
2482
2750
  id: "zoom-out",
2483
2751
  icon: "zoom-out",
@@ -2528,11 +2796,16 @@ var CodePlugin = class {
2528
2796
  instance.print?.();
2529
2797
  }
2530
2798
  }
2531
- ];
2799
+ );
2800
+ return actions;
2532
2801
  }
2533
2802
  async render(ctx) {
2534
2803
  const decoder = new TextDecoder("utf-8");
2535
- const text = decoder.decode(ctx.buffer);
2804
+ const fullText = decoder.decode(ctx.buffer);
2805
+ const pageSplitRegex = /(?:\f|\x0C|(?:\r?\n|^)\s*[-=_]{3,}\s*(?:PAGE|Page|page break|Page Break)[\s\d\w-]*[-=_]{3,}\s*(?:\r?\n|$))/i;
2806
+ const rawPages = fullText.split(pageSplitRegex).map((p) => p.trim()).filter((p) => p.length > 0);
2807
+ const totalPages = Math.max(1, rawPages.length);
2808
+ let currentPage = 1;
2536
2809
  const container = document.createElement("div");
2537
2810
  container.style.width = "100%";
2538
2811
  container.style.height = "100%";
@@ -2551,25 +2824,66 @@ var CodePlugin = class {
2551
2824
  pre.style.wordBreak = "break-all";
2552
2825
  const code = document.createElement("code");
2553
2826
  const ext = (ctx.metadata.extension || "").replace(".", "");
2554
- try {
2555
- if (ext && hljs__default.default.getLanguage(ext)) {
2556
- code.innerHTML = hljs__default.default.highlight(text, { language: ext }).value;
2557
- } else {
2558
- code.innerHTML = hljs__default.default.highlightAuto(text).value;
2827
+ const renderCodePage = (text) => {
2828
+ try {
2829
+ if (ext && hljs__default.default.getLanguage(ext)) {
2830
+ code.innerHTML = hljs__default.default.highlight(text, { language: ext }).value;
2831
+ } else {
2832
+ code.innerHTML = hljs__default.default.highlightAuto(text).value;
2833
+ }
2834
+ } catch {
2835
+ code.textContent = text;
2559
2836
  }
2560
- } catch {
2561
- code.textContent = text;
2562
- }
2837
+ };
2838
+ renderCodePage(rawPages[0] || fullText);
2563
2839
  pre.appendChild(code);
2564
2840
  container.appendChild(pre);
2841
+ let indicator = null;
2842
+ if (totalPages > 1) {
2843
+ indicator = document.createElement("div");
2844
+ indicator.className = "fp-code-page-indicator";
2845
+ indicator.style.position = "sticky";
2846
+ indicator.style.bottom = "16px";
2847
+ indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
2848
+ indicator.style.backdropFilter = "blur(8px)";
2849
+ indicator.style.color = "#f8fafc";
2850
+ indicator.style.fontSize = "12px";
2851
+ indicator.style.fontWeight = "600";
2852
+ indicator.style.padding = "5px 14px";
2853
+ indicator.style.borderRadius = "20px";
2854
+ indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
2855
+ indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
2856
+ indicator.style.zIndex = "10";
2857
+ indicator.style.userSelect = "none";
2858
+ indicator.style.pointerEvents = "none";
2859
+ indicator.style.textAlign = "center";
2860
+ indicator.style.width = "fit-content";
2861
+ indicator.style.margin = "16px auto 0";
2862
+ container.appendChild(indicator);
2863
+ }
2864
+ const showPage = (pageNum) => {
2865
+ currentPage = Math.max(1, Math.min(totalPages, pageNum));
2866
+ renderCodePage(rawPages[currentPage - 1] || fullText);
2867
+ if (indicator) {
2868
+ indicator.textContent = `Page ${currentPage} of ${totalPages}`;
2869
+ }
2870
+ ctx.emit("page-change", { page: currentPage, total: totalPages });
2871
+ };
2872
+ if (totalPages > 1) {
2873
+ showPage(1);
2874
+ }
2565
2875
  ctx.container.appendChild(container);
2566
2876
  const cleanup = () => {
2877
+ indicator?.remove();
2567
2878
  container.remove();
2568
2879
  ctx.container.innerHTML = "";
2569
2880
  };
2570
2881
  ctx.signal.addEventListener("abort", cleanup);
2571
2882
  return {
2572
2883
  destroy: cleanup,
2884
+ getPageCount: () => totalPages,
2885
+ getCurrentPage: () => currentPage,
2886
+ goToPage: (page) => showPage(page),
2573
2887
  zoomIn: () => {
2574
2888
  fontSize = Math.min(32, fontSize + 2);
2575
2889
  pre.style.fontSize = `${fontSize}px`;
@@ -2597,7 +2911,7 @@ var CodePlugin = class {
2597
2911
  window.print();
2598
2912
  },
2599
2913
  copy: () => {
2600
- navigator.clipboard?.writeText(text);
2914
+ navigator.clipboard?.writeText(fullText);
2601
2915
  }
2602
2916
  };
2603
2917
  }
@@ -3419,7 +3733,31 @@ var RtfPlugin = class {
3419
3733
  return this.extensions.includes(ext || "") || this.mimeTypes.includes(mime || "");
3420
3734
  }
3421
3735
  getToolbarActions(instance) {
3422
- return [
3736
+ const totalPages = instance.getPageCount?.() ?? 1;
3737
+ const actions = [];
3738
+ if (totalPages > 1) {
3739
+ actions.push({
3740
+ id: "page-nav",
3741
+ icon: "",
3742
+ label: "Page Navigation",
3743
+ type: "page-nav",
3744
+ group: "navigation",
3745
+ value: instance.getCurrentPage?.() ?? 1,
3746
+ max: totalPages,
3747
+ execute: (action, page) => {
3748
+ const cur = instance.getCurrentPage?.() ?? 1;
3749
+ const max = instance.getPageCount?.() ?? 1;
3750
+ if (action === "prev") {
3751
+ if (cur > 1) instance.goToPage?.(cur - 1);
3752
+ } else if (action === "next") {
3753
+ if (cur < max) instance.goToPage?.(cur + 1);
3754
+ } else if (typeof page === "number") {
3755
+ instance.goToPage?.(page);
3756
+ }
3757
+ }
3758
+ });
3759
+ }
3760
+ actions.push(
3423
3761
  {
3424
3762
  id: "zoom-out",
3425
3763
  icon: "zoom-out",
@@ -3460,7 +3798,8 @@ var RtfPlugin = class {
3460
3798
  group: "actions",
3461
3799
  execute: () => instance.print?.()
3462
3800
  }
3463
- ];
3801
+ );
3802
+ return actions;
3464
3803
  }
3465
3804
  async render(ctx) {
3466
3805
  const wrapper = document.createElement("div");
@@ -3479,28 +3818,82 @@ var RtfPlugin = class {
3479
3818
  ctx.container.style.backgroundColor = "#f1f5f9";
3480
3819
  ctx.container.appendChild(wrapper);
3481
3820
  let scale = 1;
3821
+ let pageElements = [];
3482
3822
  try {
3483
3823
  if (typeof RTFJS__namespace.loggingEnabled === "function") {
3484
3824
  RTFJS__namespace.loggingEnabled(false);
3485
3825
  }
3486
3826
  const doc = new RTFJS__namespace.Document(ctx.buffer, {});
3487
3827
  const htmlElements = await doc.render();
3488
- for (const el of htmlElements) {
3828
+ pageElements = htmlElements;
3829
+ for (let i = 0; i < htmlElements.length; i++) {
3830
+ const el = htmlElements[i];
3831
+ el.style.display = i === 0 ? "block" : "none";
3489
3832
  wrapper.appendChild(el);
3490
3833
  }
3491
3834
  } catch (err) {
3492
3835
  console.warn("[RtfPlugin] RTF render error, fallback text:", err);
3493
3836
  const text = new TextDecoder("latin1").decode(ctx.buffer);
3494
3837
  const clean = text.replace(/\\par[d]?/g, "\n").replace(/\\[a-zA-Z0-9\-]+/g, "").replace(/[{}]/g, "");
3495
- wrapper.innerHTML = `<pre style="white-space: pre-wrap; font-family: serif; color: #333;">${clean}</pre>`;
3838
+ const pre = document.createElement("pre");
3839
+ pre.style.whiteSpace = "pre-wrap";
3840
+ pre.style.fontFamily = "serif";
3841
+ pre.style.color = "#333";
3842
+ pre.textContent = clean;
3843
+ wrapper.appendChild(pre);
3844
+ pageElements = [pre];
3845
+ }
3846
+ const totalPages = Math.max(1, pageElements.length);
3847
+ let currentPage = 1;
3848
+ let indicator = null;
3849
+ if (totalPages > 1) {
3850
+ indicator = document.createElement("div");
3851
+ indicator.className = "fp-rtf-page-indicator";
3852
+ indicator.style.position = "sticky";
3853
+ indicator.style.bottom = "16px";
3854
+ indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
3855
+ indicator.style.backdropFilter = "blur(8px)";
3856
+ indicator.style.color = "#f8fafc";
3857
+ indicator.style.fontSize = "12px";
3858
+ indicator.style.fontWeight = "600";
3859
+ indicator.style.padding = "5px 14px";
3860
+ indicator.style.borderRadius = "20px";
3861
+ indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
3862
+ indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
3863
+ indicator.style.zIndex = "10";
3864
+ indicator.style.userSelect = "none";
3865
+ indicator.style.pointerEvents = "none";
3866
+ indicator.style.textAlign = "center";
3867
+ indicator.style.width = "fit-content";
3868
+ indicator.style.margin = "16px auto 0";
3869
+ ctx.container.appendChild(indicator);
3870
+ }
3871
+ const showPage = (pageNum) => {
3872
+ currentPage = Math.max(1, Math.min(totalPages, pageNum));
3873
+ if (totalPages > 1) {
3874
+ pageElements.forEach((el, idx) => {
3875
+ el.style.display = idx + 1 === currentPage ? "block" : "none";
3876
+ });
3877
+ }
3878
+ if (indicator) {
3879
+ indicator.textContent = `Page ${currentPage} of ${totalPages}`;
3880
+ }
3881
+ ctx.emit("page-change", { page: currentPage, total: totalPages });
3882
+ };
3883
+ if (totalPages > 1) {
3884
+ showPage(1);
3496
3885
  }
3497
3886
  const cleanup = () => {
3887
+ indicator?.remove();
3498
3888
  wrapper.remove();
3499
3889
  ctx.container.innerHTML = "";
3500
3890
  };
3501
3891
  ctx.signal.addEventListener("abort", cleanup);
3502
3892
  return {
3503
3893
  destroy: cleanup,
3894
+ getPageCount: () => totalPages,
3895
+ getCurrentPage: () => currentPage,
3896
+ goToPage: (page) => showPage(page),
3504
3897
  zoomIn: () => {
3505
3898
  scale += 0.1;
3506
3899
  wrapper.style.transform = `scale(${scale})`;
@@ -3688,41 +4081,41 @@ var OpenDocumentPlugin = class {
3688
4081
  }
3689
4082
  getToolbarActions(instance) {
3690
4083
  const isPresentation = instance.isPresentation;
4084
+ const totalPages = instance.getPageCount?.() ?? 1;
3691
4085
  const actions = [];
3692
- if (isPresentation) {
3693
- actions.push(
3694
- {
4086
+ if (isPresentation || totalPages > 1) {
4087
+ if (isPresentation) {
4088
+ actions.push({
3695
4089
  id: "thumbnails",
3696
4090
  icon: "thumbnails",
3697
4091
  label: "Slide Thumbnails",
3698
4092
  type: "button",
3699
4093
  group: "navigation",
3700
4094
  execute: () => instance.toggleThumbnails?.()
3701
- },
3702
- {
3703
- id: "page-nav",
3704
- icon: "",
3705
- label: "Slide Navigation",
3706
- type: "page-nav",
3707
- group: "navigation",
3708
- value: instance.getCurrentPage?.() ?? 1,
3709
- max: instance.getPageCount?.() ?? 1,
3710
- execute: (action, page) => {
3711
- if (action === "prev") {
3712
- const cur = instance.getCurrentPage?.() ?? 1;
3713
- if (cur > 1) instance.goToPage?.(cur - 1);
3714
- } else if (action === "next") {
3715
- const cur = instance.getCurrentPage?.() ?? 1;
3716
- const total = instance.getPageCount?.() ?? 1;
3717
- if (cur < total) instance.goToPage?.(cur + 1);
3718
- } else if (typeof page === "number") {
3719
- instance.goToPage?.(page);
3720
- } else if (typeof action === "number") {
3721
- instance.goToPage?.(action);
3722
- }
4095
+ });
4096
+ }
4097
+ actions.push({
4098
+ id: "page-nav",
4099
+ icon: "",
4100
+ label: isPresentation ? "Slide Navigation" : "Page Navigation",
4101
+ type: "page-nav",
4102
+ group: "navigation",
4103
+ value: instance.getCurrentPage?.() ?? 1,
4104
+ max: totalPages,
4105
+ execute: (action, page) => {
4106
+ const cur = instance.getCurrentPage?.() ?? 1;
4107
+ const max = instance.getPageCount?.() ?? 1;
4108
+ if (action === "prev") {
4109
+ if (cur > 1) instance.goToPage?.(cur - 1);
4110
+ } else if (action === "next") {
4111
+ if (cur < max) instance.goToPage?.(cur + 1);
4112
+ } else if (typeof page === "number") {
4113
+ instance.goToPage?.(page);
4114
+ } else if (typeof action === "number") {
4115
+ instance.goToPage?.(action);
3723
4116
  }
3724
4117
  }
3725
- );
4118
+ });
3726
4119
  }
3727
4120
  actions.push(
3728
4121
  {
@@ -4105,7 +4498,31 @@ var DocPlugin = class {
4105
4498
  return this.mimeTypes.includes(mime || "");
4106
4499
  }
4107
4500
  getToolbarActions(instance) {
4108
- return [
4501
+ const totalPages = instance.getPageCount?.() ?? 1;
4502
+ const actions = [];
4503
+ if (totalPages > 1) {
4504
+ actions.push({
4505
+ id: "page-nav",
4506
+ icon: "",
4507
+ label: "Page Navigation",
4508
+ type: "page-nav",
4509
+ group: "navigation",
4510
+ value: instance.getCurrentPage?.() ?? 1,
4511
+ max: totalPages,
4512
+ execute: (action, page) => {
4513
+ const cur = instance.getCurrentPage?.() ?? 1;
4514
+ const max = instance.getPageCount?.() ?? 1;
4515
+ if (action === "prev") {
4516
+ if (cur > 1) instance.goToPage?.(cur - 1);
4517
+ } else if (action === "next") {
4518
+ if (cur < max) instance.goToPage?.(cur + 1);
4519
+ } else if (typeof page === "number") {
4520
+ instance.goToPage?.(page);
4521
+ }
4522
+ }
4523
+ });
4524
+ }
4525
+ actions.push(
4109
4526
  {
4110
4527
  id: "zoom-out",
4111
4528
  icon: "zoom-out",
@@ -4154,7 +4571,8 @@ var DocPlugin = class {
4154
4571
  group: "actions",
4155
4572
  execute: () => instance.print?.()
4156
4573
  }
4157
- ];
4574
+ );
4575
+ return actions;
4158
4576
  }
4159
4577
  async render(ctx) {
4160
4578
  const container = document.createElement("div");
@@ -4181,6 +4599,7 @@ var DocPlugin = class {
4181
4599
  ctx.container.appendChild(container);
4182
4600
  let scale = 1;
4183
4601
  let extractedRawText = "";
4602
+ let isFallback = false;
4184
4603
  try {
4185
4604
  const cfbf = new CfbfReader(ctx.buffer);
4186
4605
  const wordDocStream = cfbf.readStream("WordDocument");
@@ -4194,26 +4613,89 @@ var DocPlugin = class {
4194
4613
  const tableStream = cfbf.readStream(tableName);
4195
4614
  const text = this.extractDocText(wordDocStream, tableStream);
4196
4615
  extractedRawText = text;
4197
- wrapper.innerHTML = this.formatDocToHtml(text, ctx.metadata.name || "Document");
4198
4616
  } catch (err) {
4199
4617
  console.warn("[DocPlugin] Binary parsing error, fallback text:", err);
4200
4618
  const fallback = this.heuristicTextExtraction(ctx.buffer);
4201
4619
  extractedRawText = fallback;
4202
- wrapper.innerHTML = `
4203
- <div style="border-bottom: 1px solid #e2e8f0; padding-bottom: 12px; margin-bottom: 24px;">
4204
- <h2 style="margin: 0 0 6px; font-size: 20px; color: #334155;">${ctx.metadata.name || "Word Document (.doc)"}</h2>
4205
- <span style="font-size: 12px; color: #64748b; background: #f1f5f9; padding: 2px 8px; border-radius: 4px;">Legacy Word 97-2003 Binary Preview</span>
4206
- </div>
4207
- ${this.formatDocToHtml(fallback, ctx.metadata.name || "Document")}
4208
- `;
4620
+ isFallback = true;
4621
+ }
4622
+ const rawPages = this.splitIntoPages(extractedRawText);
4623
+ const totalPages = Math.max(1, rawPages.length);
4624
+ let currentPage = 1;
4625
+ wrapper.innerHTML = "";
4626
+ const pageCards = [];
4627
+ for (let i = 0; i < totalPages; i++) {
4628
+ const pageCard = document.createElement("div");
4629
+ pageCard.className = "fp-doc-page-card";
4630
+ pageCard.style.backgroundColor = "#ffffff";
4631
+ pageCard.style.boxShadow = "0 2px 12px rgba(0,0,0,0.08)";
4632
+ pageCard.style.borderRadius = "4px";
4633
+ pageCard.style.padding = "56px 48px";
4634
+ pageCard.style.minHeight = "100%";
4635
+ pageCard.style.display = i === 0 ? "block" : "none";
4636
+ if (isFallback && i === 0) {
4637
+ pageCard.innerHTML = `
4638
+ <div style="border-bottom: 1px solid #e2e8f0; padding-bottom: 12px; margin-bottom: 24px;">
4639
+ <h2 style="margin: 0 0 6px; font-size: 20px; color: #334155;">${DOMPurify2__default.default.sanitize(ctx.metadata.name || "Word Document (.doc)")}</h2>
4640
+ <span style="font-size: 12px; color: #64748b; background: #f1f5f9; padding: 2px 8px; border-radius: 4px;">Legacy Word 97-2003 Binary Preview</span>
4641
+ </div>
4642
+ ${this.formatDocToHtml(rawPages[i], ctx.metadata.name || "Document")}
4643
+ `;
4644
+ } else {
4645
+ pageCard.innerHTML = this.formatDocToHtml(rawPages[i], ctx.metadata.name || "Document");
4646
+ }
4647
+ wrapper.appendChild(pageCard);
4648
+ pageCards.push(pageCard);
4649
+ }
4650
+ let indicator = null;
4651
+ if (totalPages > 1) {
4652
+ indicator = document.createElement("div");
4653
+ indicator.className = "fp-doc-page-indicator";
4654
+ indicator.style.position = "sticky";
4655
+ indicator.style.bottom = "16px";
4656
+ indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
4657
+ indicator.style.backdropFilter = "blur(8px)";
4658
+ indicator.style.color = "#f8fafc";
4659
+ indicator.style.fontSize = "12px";
4660
+ indicator.style.fontWeight = "600";
4661
+ indicator.style.padding = "5px 14px";
4662
+ indicator.style.borderRadius = "20px";
4663
+ indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
4664
+ indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
4665
+ indicator.style.zIndex = "10";
4666
+ indicator.style.userSelect = "none";
4667
+ indicator.style.pointerEvents = "none";
4668
+ indicator.style.textAlign = "center";
4669
+ indicator.style.width = "fit-content";
4670
+ indicator.style.margin = "16px auto 0";
4671
+ container.appendChild(indicator);
4672
+ }
4673
+ const showPage = (pageNum) => {
4674
+ currentPage = Math.max(1, Math.min(totalPages, pageNum));
4675
+ if (totalPages > 1) {
4676
+ pageCards.forEach((card, idx) => {
4677
+ card.style.display = idx + 1 === currentPage ? "block" : "none";
4678
+ });
4679
+ }
4680
+ if (indicator) {
4681
+ indicator.textContent = `Page ${currentPage} of ${totalPages}`;
4682
+ }
4683
+ ctx.emit("page-change", { page: currentPage, total: totalPages });
4684
+ };
4685
+ if (totalPages > 1) {
4686
+ showPage(1);
4209
4687
  }
4210
4688
  const cleanup = () => {
4689
+ indicator?.remove();
4211
4690
  container.remove();
4212
4691
  ctx.container.innerHTML = "";
4213
4692
  };
4214
4693
  ctx.signal.addEventListener("abort", cleanup);
4215
4694
  return {
4216
4695
  destroy: cleanup,
4696
+ getPageCount: () => totalPages,
4697
+ getCurrentPage: () => currentPage,
4698
+ goToPage: (page) => showPage(page),
4217
4699
  zoomIn: () => {
4218
4700
  scale += 0.1;
4219
4701
  wrapper.style.transform = `scale(${scale})`;
@@ -4345,11 +4827,16 @@ var DocPlugin = class {
4345
4827
  heuristicTextExtraction(buffer) {
4346
4828
  return this.extractStringsFromBytes(new Uint8Array(buffer));
4347
4829
  }
4830
+ splitIntoPages(text) {
4831
+ if (!text) return [""];
4832
+ 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);
4833
+ return parts.length > 0 ? parts : [text];
4834
+ }
4348
4835
  /**
4349
4836
  * Format cleaned extracted text into attractive HTML paragraphs, headings, and lists
4350
4837
  */
4351
4838
  formatDocToHtml(text, filename) {
4352
- const lines = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\x0B/g, "\n").replace(/\x0C/g, "\n\n").split("\n");
4839
+ const lines = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\x0B/g, "\n").split("\n");
4353
4840
  let html = "";
4354
4841
  let inList = false;
4355
4842
  for (const rawLine of lines) {