@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/index.cjs CHANGED
@@ -3590,26 +3590,6 @@ var ExcelPlugin = class {
3590
3590
  instance.zoomIn?.();
3591
3591
  }
3592
3592
  },
3593
- {
3594
- id: "fit-page",
3595
- icon: "fit-page",
3596
- label: "Fit to View",
3597
- type: "button",
3598
- group: "zoom",
3599
- execute: () => {
3600
- instance.fitToPage?.();
3601
- }
3602
- },
3603
- {
3604
- id: "rotate-cw",
3605
- icon: "rotate-cw",
3606
- label: "Rotate",
3607
- type: "button",
3608
- group: "view",
3609
- execute: () => {
3610
- instance.rotateCW?.();
3611
- }
3612
- },
3613
3593
  {
3614
3594
  id: "download",
3615
3595
  icon: "download",
@@ -3670,12 +3650,11 @@ var ExcelPlugin = class {
3670
3650
  container.appendChild(tabsArea);
3671
3651
  ctx.container.appendChild(container);
3672
3652
  let scale = 1;
3673
- let rotation = 0;
3674
3653
  let currentSheetIndex = 1;
3675
3654
  let sheetNames = [];
3676
3655
  let wb = null;
3677
3656
  const applyTransform = () => {
3678
- contentArea.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
3657
+ contentArea.style.transform = `scale(${scale})`;
3679
3658
  contentArea.style.transformOrigin = "top left";
3680
3659
  };
3681
3660
  const calculateFitScale = () => {
@@ -3683,7 +3662,7 @@ var ExcelPlugin = class {
3683
3662
  if (!table) return 1;
3684
3663
  const availW = Math.max(280, container.clientWidth - 48);
3685
3664
  const tW = table.offsetWidth || 800;
3686
- return Math.max(0.65, Math.min(1, availW / tW));
3665
+ return Math.max(0.4, Math.min(1, availW / tW));
3687
3666
  };
3688
3667
  try {
3689
3668
  wb = XLSX__namespace.read(new Uint8Array(ctx.buffer), { type: "array", cellDates: true });
@@ -3738,7 +3717,16 @@ var ExcelPlugin = class {
3738
3717
  }
3739
3718
  });
3740
3719
  ctx.emit("page-change", { page: currentSheetIndex, total: sheetNames.length });
3720
+ requestAnimationFrame(() => {
3721
+ scale = calculateFitScale();
3722
+ applyTransform();
3723
+ });
3741
3724
  };
3725
+ const ro = new ResizeObserver(() => {
3726
+ scale = calculateFitScale();
3727
+ applyTransform();
3728
+ });
3729
+ ro.observe(container);
3742
3730
  if (sheetNames.length > 0) {
3743
3731
  sheetNames.forEach((name, idx) => {
3744
3732
  const btn = document.createElement("button");
@@ -3765,6 +3753,7 @@ var ExcelPlugin = class {
3765
3753
  `;
3766
3754
  }
3767
3755
  const cleanup = () => {
3756
+ ro.disconnect();
3768
3757
  container.remove();
3769
3758
  ctx.container.innerHTML = "";
3770
3759
  };
@@ -3786,16 +3775,11 @@ var ExcelPlugin = class {
3786
3775
  },
3787
3776
  fitToPage: () => {
3788
3777
  scale = calculateFitScale();
3789
- rotation = 0;
3790
3778
  applyTransform();
3791
3779
  },
3792
3780
  rotateCW: () => {
3793
- rotation = (rotation + 90) % 360;
3794
- applyTransform();
3795
3781
  },
3796
3782
  rotateCCW: () => {
3797
- rotation = (rotation - 90 + 360) % 360;
3798
- applyTransform();
3799
3783
  },
3800
3784
  goToPage: (page) => {
3801
3785
  if (page > 0 && page <= sheetNames.length) {
@@ -3893,38 +3877,15 @@ var CsvPlugin = class {
3893
3877
  return ext === ".csv" || ext === ".tsv" || mime === "text/csv" || mime === "text/tab-separated-values";
3894
3878
  }
3895
3879
  getToolbarActions(instance) {
3896
- const totalPages = instance.getPageCount?.() ?? 1;
3897
3880
  const actions = [];
3898
3881
  actions.push({
3899
3882
  id: "thumbnails",
3900
3883
  icon: "thumbnails",
3901
- label: "Page Thumbnails",
3884
+ label: "Sheet Thumbnails",
3902
3885
  type: "button",
3903
3886
  group: "navigation",
3904
3887
  execute: () => instance.toggleThumbnails?.()
3905
3888
  });
3906
- if (totalPages > 1) {
3907
- actions.push({
3908
- id: "page-nav",
3909
- icon: "",
3910
- label: "Page Navigation",
3911
- type: "page-nav",
3912
- group: "navigation",
3913
- value: instance.getCurrentPage?.() ?? 1,
3914
- max: totalPages,
3915
- execute: (action, page) => {
3916
- const cur = instance.getCurrentPage?.() ?? 1;
3917
- const max = instance.getPageCount?.() ?? 1;
3918
- if (action === "prev") {
3919
- if (cur > 1) instance.goToPage?.(cur - 1);
3920
- } else if (action === "next") {
3921
- if (cur < max) instance.goToPage?.(cur + 1);
3922
- } else if (typeof page === "number") {
3923
- instance.goToPage?.(page);
3924
- }
3925
- }
3926
- });
3927
- }
3928
3889
  actions.push(
3929
3890
  {
3930
3891
  id: "zoom-out",
@@ -3946,16 +3907,6 @@ var CsvPlugin = class {
3946
3907
  instance.zoomIn?.();
3947
3908
  }
3948
3909
  },
3949
- {
3950
- id: "fit-page",
3951
- icon: "fit-page",
3952
- label: "Fit to View",
3953
- type: "button",
3954
- group: "zoom",
3955
- execute: () => {
3956
- instance.fitToPage?.();
3957
- }
3958
- },
3959
3910
  {
3960
3911
  id: "download",
3961
3912
  icon: "download",
@@ -3998,131 +3949,135 @@ var CsvPlugin = class {
3998
3949
  container.style.overflow = "auto";
3999
3950
  container.style.padding = "16px";
4000
3951
  container.style.boxSizing = "border-box";
4001
- container.style.transformOrigin = "top left";
3952
+ const tableWrapper = document.createElement("div");
3953
+ tableWrapper.style.transformOrigin = "top left";
4002
3954
  const delimiter = ctx.metadata.extension === ".tsv" ? " " : ",";
4003
3955
  const allLines = text.split(/\r?\n/).filter((r) => r.trim().length > 0);
4004
3956
  const headerLine = allLines[0] || "";
4005
3957
  const headerCells = headerLine.split(delimiter);
4006
3958
  const dataLines = allLines.slice(1);
4007
- const ROWS_PER_PAGE = 100;
4008
- const totalPages = Math.max(1, Math.ceil(dataLines.length / ROWS_PER_PAGE));
4009
- let currentPage = 1;
4010
3959
  const table = document.createElement("table");
4011
3960
  table.style.borderCollapse = "collapse";
4012
- table.style.width = "100%";
3961
+ table.style.minWidth = "100%";
4013
3962
  table.style.fontFamily = '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, monospace';
4014
3963
  table.style.fontSize = "13px";
4015
- const renderPage = (pageNum) => {
4016
- currentPage = Math.max(1, Math.min(totalPages, pageNum));
4017
- table.innerHTML = "";
4018
- const headerTr = document.createElement("tr");
4019
- headerCells.forEach((cell) => {
4020
- const th = document.createElement("th");
4021
- th.textContent = cell.trim();
4022
- th.style.border = "1px solid #d0d7de";
4023
- th.style.padding = "8px 12px";
4024
- th.style.backgroundColor = "#f6f8fa";
4025
- th.style.fontWeight = "600";
4026
- th.style.whiteSpace = "nowrap";
4027
- headerTr.appendChild(th);
4028
- });
4029
- table.appendChild(headerTr);
4030
- const start = (currentPage - 1) * ROWS_PER_PAGE;
4031
- const end = Math.min(dataLines.length, start + ROWS_PER_PAGE);
4032
- const pageRows = dataLines.slice(start, end);
4033
- pageRows.forEach((row, idx) => {
4034
- const tr = document.createElement("tr");
4035
- if (idx % 2 === 1) tr.style.backgroundColor = "#f8fafc";
4036
- const cells = row.split(delimiter);
4037
- cells.forEach((cell) => {
4038
- const td = document.createElement("td");
4039
- td.textContent = cell.trim();
4040
- td.style.border = "1px solid #d0d7de";
4041
- td.style.padding = "6px 12px";
4042
- td.style.whiteSpace = "nowrap";
4043
- tr.appendChild(td);
4044
- });
4045
- table.appendChild(tr);
3964
+ table.style.background = "#ffffff";
3965
+ table.style.border = "1px solid #d0d7de";
3966
+ const headerTr = document.createElement("tr");
3967
+ headerCells.forEach((cell) => {
3968
+ const th = document.createElement("th");
3969
+ th.textContent = cell.trim();
3970
+ th.style.border = "1px solid #d0d7de";
3971
+ th.style.padding = "8px 12px";
3972
+ th.style.backgroundColor = "#f6f8fa";
3973
+ th.style.fontWeight = "600";
3974
+ th.style.whiteSpace = "nowrap";
3975
+ headerTr.appendChild(th);
3976
+ });
3977
+ table.appendChild(headerTr);
3978
+ dataLines.forEach((row, idx) => {
3979
+ const tr = document.createElement("tr");
3980
+ if (idx % 2 === 1) tr.style.backgroundColor = "#f8fafc";
3981
+ const cells = row.split(delimiter);
3982
+ cells.forEach((cell) => {
3983
+ const td = document.createElement("td");
3984
+ td.textContent = cell.trim();
3985
+ td.style.border = "1px solid #d0d7de";
3986
+ td.style.padding = "6px 12px";
3987
+ td.style.whiteSpace = "nowrap";
3988
+ tr.appendChild(td);
4046
3989
  });
4047
- container.scrollTop = 0;
4048
- ctx.emit("page-change", { page: currentPage, total: totalPages });
4049
- };
4050
- renderPage(1);
4051
- container.appendChild(table);
3990
+ table.appendChild(tr);
3991
+ });
3992
+ tableWrapper.appendChild(table);
3993
+ container.appendChild(tableWrapper);
4052
3994
  ctx.container.appendChild(container);
4053
3995
  let scale = 1;
3996
+ const calculateFitScale = () => {
3997
+ const availW = Math.max(280, container.clientWidth - 48);
3998
+ const tW = table.offsetWidth || 800;
3999
+ return Math.max(0.4, Math.min(1, availW / tW));
4000
+ };
4054
4001
  const applyScale = () => {
4055
- container.style.transform = `scale(${scale})`;
4056
- container.style.transformOrigin = "top left";
4002
+ tableWrapper.style.transform = `scale(${scale})`;
4057
4003
  };
4004
+ requestAnimationFrame(() => {
4005
+ scale = calculateFitScale();
4006
+ applyScale();
4007
+ });
4008
+ const ro = new ResizeObserver(() => {
4009
+ scale = calculateFitScale();
4010
+ applyScale();
4011
+ });
4012
+ ro.observe(container);
4058
4013
  const cleanup = () => {
4014
+ ro.disconnect();
4059
4015
  container.remove();
4060
4016
  ctx.container.innerHTML = "";
4061
4017
  };
4062
4018
  ctx.signal.addEventListener("abort", cleanup);
4063
4019
  return {
4064
4020
  destroy: cleanup,
4065
- getPageCount: () => totalPages,
4066
- getCurrentPage: () => currentPage,
4067
- goToPage: (page) => renderPage(page),
4021
+ getPageCount: () => 1,
4022
+ getCurrentPage: () => 1,
4023
+ goToPage: () => {
4024
+ },
4068
4025
  getThumbnails: () => {
4069
- const thumbnails = [];
4070
- for (let i = 0; i < totalPages; i++) {
4071
- const pageIdx = i;
4072
- const startRow = pageIdx * ROWS_PER_PAGE + 1;
4073
- const endRow = Math.min(dataLines.length, (pageIdx + 1) * ROWS_PER_PAGE);
4074
- const label = totalPages === 1 ? "Table" : `Page ${pageIdx + 1} (${startRow}-${endRow})`;
4075
- thumbnails.push({
4076
- index: pageIdx,
4077
- label,
4078
- render: async (canvas) => {
4079
- canvas.width = 140;
4080
- canvas.height = 100;
4081
- const c = canvas.getContext("2d");
4082
- if (!c) return;
4083
- c.fillStyle = "#ffffff";
4084
- c.fillRect(0, 0, canvas.width, canvas.height);
4085
- c.fillStyle = "#2563eb";
4086
- c.fillRect(0, 0, canvas.width, 16);
4087
- c.fillStyle = "#ffffff";
4088
- c.font = "bold 8px sans-serif";
4089
- c.fillText(`\u{1F4C8} ${label.slice(0, 18)}`, 6, 11);
4090
- const startY = 17;
4091
- const rowH = 13;
4092
- const colW = 28;
4093
- const cols = Math.min(4, headerCells.length || 4);
4094
- c.fillStyle = "#f1f5f9";
4095
- c.fillRect(0, startY, canvas.width, rowH);
4096
- c.strokeStyle = "#cbd5e1";
4097
- c.lineWidth = 0.8;
4098
- c.strokeRect(0, startY, canvas.width, rowH);
4099
- c.fillStyle = "#64748b";
4100
- c.font = "bold 7px sans-serif";
4101
- for (let ci = 0; ci < cols; ci++) {
4102
- const hName = headerCells[ci] || `Col ${ci + 1}`;
4103
- c.fillText(hName.slice(0, 5), 8 + ci * colW, startY + 9);
4104
- }
4105
- const rowsToPreview = dataLines.slice(pageIdx * ROWS_PER_PAGE, pageIdx * ROWS_PER_PAGE + 5);
4106
- for (let ri = 0; ri < rowsToPreview.length; ri++) {
4107
- const y = startY + (ri + 1) * rowH;
4108
- if (y > canvas.height - 2) break;
4109
- c.strokeStyle = "#e2e8f0";
4110
- c.strokeRect(0, y, canvas.width, rowH);
4111
- const cVals = rowsToPreview[ri].split(delimiter);
4112
- c.fillStyle = "#334155";
4113
- c.font = "6px sans-serif";
4114
- for (let ci = 0; ci < cols; ci++) {
4115
- const val = cVals[ci] || "";
4116
- c.fillText(val.trim().slice(0, 6), 8 + ci * colW, y + 9);
4026
+ const sheetName = ctx.metadata.name?.replace(/\.[^/.]+$/, "") || "Sheet1";
4027
+ return [{
4028
+ index: 0,
4029
+ label: sheetName,
4030
+ render: async (canvas) => {
4031
+ canvas.width = 140;
4032
+ canvas.height = 100;
4033
+ const c = canvas.getContext("2d");
4034
+ if (!c) return;
4035
+ c.fillStyle = "#ffffff";
4036
+ c.fillRect(0, 0, canvas.width, canvas.height);
4037
+ c.fillStyle = "#107c41";
4038
+ c.fillRect(0, 0, canvas.width, 16);
4039
+ c.fillStyle = "#ffffff";
4040
+ c.font = 'bold 8px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
4041
+ c.fillText(`\u{1F4CA} ${sheetName.slice(0, 16)}`, 6, 11);
4042
+ const startY = 17;
4043
+ const rowH = 13;
4044
+ const colW = 30;
4045
+ const cols = headerCells.slice(0, 4).map((h) => h.trim() || "Col");
4046
+ c.fillStyle = "#f1f5f9";
4047
+ c.fillRect(0, startY, canvas.width, rowH);
4048
+ c.strokeStyle = "#cbd5e1";
4049
+ c.lineWidth = 0.8;
4050
+ c.strokeRect(0, startY, canvas.width, rowH);
4051
+ c.fillStyle = "#64748b";
4052
+ c.font = "bold 7px sans-serif";
4053
+ for (let ci = 0; ci < cols.length; ci++) {
4054
+ c.fillText(cols[ci].slice(0, 5), 14 + ci * colW, startY + 9);
4055
+ }
4056
+ for (let ri = 0; ri < Math.min(5, dataLines.length); ri++) {
4057
+ const y = startY + (ri + 1) * rowH;
4058
+ if (y > canvas.height - 2) break;
4059
+ c.fillStyle = "#f8fafc";
4060
+ c.fillRect(0, y, 12, rowH);
4061
+ c.fillStyle = "#94a3b8";
4062
+ c.font = "6px sans-serif";
4063
+ c.fillText(String(ri + 1), 3, y + 9);
4064
+ c.strokeStyle = "#e2e8f0";
4065
+ c.strokeRect(0, y, canvas.width, rowH);
4066
+ const rowCells = dataLines[ri].split(delimiter);
4067
+ c.fillStyle = "#334155";
4068
+ c.font = "6px sans-serif";
4069
+ for (let ci = 0; ci < cols.length; ci++) {
4070
+ const val = (rowCells[ci] || "").trim();
4071
+ if (val) {
4072
+ c.fillText(val.slice(0, 6), 14 + ci * colW, y + 9);
4117
4073
  }
4118
4074
  }
4119
- c.strokeStyle = "#cbd5e1";
4120
- c.lineWidth = 1;
4121
- c.strokeRect(0.5, 0.5, canvas.width - 1, canvas.height - 1);
4122
4075
  }
4123
- });
4124
- }
4125
- return thumbnails;
4076
+ c.strokeStyle = "#cbd5e1";
4077
+ c.lineWidth = 1;
4078
+ c.strokeRect(0.5, 0.5, canvas.width - 1, canvas.height - 1);
4079
+ }
4080
+ }];
4126
4081
  },
4127
4082
  zoomIn: () => {
4128
4083
  scale += 0.1;
@@ -4138,11 +4093,13 @@ var CsvPlugin = class {
4138
4093
  applyScale();
4139
4094
  },
4140
4095
  fitToPage: () => {
4141
- const availW = Math.max(280, (ctx.container.clientWidth || window.innerWidth) - 48);
4142
- const tW = table.offsetWidth || 600;
4143
- scale = Math.max(0.35, Math.min(1, availW / tW));
4096
+ scale = calculateFitScale();
4144
4097
  applyScale();
4145
4098
  },
4099
+ rotateCW: () => {
4100
+ },
4101
+ rotateCCW: () => {
4102
+ },
4146
4103
  download: () => {
4147
4104
  const blob = new Blob([ctx.buffer], { type: "text/csv" });
4148
4105
  const url = URL.createObjectURL(blob);
@@ -4254,26 +4211,6 @@ var CodePlugin = class {
4254
4211
  instance.zoomIn?.();
4255
4212
  }
4256
4213
  },
4257
- {
4258
- id: "fit-page",
4259
- icon: "fit-page",
4260
- label: "Fit to View",
4261
- type: "button",
4262
- group: "zoom",
4263
- execute: () => {
4264
- instance.fitToPage?.();
4265
- }
4266
- },
4267
- {
4268
- id: "rotate-cw",
4269
- icon: "rotate-cw",
4270
- label: "Rotate",
4271
- type: "button",
4272
- group: "view",
4273
- execute: () => {
4274
- instance.rotateCW?.();
4275
- }
4276
- },
4277
4214
  {
4278
4215
  id: "copy",
4279
4216
  icon: "copy",
@@ -5417,6 +5354,148 @@ var ThreeDPlugin = class {
5417
5354
  function threeDPlugin() {
5418
5355
  return new ThreeDPlugin();
5419
5356
  }
5357
+ function cleanRtfText(raw) {
5358
+ return raw.replace(/\\bin\d+\s/g, "").replace(/{\\\*[^}]+}/g, "").replace(/\\u(-?\d+)\??/g, (_, code) => {
5359
+ let num = parseInt(code, 10);
5360
+ if (num < 0) num += 65536;
5361
+ return String.fromCharCode(num);
5362
+ }).replace(/\\'([0-9a-fA-F]{2})/g, (_, hex) => {
5363
+ return String.fromCharCode(parseInt(hex, 16));
5364
+ }).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();
5365
+ }
5366
+ function parseRtfTables(rtfText) {
5367
+ const rowSegments = rtfText.split(/\\row\b/);
5368
+ if (rowSegments.length <= 1) return [];
5369
+ const rawTables = [];
5370
+ let currentTable = null;
5371
+ let charOffset = 0;
5372
+ for (let i = 0; i < rowSegments.length - 1; i++) {
5373
+ const segment = rowSegments[i];
5374
+ const segmentStart = charOffset;
5375
+ const segmentEnd = charOffset + segment.length + 4;
5376
+ const cellParts = segment.split(/\\cell\b/);
5377
+ if (cellParts.length > 1) {
5378
+ const cellxMatches = [...segment.matchAll(/\\cellx(\d+)/g)].map((m) => parseInt(m[1], 10));
5379
+ let cellWidths = [];
5380
+ if (cellxMatches.length > 0) {
5381
+ let seq = [cellxMatches[0]];
5382
+ for (let k = 1; k < cellxMatches.length; k++) {
5383
+ if (cellxMatches[k] > cellxMatches[k - 1]) seq.push(cellxMatches[k]);
5384
+ else break;
5385
+ }
5386
+ let prev = 0;
5387
+ cellWidths = seq.map((x) => {
5388
+ const w = x - prev;
5389
+ prev = x;
5390
+ return w;
5391
+ });
5392
+ }
5393
+ const cells = [];
5394
+ for (let c = 0; c < cellParts.length - 1; c++) {
5395
+ let rawCell = cellParts[c];
5396
+ if (c === 0) {
5397
+ const lastTrowd = rawCell.lastIndexOf("\\trowd");
5398
+ if (lastTrowd !== -1) rawCell = rawCell.slice(lastTrowd);
5399
+ }
5400
+ cells.push(cleanRtfText(rawCell));
5401
+ }
5402
+ const isNewTable = !currentTable || segmentStart - currentTable.lastEnd > 2500;
5403
+ if (isNewTable) {
5404
+ const prevTextSlice = rtfText.slice(Math.max(0, segmentStart - 4e3), segmentStart);
5405
+ const cleanPrev = cleanRtfText(prevTextSlice);
5406
+ const anchorBefore = cleanPrev.slice(-80).trim();
5407
+ currentTable = {
5408
+ id: `RTF_TABLE_${rawTables.length}`,
5409
+ startPos: segmentStart,
5410
+ lastEnd: segmentEnd,
5411
+ rows: [],
5412
+ columnWidths: cellWidths,
5413
+ anchorBefore,
5414
+ anchorAfter: ""
5415
+ };
5416
+ rawTables.push(currentTable);
5417
+ }
5418
+ if (currentTable) {
5419
+ currentTable.rows.push({
5420
+ cells,
5421
+ widths: cellWidths.length === cells.length ? cellWidths : currentTable.columnWidths
5422
+ });
5423
+ currentTable.lastEnd = segmentEnd;
5424
+ }
5425
+ }
5426
+ charOffset += segment.length + 4;
5427
+ }
5428
+ rawTables.forEach((tbl) => {
5429
+ const afterSlice = rtfText.slice(tbl.lastEnd, Math.min(rtfText.length, tbl.lastEnd + 2e3));
5430
+ const cleanAfter = cleanRtfText(afterSlice);
5431
+ tbl.anchorAfter = cleanAfter.slice(0, 80).trim();
5432
+ });
5433
+ return rawTables.map((tbl) => {
5434
+ const totalW = (tbl.columnWidths || []).reduce((a, b) => a + b, 0);
5435
+ const colPercents = totalW > 0 ? tbl.columnWidths.map((w) => Math.round(w / totalW * 100)) : [];
5436
+ return {
5437
+ id: tbl.id,
5438
+ rows: tbl.rows.map((r) => r.cells),
5439
+ colPercents,
5440
+ anchorBefore: tbl.anchorBefore,
5441
+ anchorAfter: tbl.anchorAfter
5442
+ };
5443
+ });
5444
+ }
5445
+ function renderRtfTableElement(table) {
5446
+ const tableEl = document.createElement("table");
5447
+ tableEl.className = "fp-rtf-table";
5448
+ tableEl.style.width = "100%";
5449
+ tableEl.style.borderCollapse = "collapse";
5450
+ tableEl.style.margin = "16px 0 20px";
5451
+ tableEl.style.fontSize = "10pt";
5452
+ tableEl.style.lineHeight = "1.5";
5453
+ tableEl.style.border = "1px solid #cbd5e1";
5454
+ tableEl.style.borderRadius = "4px";
5455
+ tableEl.style.boxSizing = "border-box";
5456
+ tableEl.style.backgroundColor = "#ffffff";
5457
+ const thead = document.createElement("thead");
5458
+ const tbody = document.createElement("tbody");
5459
+ table.rows.forEach((rowCells, rIdx) => {
5460
+ const tr = document.createElement("tr");
5461
+ const isHeader = rIdx === 0;
5462
+ if (isHeader) {
5463
+ tr.style.backgroundColor = "#f8fafc";
5464
+ tr.style.borderBottom = "2px solid #cbd5e1";
5465
+ } else {
5466
+ tr.style.borderBottom = "1px solid #e2e8f0";
5467
+ if (rIdx % 2 === 0) {
5468
+ tr.style.backgroundColor = "#fcfdfe";
5469
+ }
5470
+ }
5471
+ rowCells.forEach((cellText, cIdx) => {
5472
+ const cellEl = document.createElement(isHeader ? "th" : "td");
5473
+ cellEl.style.padding = "8px 12px";
5474
+ cellEl.style.border = "1px solid #cbd5e1";
5475
+ cellEl.style.verticalAlign = "top";
5476
+ cellEl.style.color = isHeader ? "#0f172a" : "#1e293b";
5477
+ cellEl.style.fontWeight = isHeader ? "600" : "normal";
5478
+ if (table.colPercents && table.colPercents[cIdx]) {
5479
+ cellEl.style.width = `${table.colPercents[cIdx]}%`;
5480
+ }
5481
+ if (/^\d+$/.test(cellText.trim()) || cellText.trim() === "#") {
5482
+ cellEl.style.textAlign = "center";
5483
+ } else {
5484
+ cellEl.style.textAlign = "left";
5485
+ }
5486
+ cellEl.textContent = cellText;
5487
+ tr.appendChild(cellEl);
5488
+ });
5489
+ if (isHeader) {
5490
+ thead.appendChild(tr);
5491
+ } else {
5492
+ tbody.appendChild(tr);
5493
+ }
5494
+ });
5495
+ tableEl.appendChild(thead);
5496
+ tableEl.appendChild(tbody);
5497
+ return tableEl;
5498
+ }
5420
5499
  var RtfPlugin = class {
5421
5500
  id = "rtf";
5422
5501
  name = "Rich Text Format (RTF)";
@@ -5452,6 +5531,14 @@ var RtfPlugin = class {
5452
5531
  }
5453
5532
  });
5454
5533
  actions.push(
5534
+ {
5535
+ id: "thumbnails",
5536
+ icon: "thumbnails",
5537
+ label: "Thumbnails",
5538
+ type: "button",
5539
+ group: "view",
5540
+ execute: () => instance.toggleThumbnails?.()
5541
+ },
5455
5542
  {
5456
5543
  id: "zoom-out",
5457
5544
  icon: "zoom-out",
@@ -5579,6 +5666,8 @@ var RtfPlugin = class {
5579
5666
  if (typeof RTFJS__namespace.loggingEnabled === "function") {
5580
5667
  RTFJS__namespace.loggingEnabled(false);
5581
5668
  }
5669
+ const rawText = new TextDecoder("latin1").decode(ctx.buffer);
5670
+ const tables = parseRtfTables(rawText);
5582
5671
  const doc = new RTFJS__namespace.Document(ctx.buffer, {});
5583
5672
  const htmlElements = await doc.render();
5584
5673
  const contentNodes = [];
@@ -5593,6 +5682,20 @@ var RtfPlugin = class {
5593
5682
  if (hasChildBlocks && !["table", "tr", "td", "th", "ul", "ol", "li"].includes(tag)) {
5594
5683
  extractBlocks(Array.from(item.children));
5595
5684
  } else {
5685
+ const svgs = item.querySelectorAll("svg, img");
5686
+ svgs.forEach((s) => {
5687
+ const el = s;
5688
+ el.style.maxWidth = "100%";
5689
+ el.style.maxHeight = "360px";
5690
+ el.style.display = "block";
5691
+ el.style.margin = "12px auto";
5692
+ if (s.tagName.toLowerCase() === "svg") {
5693
+ s.removeAttribute("width");
5694
+ s.removeAttribute("height");
5695
+ el.style.width = "100%";
5696
+ el.style.maxHeight = "360px";
5697
+ }
5698
+ });
5596
5699
  if (tag === "p" || tag === "div") {
5597
5700
  item.style.display = "block";
5598
5701
  item.style.marginBottom = "12px";
@@ -5603,14 +5706,46 @@ var RtfPlugin = class {
5603
5706
  }
5604
5707
  };
5605
5708
  extractBlocks(htmlElements);
5709
+ tables.forEach((table) => {
5710
+ const tableEl = renderRtfTableElement(table);
5711
+ let inserted = false;
5712
+ if (table.anchorBefore && table.anchorBefore.length > 10) {
5713
+ const keyword = table.anchorBefore.slice(-35).trim();
5714
+ for (let i = 0; i < contentNodes.length; i++) {
5715
+ if (contentNodes[i].textContent?.includes(keyword)) {
5716
+ contentNodes.splice(i + 1, 0, tableEl);
5717
+ inserted = true;
5718
+ break;
5719
+ }
5720
+ }
5721
+ }
5722
+ if (!inserted && table.anchorAfter && table.anchorAfter.length > 10) {
5723
+ const keyword = table.anchorAfter.slice(0, 35).trim();
5724
+ for (let i = 0; i < contentNodes.length; i++) {
5725
+ if (contentNodes[i].textContent?.includes(keyword)) {
5726
+ contentNodes.splice(i, 0, tableEl);
5727
+ inserted = true;
5728
+ break;
5729
+ }
5730
+ }
5731
+ }
5732
+ if (!inserted) {
5733
+ if (contentNodes.length > 2) {
5734
+ contentNodes.splice(2, 0, tableEl);
5735
+ } else {
5736
+ contentNodes.push(tableEl);
5737
+ }
5738
+ }
5739
+ });
5606
5740
  wrapper.innerHTML = "";
5607
5741
  contentNodes.forEach((node) => wrapper.appendChild(node));
5608
5742
  const childHeights = contentNodes.map((c) => {
5609
5743
  const rectH = c.getBoundingClientRect ? c.getBoundingClientRect().height : 0;
5610
5744
  const offH = c.offsetHeight || 0;
5745
+ const realH = Math.max(rectH, offH);
5746
+ if (realH > 0) return realH;
5611
5747
  const textLen = c.textContent?.trim().length || 0;
5612
- const estH = Math.max(28, Math.ceil(textLen / 75) * 24 + 16);
5613
- return Math.max(rectH, offH, estH);
5748
+ return Math.max(24, Math.ceil(textLen / 95) * 22 + 12);
5614
5749
  });
5615
5750
  wrapper.innerHTML = "";
5616
5751
  const createRtfCard = () => {
@@ -5619,7 +5754,7 @@ var RtfPlugin = class {
5619
5754
  card.style.backgroundColor = "#ffffff";
5620
5755
  card.style.boxShadow = "0 4px 24px rgba(0,0,0,0.08)";
5621
5756
  card.style.borderRadius = "4px";
5622
- card.style.padding = "72px 56px";
5757
+ card.style.padding = "64px 56px";
5623
5758
  card.style.width = "816px";
5624
5759
  card.style.minHeight = "1056px";
5625
5760
  card.style.boxSizing = "border-box";
@@ -5633,22 +5768,35 @@ var RtfPlugin = class {
5633
5768
  wrapper.appendChild(curCard);
5634
5769
  pageElements = [curCard];
5635
5770
  let curH = 0;
5636
- const maxH = 912;
5771
+ const maxH = 928;
5637
5772
  for (let i = 0; i < contentNodes.length; i++) {
5638
5773
  const child = contentNodes[i];
5639
5774
  const chH = childHeights[i];
5640
- curCard.appendChild(child);
5641
- curH += chH;
5642
- if (curH >= maxH && i < contentNodes.length - 1) {
5775
+ const isChildEmpty = (child.textContent || "").trim().length === 0 && !child.querySelector("table, img, svg, canvas");
5776
+ if (curH === 0 && isChildEmpty) {
5777
+ continue;
5778
+ }
5779
+ if (curH + chH > maxH && curCard.childNodes.length > 0) {
5643
5780
  curCard = createRtfCard();
5644
5781
  wrapper.appendChild(curCard);
5645
5782
  pageElements.push(curCard);
5646
5783
  curH = 0;
5647
5784
  }
5785
+ curCard.appendChild(child);
5786
+ curH += chH;
5787
+ }
5788
+ pageElements = pageElements.filter((card) => {
5789
+ const hasText = (card.textContent || "").trim().length > 0;
5790
+ const hasMedia = card.querySelector("table, img, svg, canvas") !== null;
5791
+ return hasText || hasMedia;
5792
+ });
5793
+ if (pageElements.length === 0) {
5794
+ pageElements = [curCard];
5648
5795
  }
5649
5796
  } catch (err) {
5650
5797
  console.warn("[RtfPlugin] RTF render error, fallback text:", err);
5651
5798
  const text = new TextDecoder("latin1").decode(ctx.buffer);
5799
+ const tables = parseRtfTables(text);
5652
5800
  const rawPages = text.split(/\\page\b/).map((segment) => {
5653
5801
  return segment.replace(/\\par[d]?/g, "\n").replace(/\\[a-zA-Z0-9\-]+/g, "").replace(/[{}]/g, "").trim();
5654
5802
  }).filter((p) => p.length > 0);
@@ -5659,7 +5807,7 @@ var RtfPlugin = class {
5659
5807
  pageCard.style.backgroundColor = "#ffffff";
5660
5808
  pageCard.style.boxShadow = "0 2px 12px rgba(0,0,0,0.08)";
5661
5809
  pageCard.style.borderRadius = "4px";
5662
- pageCard.style.padding = "72px 56px";
5810
+ pageCard.style.padding = "64px 56px";
5663
5811
  pageCard.style.width = "816px";
5664
5812
  pageCard.style.minHeight = "1056px";
5665
5813
  pageCard.style.boxSizing = "border-box";
@@ -5670,6 +5818,9 @@ var RtfPlugin = class {
5670
5818
  pageCard.style.whiteSpace = "pre-wrap";
5671
5819
  pageCard.style.display = i === 0 ? "block" : "none";
5672
5820
  pageCard.textContent = pages[i];
5821
+ if (i === 1 && tables.length > 0) {
5822
+ tables.forEach((tbl) => pageCard.appendChild(renderRtfTableElement(tbl)));
5823
+ }
5673
5824
  wrapper.appendChild(pageCard);
5674
5825
  pageElements.push(pageCard);
5675
5826
  }
@@ -5700,6 +5851,32 @@ var RtfPlugin = class {
5700
5851
  getPageCount: () => totalPages,
5701
5852
  getCurrentPage: () => currentPage,
5702
5853
  goToPage: (page) => showPage(page),
5854
+ getThumbnails: async () => {
5855
+ const items = [];
5856
+ for (let idx = 0; idx < totalPages; idx++) {
5857
+ const pageIdx = idx;
5858
+ items.push({
5859
+ index: pageIdx,
5860
+ label: `Page ${pageIdx + 1}`,
5861
+ render: async (canvas) => {
5862
+ const ctx2d = canvas.getContext("2d");
5863
+ if (!ctx2d) return;
5864
+ canvas.width = 140;
5865
+ canvas.height = 180;
5866
+ ctx2d.fillStyle = "#ffffff";
5867
+ ctx2d.fillRect(0, 0, 140, 180);
5868
+ ctx2d.strokeStyle = "#cbd5e1";
5869
+ ctx2d.lineWidth = 1;
5870
+ ctx2d.strokeRect(0.5, 0.5, 139, 179);
5871
+ ctx2d.fillStyle = "#334155";
5872
+ ctx2d.font = 'bold 11px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
5873
+ ctx2d.textAlign = "center";
5874
+ ctx2d.fillText(`Page ${pageIdx + 1}`, 70, 95);
5875
+ }
5876
+ });
5877
+ }
5878
+ return items;
5879
+ },
5703
5880
  zoomIn: () => {
5704
5881
  isUserZoomed = true;
5705
5882
  scale += 0.15;