@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.js CHANGED
@@ -3559,26 +3559,6 @@ var ExcelPlugin = class {
3559
3559
  instance.zoomIn?.();
3560
3560
  }
3561
3561
  },
3562
- {
3563
- id: "fit-page",
3564
- icon: "fit-page",
3565
- label: "Fit to View",
3566
- type: "button",
3567
- group: "zoom",
3568
- execute: () => {
3569
- instance.fitToPage?.();
3570
- }
3571
- },
3572
- {
3573
- id: "rotate-cw",
3574
- icon: "rotate-cw",
3575
- label: "Rotate",
3576
- type: "button",
3577
- group: "view",
3578
- execute: () => {
3579
- instance.rotateCW?.();
3580
- }
3581
- },
3582
3562
  {
3583
3563
  id: "download",
3584
3564
  icon: "download",
@@ -3639,12 +3619,11 @@ var ExcelPlugin = class {
3639
3619
  container.appendChild(tabsArea);
3640
3620
  ctx.container.appendChild(container);
3641
3621
  let scale = 1;
3642
- let rotation = 0;
3643
3622
  let currentSheetIndex = 1;
3644
3623
  let sheetNames = [];
3645
3624
  let wb = null;
3646
3625
  const applyTransform = () => {
3647
- contentArea.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
3626
+ contentArea.style.transform = `scale(${scale})`;
3648
3627
  contentArea.style.transformOrigin = "top left";
3649
3628
  };
3650
3629
  const calculateFitScale = () => {
@@ -3652,7 +3631,7 @@ var ExcelPlugin = class {
3652
3631
  if (!table) return 1;
3653
3632
  const availW = Math.max(280, container.clientWidth - 48);
3654
3633
  const tW = table.offsetWidth || 800;
3655
- return Math.max(0.65, Math.min(1, availW / tW));
3634
+ return Math.max(0.4, Math.min(1, availW / tW));
3656
3635
  };
3657
3636
  try {
3658
3637
  wb = XLSX.read(new Uint8Array(ctx.buffer), { type: "array", cellDates: true });
@@ -3707,7 +3686,16 @@ var ExcelPlugin = class {
3707
3686
  }
3708
3687
  });
3709
3688
  ctx.emit("page-change", { page: currentSheetIndex, total: sheetNames.length });
3689
+ requestAnimationFrame(() => {
3690
+ scale = calculateFitScale();
3691
+ applyTransform();
3692
+ });
3710
3693
  };
3694
+ const ro = new ResizeObserver(() => {
3695
+ scale = calculateFitScale();
3696
+ applyTransform();
3697
+ });
3698
+ ro.observe(container);
3711
3699
  if (sheetNames.length > 0) {
3712
3700
  sheetNames.forEach((name, idx) => {
3713
3701
  const btn = document.createElement("button");
@@ -3734,6 +3722,7 @@ var ExcelPlugin = class {
3734
3722
  `;
3735
3723
  }
3736
3724
  const cleanup = () => {
3725
+ ro.disconnect();
3737
3726
  container.remove();
3738
3727
  ctx.container.innerHTML = "";
3739
3728
  };
@@ -3755,16 +3744,11 @@ var ExcelPlugin = class {
3755
3744
  },
3756
3745
  fitToPage: () => {
3757
3746
  scale = calculateFitScale();
3758
- rotation = 0;
3759
3747
  applyTransform();
3760
3748
  },
3761
3749
  rotateCW: () => {
3762
- rotation = (rotation + 90) % 360;
3763
- applyTransform();
3764
3750
  },
3765
3751
  rotateCCW: () => {
3766
- rotation = (rotation - 90 + 360) % 360;
3767
- applyTransform();
3768
3752
  },
3769
3753
  goToPage: (page) => {
3770
3754
  if (page > 0 && page <= sheetNames.length) {
@@ -3862,38 +3846,15 @@ var CsvPlugin = class {
3862
3846
  return ext === ".csv" || ext === ".tsv" || mime === "text/csv" || mime === "text/tab-separated-values";
3863
3847
  }
3864
3848
  getToolbarActions(instance) {
3865
- const totalPages = instance.getPageCount?.() ?? 1;
3866
3849
  const actions = [];
3867
3850
  actions.push({
3868
3851
  id: "thumbnails",
3869
3852
  icon: "thumbnails",
3870
- label: "Page Thumbnails",
3853
+ label: "Sheet Thumbnails",
3871
3854
  type: "button",
3872
3855
  group: "navigation",
3873
3856
  execute: () => instance.toggleThumbnails?.()
3874
3857
  });
3875
- if (totalPages > 1) {
3876
- actions.push({
3877
- id: "page-nav",
3878
- icon: "",
3879
- label: "Page Navigation",
3880
- type: "page-nav",
3881
- group: "navigation",
3882
- value: instance.getCurrentPage?.() ?? 1,
3883
- max: totalPages,
3884
- execute: (action, page) => {
3885
- const cur = instance.getCurrentPage?.() ?? 1;
3886
- const max = instance.getPageCount?.() ?? 1;
3887
- if (action === "prev") {
3888
- if (cur > 1) instance.goToPage?.(cur - 1);
3889
- } else if (action === "next") {
3890
- if (cur < max) instance.goToPage?.(cur + 1);
3891
- } else if (typeof page === "number") {
3892
- instance.goToPage?.(page);
3893
- }
3894
- }
3895
- });
3896
- }
3897
3858
  actions.push(
3898
3859
  {
3899
3860
  id: "zoom-out",
@@ -3915,16 +3876,6 @@ var CsvPlugin = class {
3915
3876
  instance.zoomIn?.();
3916
3877
  }
3917
3878
  },
3918
- {
3919
- id: "fit-page",
3920
- icon: "fit-page",
3921
- label: "Fit to View",
3922
- type: "button",
3923
- group: "zoom",
3924
- execute: () => {
3925
- instance.fitToPage?.();
3926
- }
3927
- },
3928
3879
  {
3929
3880
  id: "download",
3930
3881
  icon: "download",
@@ -3967,131 +3918,135 @@ var CsvPlugin = class {
3967
3918
  container.style.overflow = "auto";
3968
3919
  container.style.padding = "16px";
3969
3920
  container.style.boxSizing = "border-box";
3970
- container.style.transformOrigin = "top left";
3921
+ const tableWrapper = document.createElement("div");
3922
+ tableWrapper.style.transformOrigin = "top left";
3971
3923
  const delimiter = ctx.metadata.extension === ".tsv" ? " " : ",";
3972
3924
  const allLines = text.split(/\r?\n/).filter((r) => r.trim().length > 0);
3973
3925
  const headerLine = allLines[0] || "";
3974
3926
  const headerCells = headerLine.split(delimiter);
3975
3927
  const dataLines = allLines.slice(1);
3976
- const ROWS_PER_PAGE = 100;
3977
- const totalPages = Math.max(1, Math.ceil(dataLines.length / ROWS_PER_PAGE));
3978
- let currentPage = 1;
3979
3928
  const table = document.createElement("table");
3980
3929
  table.style.borderCollapse = "collapse";
3981
- table.style.width = "100%";
3930
+ table.style.minWidth = "100%";
3982
3931
  table.style.fontFamily = '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, monospace';
3983
3932
  table.style.fontSize = "13px";
3984
- const renderPage = (pageNum) => {
3985
- currentPage = Math.max(1, Math.min(totalPages, pageNum));
3986
- table.innerHTML = "";
3987
- const headerTr = document.createElement("tr");
3988
- headerCells.forEach((cell) => {
3989
- const th = document.createElement("th");
3990
- th.textContent = cell.trim();
3991
- th.style.border = "1px solid #d0d7de";
3992
- th.style.padding = "8px 12px";
3993
- th.style.backgroundColor = "#f6f8fa";
3994
- th.style.fontWeight = "600";
3995
- th.style.whiteSpace = "nowrap";
3996
- headerTr.appendChild(th);
3997
- });
3998
- table.appendChild(headerTr);
3999
- const start = (currentPage - 1) * ROWS_PER_PAGE;
4000
- const end = Math.min(dataLines.length, start + ROWS_PER_PAGE);
4001
- const pageRows = dataLines.slice(start, end);
4002
- pageRows.forEach((row, idx) => {
4003
- const tr = document.createElement("tr");
4004
- if (idx % 2 === 1) tr.style.backgroundColor = "#f8fafc";
4005
- const cells = row.split(delimiter);
4006
- cells.forEach((cell) => {
4007
- const td = document.createElement("td");
4008
- td.textContent = cell.trim();
4009
- td.style.border = "1px solid #d0d7de";
4010
- td.style.padding = "6px 12px";
4011
- td.style.whiteSpace = "nowrap";
4012
- tr.appendChild(td);
4013
- });
4014
- table.appendChild(tr);
3933
+ table.style.background = "#ffffff";
3934
+ table.style.border = "1px solid #d0d7de";
3935
+ const headerTr = document.createElement("tr");
3936
+ headerCells.forEach((cell) => {
3937
+ const th = document.createElement("th");
3938
+ th.textContent = cell.trim();
3939
+ th.style.border = "1px solid #d0d7de";
3940
+ th.style.padding = "8px 12px";
3941
+ th.style.backgroundColor = "#f6f8fa";
3942
+ th.style.fontWeight = "600";
3943
+ th.style.whiteSpace = "nowrap";
3944
+ headerTr.appendChild(th);
3945
+ });
3946
+ table.appendChild(headerTr);
3947
+ dataLines.forEach((row, idx) => {
3948
+ const tr = document.createElement("tr");
3949
+ if (idx % 2 === 1) tr.style.backgroundColor = "#f8fafc";
3950
+ const cells = row.split(delimiter);
3951
+ cells.forEach((cell) => {
3952
+ const td = document.createElement("td");
3953
+ td.textContent = cell.trim();
3954
+ td.style.border = "1px solid #d0d7de";
3955
+ td.style.padding = "6px 12px";
3956
+ td.style.whiteSpace = "nowrap";
3957
+ tr.appendChild(td);
4015
3958
  });
4016
- container.scrollTop = 0;
4017
- ctx.emit("page-change", { page: currentPage, total: totalPages });
4018
- };
4019
- renderPage(1);
4020
- container.appendChild(table);
3959
+ table.appendChild(tr);
3960
+ });
3961
+ tableWrapper.appendChild(table);
3962
+ container.appendChild(tableWrapper);
4021
3963
  ctx.container.appendChild(container);
4022
3964
  let scale = 1;
3965
+ const calculateFitScale = () => {
3966
+ const availW = Math.max(280, container.clientWidth - 48);
3967
+ const tW = table.offsetWidth || 800;
3968
+ return Math.max(0.4, Math.min(1, availW / tW));
3969
+ };
4023
3970
  const applyScale = () => {
4024
- container.style.transform = `scale(${scale})`;
4025
- container.style.transformOrigin = "top left";
3971
+ tableWrapper.style.transform = `scale(${scale})`;
4026
3972
  };
3973
+ requestAnimationFrame(() => {
3974
+ scale = calculateFitScale();
3975
+ applyScale();
3976
+ });
3977
+ const ro = new ResizeObserver(() => {
3978
+ scale = calculateFitScale();
3979
+ applyScale();
3980
+ });
3981
+ ro.observe(container);
4027
3982
  const cleanup = () => {
3983
+ ro.disconnect();
4028
3984
  container.remove();
4029
3985
  ctx.container.innerHTML = "";
4030
3986
  };
4031
3987
  ctx.signal.addEventListener("abort", cleanup);
4032
3988
  return {
4033
3989
  destroy: cleanup,
4034
- getPageCount: () => totalPages,
4035
- getCurrentPage: () => currentPage,
4036
- goToPage: (page) => renderPage(page),
3990
+ getPageCount: () => 1,
3991
+ getCurrentPage: () => 1,
3992
+ goToPage: () => {
3993
+ },
4037
3994
  getThumbnails: () => {
4038
- const thumbnails = [];
4039
- for (let i = 0; i < totalPages; i++) {
4040
- const pageIdx = i;
4041
- const startRow = pageIdx * ROWS_PER_PAGE + 1;
4042
- const endRow = Math.min(dataLines.length, (pageIdx + 1) * ROWS_PER_PAGE);
4043
- const label = totalPages === 1 ? "Table" : `Page ${pageIdx + 1} (${startRow}-${endRow})`;
4044
- thumbnails.push({
4045
- index: pageIdx,
4046
- label,
4047
- render: async (canvas) => {
4048
- canvas.width = 140;
4049
- canvas.height = 100;
4050
- const c = canvas.getContext("2d");
4051
- if (!c) return;
4052
- c.fillStyle = "#ffffff";
4053
- c.fillRect(0, 0, canvas.width, canvas.height);
4054
- c.fillStyle = "#2563eb";
4055
- c.fillRect(0, 0, canvas.width, 16);
4056
- c.fillStyle = "#ffffff";
4057
- c.font = "bold 8px sans-serif";
4058
- c.fillText(`\u{1F4C8} ${label.slice(0, 18)}`, 6, 11);
4059
- const startY = 17;
4060
- const rowH = 13;
4061
- const colW = 28;
4062
- const cols = Math.min(4, headerCells.length || 4);
4063
- c.fillStyle = "#f1f5f9";
4064
- c.fillRect(0, startY, canvas.width, rowH);
4065
- c.strokeStyle = "#cbd5e1";
4066
- c.lineWidth = 0.8;
4067
- c.strokeRect(0, startY, canvas.width, rowH);
4068
- c.fillStyle = "#64748b";
4069
- c.font = "bold 7px sans-serif";
4070
- for (let ci = 0; ci < cols; ci++) {
4071
- const hName = headerCells[ci] || `Col ${ci + 1}`;
4072
- c.fillText(hName.slice(0, 5), 8 + ci * colW, startY + 9);
4073
- }
4074
- const rowsToPreview = dataLines.slice(pageIdx * ROWS_PER_PAGE, pageIdx * ROWS_PER_PAGE + 5);
4075
- for (let ri = 0; ri < rowsToPreview.length; ri++) {
4076
- const y = startY + (ri + 1) * rowH;
4077
- if (y > canvas.height - 2) break;
4078
- c.strokeStyle = "#e2e8f0";
4079
- c.strokeRect(0, y, canvas.width, rowH);
4080
- const cVals = rowsToPreview[ri].split(delimiter);
4081
- c.fillStyle = "#334155";
4082
- c.font = "6px sans-serif";
4083
- for (let ci = 0; ci < cols; ci++) {
4084
- const val = cVals[ci] || "";
4085
- c.fillText(val.trim().slice(0, 6), 8 + ci * colW, y + 9);
3995
+ const sheetName = ctx.metadata.name?.replace(/\.[^/.]+$/, "") || "Sheet1";
3996
+ return [{
3997
+ index: 0,
3998
+ label: sheetName,
3999
+ render: async (canvas) => {
4000
+ canvas.width = 140;
4001
+ canvas.height = 100;
4002
+ const c = canvas.getContext("2d");
4003
+ if (!c) return;
4004
+ c.fillStyle = "#ffffff";
4005
+ c.fillRect(0, 0, canvas.width, canvas.height);
4006
+ c.fillStyle = "#107c41";
4007
+ c.fillRect(0, 0, canvas.width, 16);
4008
+ c.fillStyle = "#ffffff";
4009
+ c.font = 'bold 8px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
4010
+ c.fillText(`\u{1F4CA} ${sheetName.slice(0, 16)}`, 6, 11);
4011
+ const startY = 17;
4012
+ const rowH = 13;
4013
+ const colW = 30;
4014
+ const cols = headerCells.slice(0, 4).map((h) => h.trim() || "Col");
4015
+ c.fillStyle = "#f1f5f9";
4016
+ c.fillRect(0, startY, canvas.width, rowH);
4017
+ c.strokeStyle = "#cbd5e1";
4018
+ c.lineWidth = 0.8;
4019
+ c.strokeRect(0, startY, canvas.width, rowH);
4020
+ c.fillStyle = "#64748b";
4021
+ c.font = "bold 7px sans-serif";
4022
+ for (let ci = 0; ci < cols.length; ci++) {
4023
+ c.fillText(cols[ci].slice(0, 5), 14 + ci * colW, startY + 9);
4024
+ }
4025
+ for (let ri = 0; ri < Math.min(5, dataLines.length); ri++) {
4026
+ const y = startY + (ri + 1) * rowH;
4027
+ if (y > canvas.height - 2) break;
4028
+ c.fillStyle = "#f8fafc";
4029
+ c.fillRect(0, y, 12, rowH);
4030
+ c.fillStyle = "#94a3b8";
4031
+ c.font = "6px sans-serif";
4032
+ c.fillText(String(ri + 1), 3, y + 9);
4033
+ c.strokeStyle = "#e2e8f0";
4034
+ c.strokeRect(0, y, canvas.width, rowH);
4035
+ const rowCells = dataLines[ri].split(delimiter);
4036
+ c.fillStyle = "#334155";
4037
+ c.font = "6px sans-serif";
4038
+ for (let ci = 0; ci < cols.length; ci++) {
4039
+ const val = (rowCells[ci] || "").trim();
4040
+ if (val) {
4041
+ c.fillText(val.slice(0, 6), 14 + ci * colW, y + 9);
4086
4042
  }
4087
4043
  }
4088
- c.strokeStyle = "#cbd5e1";
4089
- c.lineWidth = 1;
4090
- c.strokeRect(0.5, 0.5, canvas.width - 1, canvas.height - 1);
4091
4044
  }
4092
- });
4093
- }
4094
- return thumbnails;
4045
+ c.strokeStyle = "#cbd5e1";
4046
+ c.lineWidth = 1;
4047
+ c.strokeRect(0.5, 0.5, canvas.width - 1, canvas.height - 1);
4048
+ }
4049
+ }];
4095
4050
  },
4096
4051
  zoomIn: () => {
4097
4052
  scale += 0.1;
@@ -4107,11 +4062,13 @@ var CsvPlugin = class {
4107
4062
  applyScale();
4108
4063
  },
4109
4064
  fitToPage: () => {
4110
- const availW = Math.max(280, (ctx.container.clientWidth || window.innerWidth) - 48);
4111
- const tW = table.offsetWidth || 600;
4112
- scale = Math.max(0.35, Math.min(1, availW / tW));
4065
+ scale = calculateFitScale();
4113
4066
  applyScale();
4114
4067
  },
4068
+ rotateCW: () => {
4069
+ },
4070
+ rotateCCW: () => {
4071
+ },
4115
4072
  download: () => {
4116
4073
  const blob = new Blob([ctx.buffer], { type: "text/csv" });
4117
4074
  const url = URL.createObjectURL(blob);
@@ -4223,26 +4180,6 @@ var CodePlugin = class {
4223
4180
  instance.zoomIn?.();
4224
4181
  }
4225
4182
  },
4226
- {
4227
- id: "fit-page",
4228
- icon: "fit-page",
4229
- label: "Fit to View",
4230
- type: "button",
4231
- group: "zoom",
4232
- execute: () => {
4233
- instance.fitToPage?.();
4234
- }
4235
- },
4236
- {
4237
- id: "rotate-cw",
4238
- icon: "rotate-cw",
4239
- label: "Rotate",
4240
- type: "button",
4241
- group: "view",
4242
- execute: () => {
4243
- instance.rotateCW?.();
4244
- }
4245
- },
4246
4183
  {
4247
4184
  id: "copy",
4248
4185
  icon: "copy",
@@ -5386,6 +5323,148 @@ var ThreeDPlugin = class {
5386
5323
  function threeDPlugin() {
5387
5324
  return new ThreeDPlugin();
5388
5325
  }
5326
+ function cleanRtfText(raw) {
5327
+ return raw.replace(/\\bin\d+\s/g, "").replace(/{\\\*[^}]+}/g, "").replace(/\\u(-?\d+)\??/g, (_, code) => {
5328
+ let num = parseInt(code, 10);
5329
+ if (num < 0) num += 65536;
5330
+ return String.fromCharCode(num);
5331
+ }).replace(/\\'([0-9a-fA-F]{2})/g, (_, hex) => {
5332
+ return String.fromCharCode(parseInt(hex, 16));
5333
+ }).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();
5334
+ }
5335
+ function parseRtfTables(rtfText) {
5336
+ const rowSegments = rtfText.split(/\\row\b/);
5337
+ if (rowSegments.length <= 1) return [];
5338
+ const rawTables = [];
5339
+ let currentTable = null;
5340
+ let charOffset = 0;
5341
+ for (let i = 0; i < rowSegments.length - 1; i++) {
5342
+ const segment = rowSegments[i];
5343
+ const segmentStart = charOffset;
5344
+ const segmentEnd = charOffset + segment.length + 4;
5345
+ const cellParts = segment.split(/\\cell\b/);
5346
+ if (cellParts.length > 1) {
5347
+ const cellxMatches = [...segment.matchAll(/\\cellx(\d+)/g)].map((m) => parseInt(m[1], 10));
5348
+ let cellWidths = [];
5349
+ if (cellxMatches.length > 0) {
5350
+ let seq = [cellxMatches[0]];
5351
+ for (let k = 1; k < cellxMatches.length; k++) {
5352
+ if (cellxMatches[k] > cellxMatches[k - 1]) seq.push(cellxMatches[k]);
5353
+ else break;
5354
+ }
5355
+ let prev = 0;
5356
+ cellWidths = seq.map((x) => {
5357
+ const w = x - prev;
5358
+ prev = x;
5359
+ return w;
5360
+ });
5361
+ }
5362
+ const cells = [];
5363
+ for (let c = 0; c < cellParts.length - 1; c++) {
5364
+ let rawCell = cellParts[c];
5365
+ if (c === 0) {
5366
+ const lastTrowd = rawCell.lastIndexOf("\\trowd");
5367
+ if (lastTrowd !== -1) rawCell = rawCell.slice(lastTrowd);
5368
+ }
5369
+ cells.push(cleanRtfText(rawCell));
5370
+ }
5371
+ const isNewTable = !currentTable || segmentStart - currentTable.lastEnd > 2500;
5372
+ if (isNewTable) {
5373
+ const prevTextSlice = rtfText.slice(Math.max(0, segmentStart - 4e3), segmentStart);
5374
+ const cleanPrev = cleanRtfText(prevTextSlice);
5375
+ const anchorBefore = cleanPrev.slice(-80).trim();
5376
+ currentTable = {
5377
+ id: `RTF_TABLE_${rawTables.length}`,
5378
+ startPos: segmentStart,
5379
+ lastEnd: segmentEnd,
5380
+ rows: [],
5381
+ columnWidths: cellWidths,
5382
+ anchorBefore,
5383
+ anchorAfter: ""
5384
+ };
5385
+ rawTables.push(currentTable);
5386
+ }
5387
+ if (currentTable) {
5388
+ currentTable.rows.push({
5389
+ cells,
5390
+ widths: cellWidths.length === cells.length ? cellWidths : currentTable.columnWidths
5391
+ });
5392
+ currentTable.lastEnd = segmentEnd;
5393
+ }
5394
+ }
5395
+ charOffset += segment.length + 4;
5396
+ }
5397
+ rawTables.forEach((tbl) => {
5398
+ const afterSlice = rtfText.slice(tbl.lastEnd, Math.min(rtfText.length, tbl.lastEnd + 2e3));
5399
+ const cleanAfter = cleanRtfText(afterSlice);
5400
+ tbl.anchorAfter = cleanAfter.slice(0, 80).trim();
5401
+ });
5402
+ return rawTables.map((tbl) => {
5403
+ const totalW = (tbl.columnWidths || []).reduce((a, b) => a + b, 0);
5404
+ const colPercents = totalW > 0 ? tbl.columnWidths.map((w) => Math.round(w / totalW * 100)) : [];
5405
+ return {
5406
+ id: tbl.id,
5407
+ rows: tbl.rows.map((r) => r.cells),
5408
+ colPercents,
5409
+ anchorBefore: tbl.anchorBefore,
5410
+ anchorAfter: tbl.anchorAfter
5411
+ };
5412
+ });
5413
+ }
5414
+ function renderRtfTableElement(table) {
5415
+ const tableEl = document.createElement("table");
5416
+ tableEl.className = "fp-rtf-table";
5417
+ tableEl.style.width = "100%";
5418
+ tableEl.style.borderCollapse = "collapse";
5419
+ tableEl.style.margin = "16px 0 20px";
5420
+ tableEl.style.fontSize = "10pt";
5421
+ tableEl.style.lineHeight = "1.5";
5422
+ tableEl.style.border = "1px solid #cbd5e1";
5423
+ tableEl.style.borderRadius = "4px";
5424
+ tableEl.style.boxSizing = "border-box";
5425
+ tableEl.style.backgroundColor = "#ffffff";
5426
+ const thead = document.createElement("thead");
5427
+ const tbody = document.createElement("tbody");
5428
+ table.rows.forEach((rowCells, rIdx) => {
5429
+ const tr = document.createElement("tr");
5430
+ const isHeader = rIdx === 0;
5431
+ if (isHeader) {
5432
+ tr.style.backgroundColor = "#f8fafc";
5433
+ tr.style.borderBottom = "2px solid #cbd5e1";
5434
+ } else {
5435
+ tr.style.borderBottom = "1px solid #e2e8f0";
5436
+ if (rIdx % 2 === 0) {
5437
+ tr.style.backgroundColor = "#fcfdfe";
5438
+ }
5439
+ }
5440
+ rowCells.forEach((cellText, cIdx) => {
5441
+ const cellEl = document.createElement(isHeader ? "th" : "td");
5442
+ cellEl.style.padding = "8px 12px";
5443
+ cellEl.style.border = "1px solid #cbd5e1";
5444
+ cellEl.style.verticalAlign = "top";
5445
+ cellEl.style.color = isHeader ? "#0f172a" : "#1e293b";
5446
+ cellEl.style.fontWeight = isHeader ? "600" : "normal";
5447
+ if (table.colPercents && table.colPercents[cIdx]) {
5448
+ cellEl.style.width = `${table.colPercents[cIdx]}%`;
5449
+ }
5450
+ if (/^\d+$/.test(cellText.trim()) || cellText.trim() === "#") {
5451
+ cellEl.style.textAlign = "center";
5452
+ } else {
5453
+ cellEl.style.textAlign = "left";
5454
+ }
5455
+ cellEl.textContent = cellText;
5456
+ tr.appendChild(cellEl);
5457
+ });
5458
+ if (isHeader) {
5459
+ thead.appendChild(tr);
5460
+ } else {
5461
+ tbody.appendChild(tr);
5462
+ }
5463
+ });
5464
+ tableEl.appendChild(thead);
5465
+ tableEl.appendChild(tbody);
5466
+ return tableEl;
5467
+ }
5389
5468
  var RtfPlugin = class {
5390
5469
  id = "rtf";
5391
5470
  name = "Rich Text Format (RTF)";
@@ -5421,6 +5500,14 @@ var RtfPlugin = class {
5421
5500
  }
5422
5501
  });
5423
5502
  actions.push(
5503
+ {
5504
+ id: "thumbnails",
5505
+ icon: "thumbnails",
5506
+ label: "Thumbnails",
5507
+ type: "button",
5508
+ group: "view",
5509
+ execute: () => instance.toggleThumbnails?.()
5510
+ },
5424
5511
  {
5425
5512
  id: "zoom-out",
5426
5513
  icon: "zoom-out",
@@ -5548,6 +5635,8 @@ var RtfPlugin = class {
5548
5635
  if (typeof RTFJS.loggingEnabled === "function") {
5549
5636
  RTFJS.loggingEnabled(false);
5550
5637
  }
5638
+ const rawText = new TextDecoder("latin1").decode(ctx.buffer);
5639
+ const tables = parseRtfTables(rawText);
5551
5640
  const doc = new RTFJS.Document(ctx.buffer, {});
5552
5641
  const htmlElements = await doc.render();
5553
5642
  const contentNodes = [];
@@ -5562,6 +5651,20 @@ var RtfPlugin = class {
5562
5651
  if (hasChildBlocks && !["table", "tr", "td", "th", "ul", "ol", "li"].includes(tag)) {
5563
5652
  extractBlocks(Array.from(item.children));
5564
5653
  } else {
5654
+ const svgs = item.querySelectorAll("svg, img");
5655
+ svgs.forEach((s) => {
5656
+ const el = s;
5657
+ el.style.maxWidth = "100%";
5658
+ el.style.maxHeight = "360px";
5659
+ el.style.display = "block";
5660
+ el.style.margin = "12px auto";
5661
+ if (s.tagName.toLowerCase() === "svg") {
5662
+ s.removeAttribute("width");
5663
+ s.removeAttribute("height");
5664
+ el.style.width = "100%";
5665
+ el.style.maxHeight = "360px";
5666
+ }
5667
+ });
5565
5668
  if (tag === "p" || tag === "div") {
5566
5669
  item.style.display = "block";
5567
5670
  item.style.marginBottom = "12px";
@@ -5572,14 +5675,46 @@ var RtfPlugin = class {
5572
5675
  }
5573
5676
  };
5574
5677
  extractBlocks(htmlElements);
5678
+ tables.forEach((table) => {
5679
+ const tableEl = renderRtfTableElement(table);
5680
+ let inserted = false;
5681
+ if (table.anchorBefore && table.anchorBefore.length > 10) {
5682
+ const keyword = table.anchorBefore.slice(-35).trim();
5683
+ for (let i = 0; i < contentNodes.length; i++) {
5684
+ if (contentNodes[i].textContent?.includes(keyword)) {
5685
+ contentNodes.splice(i + 1, 0, tableEl);
5686
+ inserted = true;
5687
+ break;
5688
+ }
5689
+ }
5690
+ }
5691
+ if (!inserted && table.anchorAfter && table.anchorAfter.length > 10) {
5692
+ const keyword = table.anchorAfter.slice(0, 35).trim();
5693
+ for (let i = 0; i < contentNodes.length; i++) {
5694
+ if (contentNodes[i].textContent?.includes(keyword)) {
5695
+ contentNodes.splice(i, 0, tableEl);
5696
+ inserted = true;
5697
+ break;
5698
+ }
5699
+ }
5700
+ }
5701
+ if (!inserted) {
5702
+ if (contentNodes.length > 2) {
5703
+ contentNodes.splice(2, 0, tableEl);
5704
+ } else {
5705
+ contentNodes.push(tableEl);
5706
+ }
5707
+ }
5708
+ });
5575
5709
  wrapper.innerHTML = "";
5576
5710
  contentNodes.forEach((node) => wrapper.appendChild(node));
5577
5711
  const childHeights = contentNodes.map((c) => {
5578
5712
  const rectH = c.getBoundingClientRect ? c.getBoundingClientRect().height : 0;
5579
5713
  const offH = c.offsetHeight || 0;
5714
+ const realH = Math.max(rectH, offH);
5715
+ if (realH > 0) return realH;
5580
5716
  const textLen = c.textContent?.trim().length || 0;
5581
- const estH = Math.max(28, Math.ceil(textLen / 75) * 24 + 16);
5582
- return Math.max(rectH, offH, estH);
5717
+ return Math.max(24, Math.ceil(textLen / 95) * 22 + 12);
5583
5718
  });
5584
5719
  wrapper.innerHTML = "";
5585
5720
  const createRtfCard = () => {
@@ -5588,7 +5723,7 @@ var RtfPlugin = class {
5588
5723
  card.style.backgroundColor = "#ffffff";
5589
5724
  card.style.boxShadow = "0 4px 24px rgba(0,0,0,0.08)";
5590
5725
  card.style.borderRadius = "4px";
5591
- card.style.padding = "72px 56px";
5726
+ card.style.padding = "64px 56px";
5592
5727
  card.style.width = "816px";
5593
5728
  card.style.minHeight = "1056px";
5594
5729
  card.style.boxSizing = "border-box";
@@ -5602,22 +5737,35 @@ var RtfPlugin = class {
5602
5737
  wrapper.appendChild(curCard);
5603
5738
  pageElements = [curCard];
5604
5739
  let curH = 0;
5605
- const maxH = 912;
5740
+ const maxH = 928;
5606
5741
  for (let i = 0; i < contentNodes.length; i++) {
5607
5742
  const child = contentNodes[i];
5608
5743
  const chH = childHeights[i];
5609
- curCard.appendChild(child);
5610
- curH += chH;
5611
- if (curH >= maxH && i < contentNodes.length - 1) {
5744
+ const isChildEmpty = (child.textContent || "").trim().length === 0 && !child.querySelector("table, img, svg, canvas");
5745
+ if (curH === 0 && isChildEmpty) {
5746
+ continue;
5747
+ }
5748
+ if (curH + chH > maxH && curCard.childNodes.length > 0) {
5612
5749
  curCard = createRtfCard();
5613
5750
  wrapper.appendChild(curCard);
5614
5751
  pageElements.push(curCard);
5615
5752
  curH = 0;
5616
5753
  }
5754
+ curCard.appendChild(child);
5755
+ curH += chH;
5756
+ }
5757
+ pageElements = pageElements.filter((card) => {
5758
+ const hasText = (card.textContent || "").trim().length > 0;
5759
+ const hasMedia = card.querySelector("table, img, svg, canvas") !== null;
5760
+ return hasText || hasMedia;
5761
+ });
5762
+ if (pageElements.length === 0) {
5763
+ pageElements = [curCard];
5617
5764
  }
5618
5765
  } catch (err) {
5619
5766
  console.warn("[RtfPlugin] RTF render error, fallback text:", err);
5620
5767
  const text = new TextDecoder("latin1").decode(ctx.buffer);
5768
+ const tables = parseRtfTables(text);
5621
5769
  const rawPages = text.split(/\\page\b/).map((segment) => {
5622
5770
  return segment.replace(/\\par[d]?/g, "\n").replace(/\\[a-zA-Z0-9\-]+/g, "").replace(/[{}]/g, "").trim();
5623
5771
  }).filter((p) => p.length > 0);
@@ -5628,7 +5776,7 @@ var RtfPlugin = class {
5628
5776
  pageCard.style.backgroundColor = "#ffffff";
5629
5777
  pageCard.style.boxShadow = "0 2px 12px rgba(0,0,0,0.08)";
5630
5778
  pageCard.style.borderRadius = "4px";
5631
- pageCard.style.padding = "72px 56px";
5779
+ pageCard.style.padding = "64px 56px";
5632
5780
  pageCard.style.width = "816px";
5633
5781
  pageCard.style.minHeight = "1056px";
5634
5782
  pageCard.style.boxSizing = "border-box";
@@ -5639,6 +5787,9 @@ var RtfPlugin = class {
5639
5787
  pageCard.style.whiteSpace = "pre-wrap";
5640
5788
  pageCard.style.display = i === 0 ? "block" : "none";
5641
5789
  pageCard.textContent = pages[i];
5790
+ if (i === 1 && tables.length > 0) {
5791
+ tables.forEach((tbl) => pageCard.appendChild(renderRtfTableElement(tbl)));
5792
+ }
5642
5793
  wrapper.appendChild(pageCard);
5643
5794
  pageElements.push(pageCard);
5644
5795
  }
@@ -5669,6 +5820,32 @@ var RtfPlugin = class {
5669
5820
  getPageCount: () => totalPages,
5670
5821
  getCurrentPage: () => currentPage,
5671
5822
  goToPage: (page) => showPage(page),
5823
+ getThumbnails: async () => {
5824
+ const items = [];
5825
+ for (let idx = 0; idx < totalPages; idx++) {
5826
+ const pageIdx = idx;
5827
+ items.push({
5828
+ index: pageIdx,
5829
+ label: `Page ${pageIdx + 1}`,
5830
+ render: async (canvas) => {
5831
+ const ctx2d = canvas.getContext("2d");
5832
+ if (!ctx2d) return;
5833
+ canvas.width = 140;
5834
+ canvas.height = 180;
5835
+ ctx2d.fillStyle = "#ffffff";
5836
+ ctx2d.fillRect(0, 0, 140, 180);
5837
+ ctx2d.strokeStyle = "#cbd5e1";
5838
+ ctx2d.lineWidth = 1;
5839
+ ctx2d.strokeRect(0.5, 0.5, 139, 179);
5840
+ ctx2d.fillStyle = "#334155";
5841
+ ctx2d.font = 'bold 11px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
5842
+ ctx2d.textAlign = "center";
5843
+ ctx2d.fillText(`Page ${pageIdx + 1}`, 70, 95);
5844
+ }
5845
+ });
5846
+ }
5847
+ return items;
5848
+ },
5672
5849
  zoomIn: () => {
5673
5850
  isUserZoomed = true;
5674
5851
  scale += 0.15;