@files-preview-app/preview-file 1.3.2 → 1.3.3

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
@@ -3509,26 +3509,6 @@ var ExcelPlugin = class {
3509
3509
  instance.zoomIn?.();
3510
3510
  }
3511
3511
  },
3512
- {
3513
- id: "fit-page",
3514
- icon: "fit-page",
3515
- label: "Fit to View",
3516
- type: "button",
3517
- group: "zoom",
3518
- execute: () => {
3519
- instance.fitToPage?.();
3520
- }
3521
- },
3522
- {
3523
- id: "rotate-cw",
3524
- icon: "rotate-cw",
3525
- label: "Rotate",
3526
- type: "button",
3527
- group: "view",
3528
- execute: () => {
3529
- instance.rotateCW?.();
3530
- }
3531
- },
3532
3512
  {
3533
3513
  id: "download",
3534
3514
  icon: "download",
@@ -3589,12 +3569,11 @@ var ExcelPlugin = class {
3589
3569
  container.appendChild(tabsArea);
3590
3570
  ctx.container.appendChild(container);
3591
3571
  let scale = 1;
3592
- let rotation = 0;
3593
3572
  let currentSheetIndex = 1;
3594
3573
  let sheetNames = [];
3595
3574
  let wb = null;
3596
3575
  const applyTransform = () => {
3597
- contentArea.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
3576
+ contentArea.style.transform = `scale(${scale})`;
3598
3577
  contentArea.style.transformOrigin = "top left";
3599
3578
  };
3600
3579
  const calculateFitScale = () => {
@@ -3602,7 +3581,7 @@ var ExcelPlugin = class {
3602
3581
  if (!table) return 1;
3603
3582
  const availW = Math.max(280, container.clientWidth - 48);
3604
3583
  const tW = table.offsetWidth || 800;
3605
- return Math.max(0.65, Math.min(1, availW / tW));
3584
+ return Math.max(0.4, Math.min(1, availW / tW));
3606
3585
  };
3607
3586
  try {
3608
3587
  wb = XLSX__namespace.read(new Uint8Array(ctx.buffer), { type: "array", cellDates: true });
@@ -3657,7 +3636,16 @@ var ExcelPlugin = class {
3657
3636
  }
3658
3637
  });
3659
3638
  ctx.emit("page-change", { page: currentSheetIndex, total: sheetNames.length });
3639
+ requestAnimationFrame(() => {
3640
+ scale = calculateFitScale();
3641
+ applyTransform();
3642
+ });
3660
3643
  };
3644
+ const ro = new ResizeObserver(() => {
3645
+ scale = calculateFitScale();
3646
+ applyTransform();
3647
+ });
3648
+ ro.observe(container);
3661
3649
  if (sheetNames.length > 0) {
3662
3650
  sheetNames.forEach((name, idx) => {
3663
3651
  const btn = document.createElement("button");
@@ -3684,6 +3672,7 @@ var ExcelPlugin = class {
3684
3672
  `;
3685
3673
  }
3686
3674
  const cleanup = () => {
3675
+ ro.disconnect();
3687
3676
  container.remove();
3688
3677
  ctx.container.innerHTML = "";
3689
3678
  };
@@ -3705,16 +3694,11 @@ var ExcelPlugin = class {
3705
3694
  },
3706
3695
  fitToPage: () => {
3707
3696
  scale = calculateFitScale();
3708
- rotation = 0;
3709
3697
  applyTransform();
3710
3698
  },
3711
3699
  rotateCW: () => {
3712
- rotation = (rotation + 90) % 360;
3713
- applyTransform();
3714
3700
  },
3715
3701
  rotateCCW: () => {
3716
- rotation = (rotation - 90 + 360) % 360;
3717
- applyTransform();
3718
3702
  },
3719
3703
  goToPage: (page) => {
3720
3704
  if (page > 0 && page <= sheetNames.length) {
@@ -3812,38 +3796,15 @@ var CsvPlugin = class {
3812
3796
  return ext === ".csv" || ext === ".tsv" || mime === "text/csv" || mime === "text/tab-separated-values";
3813
3797
  }
3814
3798
  getToolbarActions(instance) {
3815
- const totalPages = instance.getPageCount?.() ?? 1;
3816
3799
  const actions = [];
3817
3800
  actions.push({
3818
3801
  id: "thumbnails",
3819
3802
  icon: "thumbnails",
3820
- label: "Page Thumbnails",
3803
+ label: "Sheet Thumbnails",
3821
3804
  type: "button",
3822
3805
  group: "navigation",
3823
3806
  execute: () => instance.toggleThumbnails?.()
3824
3807
  });
3825
- if (totalPages > 1) {
3826
- actions.push({
3827
- id: "page-nav",
3828
- icon: "",
3829
- label: "Page Navigation",
3830
- type: "page-nav",
3831
- group: "navigation",
3832
- value: instance.getCurrentPage?.() ?? 1,
3833
- max: totalPages,
3834
- execute: (action, page) => {
3835
- const cur = instance.getCurrentPage?.() ?? 1;
3836
- const max = instance.getPageCount?.() ?? 1;
3837
- if (action === "prev") {
3838
- if (cur > 1) instance.goToPage?.(cur - 1);
3839
- } else if (action === "next") {
3840
- if (cur < max) instance.goToPage?.(cur + 1);
3841
- } else if (typeof page === "number") {
3842
- instance.goToPage?.(page);
3843
- }
3844
- }
3845
- });
3846
- }
3847
3808
  actions.push(
3848
3809
  {
3849
3810
  id: "zoom-out",
@@ -3865,16 +3826,6 @@ var CsvPlugin = class {
3865
3826
  instance.zoomIn?.();
3866
3827
  }
3867
3828
  },
3868
- {
3869
- id: "fit-page",
3870
- icon: "fit-page",
3871
- label: "Fit to View",
3872
- type: "button",
3873
- group: "zoom",
3874
- execute: () => {
3875
- instance.fitToPage?.();
3876
- }
3877
- },
3878
3829
  {
3879
3830
  id: "download",
3880
3831
  icon: "download",
@@ -3917,131 +3868,135 @@ var CsvPlugin = class {
3917
3868
  container.style.overflow = "auto";
3918
3869
  container.style.padding = "16px";
3919
3870
  container.style.boxSizing = "border-box";
3920
- container.style.transformOrigin = "top left";
3871
+ const tableWrapper = document.createElement("div");
3872
+ tableWrapper.style.transformOrigin = "top left";
3921
3873
  const delimiter = ctx.metadata.extension === ".tsv" ? " " : ",";
3922
3874
  const allLines = text.split(/\r?\n/).filter((r) => r.trim().length > 0);
3923
3875
  const headerLine = allLines[0] || "";
3924
3876
  const headerCells = headerLine.split(delimiter);
3925
3877
  const dataLines = allLines.slice(1);
3926
- const ROWS_PER_PAGE = 100;
3927
- const totalPages = Math.max(1, Math.ceil(dataLines.length / ROWS_PER_PAGE));
3928
- let currentPage = 1;
3929
3878
  const table = document.createElement("table");
3930
3879
  table.style.borderCollapse = "collapse";
3931
- table.style.width = "100%";
3880
+ table.style.minWidth = "100%";
3932
3881
  table.style.fontFamily = '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, monospace';
3933
3882
  table.style.fontSize = "13px";
3934
- const renderPage = (pageNum) => {
3935
- currentPage = Math.max(1, Math.min(totalPages, pageNum));
3936
- table.innerHTML = "";
3937
- const headerTr = document.createElement("tr");
3938
- headerCells.forEach((cell) => {
3939
- const th = document.createElement("th");
3940
- th.textContent = cell.trim();
3941
- th.style.border = "1px solid #d0d7de";
3942
- th.style.padding = "8px 12px";
3943
- th.style.backgroundColor = "#f6f8fa";
3944
- th.style.fontWeight = "600";
3945
- th.style.whiteSpace = "nowrap";
3946
- headerTr.appendChild(th);
3947
- });
3948
- table.appendChild(headerTr);
3949
- const start = (currentPage - 1) * ROWS_PER_PAGE;
3950
- const end = Math.min(dataLines.length, start + ROWS_PER_PAGE);
3951
- const pageRows = dataLines.slice(start, end);
3952
- pageRows.forEach((row, idx) => {
3953
- const tr = document.createElement("tr");
3954
- if (idx % 2 === 1) tr.style.backgroundColor = "#f8fafc";
3955
- const cells = row.split(delimiter);
3956
- cells.forEach((cell) => {
3957
- const td = document.createElement("td");
3958
- td.textContent = cell.trim();
3959
- td.style.border = "1px solid #d0d7de";
3960
- td.style.padding = "6px 12px";
3961
- td.style.whiteSpace = "nowrap";
3962
- tr.appendChild(td);
3963
- });
3964
- table.appendChild(tr);
3883
+ table.style.background = "#ffffff";
3884
+ table.style.border = "1px solid #d0d7de";
3885
+ const headerTr = document.createElement("tr");
3886
+ headerCells.forEach((cell) => {
3887
+ const th = document.createElement("th");
3888
+ th.textContent = cell.trim();
3889
+ th.style.border = "1px solid #d0d7de";
3890
+ th.style.padding = "8px 12px";
3891
+ th.style.backgroundColor = "#f6f8fa";
3892
+ th.style.fontWeight = "600";
3893
+ th.style.whiteSpace = "nowrap";
3894
+ headerTr.appendChild(th);
3895
+ });
3896
+ table.appendChild(headerTr);
3897
+ dataLines.forEach((row, idx) => {
3898
+ const tr = document.createElement("tr");
3899
+ if (idx % 2 === 1) tr.style.backgroundColor = "#f8fafc";
3900
+ const cells = row.split(delimiter);
3901
+ cells.forEach((cell) => {
3902
+ const td = document.createElement("td");
3903
+ td.textContent = cell.trim();
3904
+ td.style.border = "1px solid #d0d7de";
3905
+ td.style.padding = "6px 12px";
3906
+ td.style.whiteSpace = "nowrap";
3907
+ tr.appendChild(td);
3965
3908
  });
3966
- container.scrollTop = 0;
3967
- ctx.emit("page-change", { page: currentPage, total: totalPages });
3968
- };
3969
- renderPage(1);
3970
- container.appendChild(table);
3909
+ table.appendChild(tr);
3910
+ });
3911
+ tableWrapper.appendChild(table);
3912
+ container.appendChild(tableWrapper);
3971
3913
  ctx.container.appendChild(container);
3972
3914
  let scale = 1;
3915
+ const calculateFitScale = () => {
3916
+ const availW = Math.max(280, container.clientWidth - 48);
3917
+ const tW = table.offsetWidth || 800;
3918
+ return Math.max(0.4, Math.min(1, availW / tW));
3919
+ };
3973
3920
  const applyScale = () => {
3974
- container.style.transform = `scale(${scale})`;
3975
- container.style.transformOrigin = "top left";
3921
+ tableWrapper.style.transform = `scale(${scale})`;
3976
3922
  };
3923
+ requestAnimationFrame(() => {
3924
+ scale = calculateFitScale();
3925
+ applyScale();
3926
+ });
3927
+ const ro = new ResizeObserver(() => {
3928
+ scale = calculateFitScale();
3929
+ applyScale();
3930
+ });
3931
+ ro.observe(container);
3977
3932
  const cleanup = () => {
3933
+ ro.disconnect();
3978
3934
  container.remove();
3979
3935
  ctx.container.innerHTML = "";
3980
3936
  };
3981
3937
  ctx.signal.addEventListener("abort", cleanup);
3982
3938
  return {
3983
3939
  destroy: cleanup,
3984
- getPageCount: () => totalPages,
3985
- getCurrentPage: () => currentPage,
3986
- goToPage: (page) => renderPage(page),
3940
+ getPageCount: () => 1,
3941
+ getCurrentPage: () => 1,
3942
+ goToPage: () => {
3943
+ },
3987
3944
  getThumbnails: () => {
3988
- const thumbnails = [];
3989
- for (let i = 0; i < totalPages; i++) {
3990
- const pageIdx = i;
3991
- const startRow = pageIdx * ROWS_PER_PAGE + 1;
3992
- const endRow = Math.min(dataLines.length, (pageIdx + 1) * ROWS_PER_PAGE);
3993
- const label = totalPages === 1 ? "Table" : `Page ${pageIdx + 1} (${startRow}-${endRow})`;
3994
- thumbnails.push({
3995
- index: pageIdx,
3996
- label,
3997
- render: async (canvas) => {
3998
- canvas.width = 140;
3999
- canvas.height = 100;
4000
- const c = canvas.getContext("2d");
4001
- if (!c) return;
4002
- c.fillStyle = "#ffffff";
4003
- c.fillRect(0, 0, canvas.width, canvas.height);
4004
- c.fillStyle = "#2563eb";
4005
- c.fillRect(0, 0, canvas.width, 16);
4006
- c.fillStyle = "#ffffff";
4007
- c.font = "bold 8px sans-serif";
4008
- c.fillText(`\u{1F4C8} ${label.slice(0, 18)}`, 6, 11);
4009
- const startY = 17;
4010
- const rowH = 13;
4011
- const colW = 28;
4012
- const cols = Math.min(4, headerCells.length || 4);
4013
- c.fillStyle = "#f1f5f9";
4014
- c.fillRect(0, startY, canvas.width, rowH);
4015
- c.strokeStyle = "#cbd5e1";
4016
- c.lineWidth = 0.8;
4017
- c.strokeRect(0, startY, canvas.width, rowH);
4018
- c.fillStyle = "#64748b";
4019
- c.font = "bold 7px sans-serif";
4020
- for (let ci = 0; ci < cols; ci++) {
4021
- const hName = headerCells[ci] || `Col ${ci + 1}`;
4022
- c.fillText(hName.slice(0, 5), 8 + ci * colW, startY + 9);
4023
- }
4024
- const rowsToPreview = dataLines.slice(pageIdx * ROWS_PER_PAGE, pageIdx * ROWS_PER_PAGE + 5);
4025
- for (let ri = 0; ri < rowsToPreview.length; ri++) {
4026
- const y = startY + (ri + 1) * rowH;
4027
- if (y > canvas.height - 2) break;
4028
- c.strokeStyle = "#e2e8f0";
4029
- c.strokeRect(0, y, canvas.width, rowH);
4030
- const cVals = rowsToPreview[ri].split(delimiter);
4031
- c.fillStyle = "#334155";
4032
- c.font = "6px sans-serif";
4033
- for (let ci = 0; ci < cols; ci++) {
4034
- const val = cVals[ci] || "";
4035
- c.fillText(val.trim().slice(0, 6), 8 + ci * colW, y + 9);
3945
+ const sheetName = ctx.metadata.name?.replace(/\.[^/.]+$/, "") || "Sheet1";
3946
+ return [{
3947
+ index: 0,
3948
+ label: sheetName,
3949
+ render: async (canvas) => {
3950
+ canvas.width = 140;
3951
+ canvas.height = 100;
3952
+ const c = canvas.getContext("2d");
3953
+ if (!c) return;
3954
+ c.fillStyle = "#ffffff";
3955
+ c.fillRect(0, 0, canvas.width, canvas.height);
3956
+ c.fillStyle = "#107c41";
3957
+ c.fillRect(0, 0, canvas.width, 16);
3958
+ c.fillStyle = "#ffffff";
3959
+ c.font = 'bold 8px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
3960
+ c.fillText(`\u{1F4CA} ${sheetName.slice(0, 16)}`, 6, 11);
3961
+ const startY = 17;
3962
+ const rowH = 13;
3963
+ const colW = 30;
3964
+ const cols = headerCells.slice(0, 4).map((h) => h.trim() || "Col");
3965
+ c.fillStyle = "#f1f5f9";
3966
+ c.fillRect(0, startY, canvas.width, rowH);
3967
+ c.strokeStyle = "#cbd5e1";
3968
+ c.lineWidth = 0.8;
3969
+ c.strokeRect(0, startY, canvas.width, rowH);
3970
+ c.fillStyle = "#64748b";
3971
+ c.font = "bold 7px sans-serif";
3972
+ for (let ci = 0; ci < cols.length; ci++) {
3973
+ c.fillText(cols[ci].slice(0, 5), 14 + ci * colW, startY + 9);
3974
+ }
3975
+ for (let ri = 0; ri < Math.min(5, dataLines.length); ri++) {
3976
+ const y = startY + (ri + 1) * rowH;
3977
+ if (y > canvas.height - 2) break;
3978
+ c.fillStyle = "#f8fafc";
3979
+ c.fillRect(0, y, 12, rowH);
3980
+ c.fillStyle = "#94a3b8";
3981
+ c.font = "6px sans-serif";
3982
+ c.fillText(String(ri + 1), 3, y + 9);
3983
+ c.strokeStyle = "#e2e8f0";
3984
+ c.strokeRect(0, y, canvas.width, rowH);
3985
+ const rowCells = dataLines[ri].split(delimiter);
3986
+ c.fillStyle = "#334155";
3987
+ c.font = "6px sans-serif";
3988
+ for (let ci = 0; ci < cols.length; ci++) {
3989
+ const val = (rowCells[ci] || "").trim();
3990
+ if (val) {
3991
+ c.fillText(val.slice(0, 6), 14 + ci * colW, y + 9);
4036
3992
  }
4037
3993
  }
4038
- c.strokeStyle = "#cbd5e1";
4039
- c.lineWidth = 1;
4040
- c.strokeRect(0.5, 0.5, canvas.width - 1, canvas.height - 1);
4041
3994
  }
4042
- });
4043
- }
4044
- return thumbnails;
3995
+ c.strokeStyle = "#cbd5e1";
3996
+ c.lineWidth = 1;
3997
+ c.strokeRect(0.5, 0.5, canvas.width - 1, canvas.height - 1);
3998
+ }
3999
+ }];
4045
4000
  },
4046
4001
  zoomIn: () => {
4047
4002
  scale += 0.1;
@@ -4057,11 +4012,13 @@ var CsvPlugin = class {
4057
4012
  applyScale();
4058
4013
  },
4059
4014
  fitToPage: () => {
4060
- const availW = Math.max(280, (ctx.container.clientWidth || window.innerWidth) - 48);
4061
- const tW = table.offsetWidth || 600;
4062
- scale = Math.max(0.35, Math.min(1, availW / tW));
4015
+ scale = calculateFitScale();
4063
4016
  applyScale();
4064
4017
  },
4018
+ rotateCW: () => {
4019
+ },
4020
+ rotateCCW: () => {
4021
+ },
4065
4022
  download: () => {
4066
4023
  const blob = new Blob([ctx.buffer], { type: "text/csv" });
4067
4024
  const url = URL.createObjectURL(blob);
@@ -4173,26 +4130,6 @@ var CodePlugin = class {
4173
4130
  instance.zoomIn?.();
4174
4131
  }
4175
4132
  },
4176
- {
4177
- id: "fit-page",
4178
- icon: "fit-page",
4179
- label: "Fit to View",
4180
- type: "button",
4181
- group: "zoom",
4182
- execute: () => {
4183
- instance.fitToPage?.();
4184
- }
4185
- },
4186
- {
4187
- id: "rotate-cw",
4188
- icon: "rotate-cw",
4189
- label: "Rotate",
4190
- type: "button",
4191
- group: "view",
4192
- execute: () => {
4193
- instance.rotateCW?.();
4194
- }
4195
- },
4196
4133
  {
4197
4134
  id: "copy",
4198
4135
  icon: "copy",
@@ -5336,6 +5273,148 @@ var ThreeDPlugin = class {
5336
5273
  function threeDPlugin() {
5337
5274
  return new ThreeDPlugin();
5338
5275
  }
5276
+ function cleanRtfText(raw) {
5277
+ return raw.replace(/\\bin\d+\s/g, "").replace(/{\\\*[^}]+}/g, "").replace(/\\u(-?\d+)\??/g, (_, code) => {
5278
+ let num = parseInt(code, 10);
5279
+ if (num < 0) num += 65536;
5280
+ return String.fromCharCode(num);
5281
+ }).replace(/\\'([0-9a-fA-F]{2})/g, (_, hex) => {
5282
+ return String.fromCharCode(parseInt(hex, 16));
5283
+ }).replace(/\\tab\b/g, " ").replace(/\\emdash\b/g, "\u2014").replace(/\\endash\b/g, "\u2013").replace(/\\bullet\b/g, "\u2022").replace(/\\lquote\b/g, "\u2018").replace(/\\rquote\b/g, "\u2019").replace(/\\ldblquote\b/g, "\u201C").replace(/\\rdblquote\b/g, "\u201D").replace(/\\\w+(?:-?\d+)?\s?/g, " ").replace(/[{}\r\n]/g, " ").replace(/\s+/g, " ").trim();
5284
+ }
5285
+ function parseRtfTables(rtfText) {
5286
+ const rowSegments = rtfText.split(/\\row\b/);
5287
+ if (rowSegments.length <= 1) return [];
5288
+ const rawTables = [];
5289
+ let currentTable = null;
5290
+ let charOffset = 0;
5291
+ for (let i = 0; i < rowSegments.length - 1; i++) {
5292
+ const segment = rowSegments[i];
5293
+ const segmentStart = charOffset;
5294
+ const segmentEnd = charOffset + segment.length + 4;
5295
+ const cellParts = segment.split(/\\cell\b/);
5296
+ if (cellParts.length > 1) {
5297
+ const cellxMatches = [...segment.matchAll(/\\cellx(\d+)/g)].map((m) => parseInt(m[1], 10));
5298
+ let cellWidths = [];
5299
+ if (cellxMatches.length > 0) {
5300
+ let seq = [cellxMatches[0]];
5301
+ for (let k = 1; k < cellxMatches.length; k++) {
5302
+ if (cellxMatches[k] > cellxMatches[k - 1]) seq.push(cellxMatches[k]);
5303
+ else break;
5304
+ }
5305
+ let prev = 0;
5306
+ cellWidths = seq.map((x) => {
5307
+ const w = x - prev;
5308
+ prev = x;
5309
+ return w;
5310
+ });
5311
+ }
5312
+ const cells = [];
5313
+ for (let c = 0; c < cellParts.length - 1; c++) {
5314
+ let rawCell = cellParts[c];
5315
+ if (c === 0) {
5316
+ const lastTrowd = rawCell.lastIndexOf("\\trowd");
5317
+ if (lastTrowd !== -1) rawCell = rawCell.slice(lastTrowd);
5318
+ }
5319
+ cells.push(cleanRtfText(rawCell));
5320
+ }
5321
+ const isNewTable = !currentTable || segmentStart - currentTable.lastEnd > 2500;
5322
+ if (isNewTable) {
5323
+ const prevTextSlice = rtfText.slice(Math.max(0, segmentStart - 4e3), segmentStart);
5324
+ const cleanPrev = cleanRtfText(prevTextSlice);
5325
+ const anchorBefore = cleanPrev.slice(-80).trim();
5326
+ currentTable = {
5327
+ id: `RTF_TABLE_${rawTables.length}`,
5328
+ startPos: segmentStart,
5329
+ lastEnd: segmentEnd,
5330
+ rows: [],
5331
+ columnWidths: cellWidths,
5332
+ anchorBefore,
5333
+ anchorAfter: ""
5334
+ };
5335
+ rawTables.push(currentTable);
5336
+ }
5337
+ if (currentTable) {
5338
+ currentTable.rows.push({
5339
+ cells,
5340
+ widths: cellWidths.length === cells.length ? cellWidths : currentTable.columnWidths
5341
+ });
5342
+ currentTable.lastEnd = segmentEnd;
5343
+ }
5344
+ }
5345
+ charOffset += segment.length + 4;
5346
+ }
5347
+ rawTables.forEach((tbl) => {
5348
+ const afterSlice = rtfText.slice(tbl.lastEnd, Math.min(rtfText.length, tbl.lastEnd + 2e3));
5349
+ const cleanAfter = cleanRtfText(afterSlice);
5350
+ tbl.anchorAfter = cleanAfter.slice(0, 80).trim();
5351
+ });
5352
+ return rawTables.map((tbl) => {
5353
+ const totalW = (tbl.columnWidths || []).reduce((a, b) => a + b, 0);
5354
+ const colPercents = totalW > 0 ? tbl.columnWidths.map((w) => Math.round(w / totalW * 100)) : [];
5355
+ return {
5356
+ id: tbl.id,
5357
+ rows: tbl.rows.map((r) => r.cells),
5358
+ colPercents,
5359
+ anchorBefore: tbl.anchorBefore,
5360
+ anchorAfter: tbl.anchorAfter
5361
+ };
5362
+ });
5363
+ }
5364
+ function renderRtfTableElement(table) {
5365
+ const tableEl = document.createElement("table");
5366
+ tableEl.className = "fp-rtf-table";
5367
+ tableEl.style.width = "100%";
5368
+ tableEl.style.borderCollapse = "collapse";
5369
+ tableEl.style.margin = "16px 0 20px";
5370
+ tableEl.style.fontSize = "10pt";
5371
+ tableEl.style.lineHeight = "1.5";
5372
+ tableEl.style.border = "1px solid #cbd5e1";
5373
+ tableEl.style.borderRadius = "4px";
5374
+ tableEl.style.boxSizing = "border-box";
5375
+ tableEl.style.backgroundColor = "#ffffff";
5376
+ const thead = document.createElement("thead");
5377
+ const tbody = document.createElement("tbody");
5378
+ table.rows.forEach((rowCells, rIdx) => {
5379
+ const tr = document.createElement("tr");
5380
+ const isHeader = rIdx === 0;
5381
+ if (isHeader) {
5382
+ tr.style.backgroundColor = "#f8fafc";
5383
+ tr.style.borderBottom = "2px solid #cbd5e1";
5384
+ } else {
5385
+ tr.style.borderBottom = "1px solid #e2e8f0";
5386
+ if (rIdx % 2 === 0) {
5387
+ tr.style.backgroundColor = "#fcfdfe";
5388
+ }
5389
+ }
5390
+ rowCells.forEach((cellText, cIdx) => {
5391
+ const cellEl = document.createElement(isHeader ? "th" : "td");
5392
+ cellEl.style.padding = "8px 12px";
5393
+ cellEl.style.border = "1px solid #cbd5e1";
5394
+ cellEl.style.verticalAlign = "top";
5395
+ cellEl.style.color = isHeader ? "#0f172a" : "#1e293b";
5396
+ cellEl.style.fontWeight = isHeader ? "600" : "normal";
5397
+ if (table.colPercents && table.colPercents[cIdx]) {
5398
+ cellEl.style.width = `${table.colPercents[cIdx]}%`;
5399
+ }
5400
+ if (/^\d+$/.test(cellText.trim()) || cellText.trim() === "#") {
5401
+ cellEl.style.textAlign = "center";
5402
+ } else {
5403
+ cellEl.style.textAlign = "left";
5404
+ }
5405
+ cellEl.textContent = cellText;
5406
+ tr.appendChild(cellEl);
5407
+ });
5408
+ if (isHeader) {
5409
+ thead.appendChild(tr);
5410
+ } else {
5411
+ tbody.appendChild(tr);
5412
+ }
5413
+ });
5414
+ tableEl.appendChild(thead);
5415
+ tableEl.appendChild(tbody);
5416
+ return tableEl;
5417
+ }
5339
5418
  var RtfPlugin = class {
5340
5419
  id = "rtf";
5341
5420
  name = "Rich Text Format (RTF)";
@@ -5371,6 +5450,14 @@ var RtfPlugin = class {
5371
5450
  }
5372
5451
  });
5373
5452
  actions.push(
5453
+ {
5454
+ id: "thumbnails",
5455
+ icon: "thumbnails",
5456
+ label: "Thumbnails",
5457
+ type: "button",
5458
+ group: "view",
5459
+ execute: () => instance.toggleThumbnails?.()
5460
+ },
5374
5461
  {
5375
5462
  id: "zoom-out",
5376
5463
  icon: "zoom-out",
@@ -5498,6 +5585,8 @@ var RtfPlugin = class {
5498
5585
  if (typeof RTFJS__namespace.loggingEnabled === "function") {
5499
5586
  RTFJS__namespace.loggingEnabled(false);
5500
5587
  }
5588
+ const rawText = new TextDecoder("latin1").decode(ctx.buffer);
5589
+ const tables = parseRtfTables(rawText);
5501
5590
  const doc = new RTFJS__namespace.Document(ctx.buffer, {});
5502
5591
  const htmlElements = await doc.render();
5503
5592
  const contentNodes = [];
@@ -5512,6 +5601,20 @@ var RtfPlugin = class {
5512
5601
  if (hasChildBlocks && !["table", "tr", "td", "th", "ul", "ol", "li"].includes(tag)) {
5513
5602
  extractBlocks(Array.from(item.children));
5514
5603
  } else {
5604
+ const svgs = item.querySelectorAll("svg, img");
5605
+ svgs.forEach((s) => {
5606
+ const el = s;
5607
+ el.style.maxWidth = "100%";
5608
+ el.style.maxHeight = "360px";
5609
+ el.style.display = "block";
5610
+ el.style.margin = "12px auto";
5611
+ if (s.tagName.toLowerCase() === "svg") {
5612
+ s.removeAttribute("width");
5613
+ s.removeAttribute("height");
5614
+ el.style.width = "100%";
5615
+ el.style.maxHeight = "360px";
5616
+ }
5617
+ });
5515
5618
  if (tag === "p" || tag === "div") {
5516
5619
  item.style.display = "block";
5517
5620
  item.style.marginBottom = "12px";
@@ -5522,14 +5625,46 @@ var RtfPlugin = class {
5522
5625
  }
5523
5626
  };
5524
5627
  extractBlocks(htmlElements);
5628
+ tables.forEach((table) => {
5629
+ const tableEl = renderRtfTableElement(table);
5630
+ let inserted = false;
5631
+ if (table.anchorBefore && table.anchorBefore.length > 10) {
5632
+ const keyword = table.anchorBefore.slice(-35).trim();
5633
+ for (let i = 0; i < contentNodes.length; i++) {
5634
+ if (contentNodes[i].textContent?.includes(keyword)) {
5635
+ contentNodes.splice(i + 1, 0, tableEl);
5636
+ inserted = true;
5637
+ break;
5638
+ }
5639
+ }
5640
+ }
5641
+ if (!inserted && table.anchorAfter && table.anchorAfter.length > 10) {
5642
+ const keyword = table.anchorAfter.slice(0, 35).trim();
5643
+ for (let i = 0; i < contentNodes.length; i++) {
5644
+ if (contentNodes[i].textContent?.includes(keyword)) {
5645
+ contentNodes.splice(i, 0, tableEl);
5646
+ inserted = true;
5647
+ break;
5648
+ }
5649
+ }
5650
+ }
5651
+ if (!inserted) {
5652
+ if (contentNodes.length > 2) {
5653
+ contentNodes.splice(2, 0, tableEl);
5654
+ } else {
5655
+ contentNodes.push(tableEl);
5656
+ }
5657
+ }
5658
+ });
5525
5659
  wrapper.innerHTML = "";
5526
5660
  contentNodes.forEach((node) => wrapper.appendChild(node));
5527
5661
  const childHeights = contentNodes.map((c) => {
5528
5662
  const rectH = c.getBoundingClientRect ? c.getBoundingClientRect().height : 0;
5529
5663
  const offH = c.offsetHeight || 0;
5664
+ const realH = Math.max(rectH, offH);
5665
+ if (realH > 0) return realH;
5530
5666
  const textLen = c.textContent?.trim().length || 0;
5531
- const estH = Math.max(28, Math.ceil(textLen / 75) * 24 + 16);
5532
- return Math.max(rectH, offH, estH);
5667
+ return Math.max(24, Math.ceil(textLen / 95) * 22 + 12);
5533
5668
  });
5534
5669
  wrapper.innerHTML = "";
5535
5670
  const createRtfCard = () => {
@@ -5538,7 +5673,7 @@ var RtfPlugin = class {
5538
5673
  card.style.backgroundColor = "#ffffff";
5539
5674
  card.style.boxShadow = "0 4px 24px rgba(0,0,0,0.08)";
5540
5675
  card.style.borderRadius = "4px";
5541
- card.style.padding = "72px 56px";
5676
+ card.style.padding = "64px 56px";
5542
5677
  card.style.width = "816px";
5543
5678
  card.style.minHeight = "1056px";
5544
5679
  card.style.boxSizing = "border-box";
@@ -5552,22 +5687,35 @@ var RtfPlugin = class {
5552
5687
  wrapper.appendChild(curCard);
5553
5688
  pageElements = [curCard];
5554
5689
  let curH = 0;
5555
- const maxH = 912;
5690
+ const maxH = 928;
5556
5691
  for (let i = 0; i < contentNodes.length; i++) {
5557
5692
  const child = contentNodes[i];
5558
5693
  const chH = childHeights[i];
5559
- curCard.appendChild(child);
5560
- curH += chH;
5561
- if (curH >= maxH && i < contentNodes.length - 1) {
5694
+ const isChildEmpty = (child.textContent || "").trim().length === 0 && !child.querySelector("table, img, svg, canvas");
5695
+ if (curH === 0 && isChildEmpty) {
5696
+ continue;
5697
+ }
5698
+ if (curH + chH > maxH && curCard.childNodes.length > 0) {
5562
5699
  curCard = createRtfCard();
5563
5700
  wrapper.appendChild(curCard);
5564
5701
  pageElements.push(curCard);
5565
5702
  curH = 0;
5566
5703
  }
5704
+ curCard.appendChild(child);
5705
+ curH += chH;
5706
+ }
5707
+ pageElements = pageElements.filter((card) => {
5708
+ const hasText = (card.textContent || "").trim().length > 0;
5709
+ const hasMedia = card.querySelector("table, img, svg, canvas") !== null;
5710
+ return hasText || hasMedia;
5711
+ });
5712
+ if (pageElements.length === 0) {
5713
+ pageElements = [curCard];
5567
5714
  }
5568
5715
  } catch (err) {
5569
5716
  console.warn("[RtfPlugin] RTF render error, fallback text:", err);
5570
5717
  const text = new TextDecoder("latin1").decode(ctx.buffer);
5718
+ const tables = parseRtfTables(text);
5571
5719
  const rawPages = text.split(/\\page\b/).map((segment) => {
5572
5720
  return segment.replace(/\\par[d]?/g, "\n").replace(/\\[a-zA-Z0-9\-]+/g, "").replace(/[{}]/g, "").trim();
5573
5721
  }).filter((p) => p.length > 0);
@@ -5578,7 +5726,7 @@ var RtfPlugin = class {
5578
5726
  pageCard.style.backgroundColor = "#ffffff";
5579
5727
  pageCard.style.boxShadow = "0 2px 12px rgba(0,0,0,0.08)";
5580
5728
  pageCard.style.borderRadius = "4px";
5581
- pageCard.style.padding = "72px 56px";
5729
+ pageCard.style.padding = "64px 56px";
5582
5730
  pageCard.style.width = "816px";
5583
5731
  pageCard.style.minHeight = "1056px";
5584
5732
  pageCard.style.boxSizing = "border-box";
@@ -5589,6 +5737,9 @@ var RtfPlugin = class {
5589
5737
  pageCard.style.whiteSpace = "pre-wrap";
5590
5738
  pageCard.style.display = i === 0 ? "block" : "none";
5591
5739
  pageCard.textContent = pages[i];
5740
+ if (i === 1 && tables.length > 0) {
5741
+ tables.forEach((tbl) => pageCard.appendChild(renderRtfTableElement(tbl)));
5742
+ }
5592
5743
  wrapper.appendChild(pageCard);
5593
5744
  pageElements.push(pageCard);
5594
5745
  }
@@ -5619,6 +5770,32 @@ var RtfPlugin = class {
5619
5770
  getPageCount: () => totalPages,
5620
5771
  getCurrentPage: () => currentPage,
5621
5772
  goToPage: (page) => showPage(page),
5773
+ getThumbnails: async () => {
5774
+ const items = [];
5775
+ for (let idx = 0; idx < totalPages; idx++) {
5776
+ const pageIdx = idx;
5777
+ items.push({
5778
+ index: pageIdx,
5779
+ label: `Page ${pageIdx + 1}`,
5780
+ render: async (canvas) => {
5781
+ const ctx2d = canvas.getContext("2d");
5782
+ if (!ctx2d) return;
5783
+ canvas.width = 140;
5784
+ canvas.height = 180;
5785
+ ctx2d.fillStyle = "#ffffff";
5786
+ ctx2d.fillRect(0, 0, 140, 180);
5787
+ ctx2d.strokeStyle = "#cbd5e1";
5788
+ ctx2d.lineWidth = 1;
5789
+ ctx2d.strokeRect(0.5, 0.5, 139, 179);
5790
+ ctx2d.fillStyle = "#334155";
5791
+ ctx2d.font = 'bold 11px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
5792
+ ctx2d.textAlign = "center";
5793
+ ctx2d.fillText(`Page ${pageIdx + 1}`, 70, 95);
5794
+ }
5795
+ });
5796
+ }
5797
+ return items;
5798
+ },
5622
5799
  zoomIn: () => {
5623
5800
  isUserZoomed = true;
5624
5801
  scale += 0.15;
@@ -7656,6 +7833,8 @@ var FilePreview = react.memo(react.forwardRef(
7656
7833
  onError,
7657
7834
  onPageChange,
7658
7835
  onZoomChange,
7836
+ onRotate,
7837
+ onDestroy,
7659
7838
  className,
7660
7839
  style
7661
7840
  }, ref) => {
@@ -7678,6 +7857,8 @@ var FilePreview = react.memo(react.forwardRef(
7678
7857
  if (onError) unsubs.push(viewer.on("error", (e) => onError(e instanceof Error ? e : new Error(String(e)))));
7679
7858
  if (onPageChange) unsubs.push(viewer.on("page-change", onPageChange));
7680
7859
  if (onZoomChange) unsubs.push(viewer.on("zoom-change", onZoomChange));
7860
+ if (onRotate) unsubs.push(viewer.on("rotate", onRotate));
7861
+ if (onDestroy) unsubs.push(viewer.on("destroy", onDestroy));
7681
7862
  return () => {
7682
7863
  unsubs.forEach((fn) => fn());
7683
7864
  viewer.destroy();