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