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