@open-file-viewer/core 0.1.20 → 0.1.21

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
@@ -569,7 +569,40 @@ var defaultMessages = {
569
569
  size: "\u5927\u5C0F",
570
570
  source: "\u6765\u6E90",
571
571
  remoteUrl: "\u8FDC\u7A0B URL",
572
- localFile: "\u672C\u5730/\u5185\u5B58\u6587\u4EF6"
572
+ localFile: "\u672C\u5730/\u5185\u5B58\u6587\u4EF6",
573
+ textPlainLanguage: "\u7EAF\u6587\u672C",
574
+ textLineCount: "{count} \u884C",
575
+ textWrap: "\u6362\u884C",
576
+ textCopy: "\u590D\u5236",
577
+ textCopied: "\u5DF2\u590D\u5236",
578
+ textCopyFailed: "\u590D\u5236\u5931\u8D25",
579
+ textDownload: "\u4E0B\u8F7D",
580
+ textDownloadReady: "\u4E0B\u8F7D\u5DF2\u51C6\u5907",
581
+ textLargeFileNotice: "\u6587\u4EF6\u8F83\u5927\uFF0C\u5F53\u524D\u5C55\u793A\u524D {size}\uFF0C\u590D\u5236\u548C\u4E0B\u8F7D\u4ECD\u4F1A\u4F7F\u7528\u5B8C\u6574\u5185\u5BB9\u3002",
582
+ textHighlightSkipped: "\u5185\u5BB9\u8F83\u5927\uFF0C\u5DF2\u8DF3\u8FC7\u8BED\u6CD5\u9AD8\u4EAE\u4EE5\u4FDD\u6301\u6EDA\u52A8\u6D41\u7545\u3002",
583
+ textPreviewFailedTitle: "\u6587\u672C\u9884\u89C8\u5931\u8D25",
584
+ textPreviewFailedMessage: "\u65E0\u6CD5\u8BFB\u53D6\u8BE5\u6587\u672C\u5185\u5BB9\uFF0C\u53EF\u80FD\u662F\u8FDC\u7A0B\u6587\u4EF6\u4E0D\u53EF\u8BBF\u95EE\u6216\u54CD\u5E94\u72B6\u6001\u5F02\u5E38\u3002",
585
+ textOpenOriginal: "\u6253\u5F00\u539F\u6587\u4EF6",
586
+ officeLegacyConversionTitle: "Office \u8F6C\u6362\u63D0\u793A",
587
+ officeLegacyBinaryNotice: "\u5C5E\u4E8E\u65E7\u7248 Microsoft Office \u4E8C\u8FDB\u5236\u683C\u5F0F\uFF0C\u6D4F\u89C8\u5668\u5185\u65E0\u6CD5\u9AD8\u4FDD\u771F\u89E3\u6790\uFF1B\u5F53\u524D\u4EC5\u5C55\u793A\u53EF\u4FE1\u6587\u672C\u7247\u6BB5\u548C\u7ED3\u6784\u6307\u7EB9\uFF0C\u5B8C\u6574\u6392\u7248\u5EFA\u8BAE\u63A5\u5165 LibreOffice/OnlyOffice \u670D\u52A1\u7AEF\u8F6C\u6362\u4E3A PDF/HTML\u3002",
588
+ officeLegacyMetaFormatType: "\u683C\u5F0F\u7C7B\u578B",
589
+ officeLegacyMetaFileStructure: "\u6587\u4EF6\u7ED3\u6784",
590
+ officeLegacyOleDetected: "\u68C0\u6D4B\u5230 OLE Compound File \u7B7E\u540D",
591
+ officeLegacyOleMissing: "\u672A\u68C0\u6D4B\u5230\u6807\u51C6 OLE \u7B7E\u540D\uFF0C\u6309\u539F\u59CB\u4E8C\u8FDB\u5236\u5C1D\u8BD5\u63D0\u53D6",
592
+ officeLegacyMetaTextFragments: "\u6587\u672C\u7247\u6BB5",
593
+ officeLegacyTextFragmentCount: "{count} \u6BB5",
594
+ officeLegacyMetaParseStatus: "\u89E3\u6790\u72B6\u6001",
595
+ officeLegacyReadableFragments: "\u53EF\u8BFB\u6587\u672C\u7247\u6BB5",
596
+ officeLegacyNoText: "\u672A\u63D0\u53D6\u5230\u7A33\u5B9A\u53EF\u8BFB\u6587\u672C\u3002\u8BE5\u6587\u4EF6\u53EF\u80FD\u7ECF\u8FC7\u538B\u7F29\u3001\u52A0\u5BC6\uFF0C\u6216\u6587\u672C\u7F16\u7801\u65E0\u6CD5\u5728\u6D4F\u89C8\u5668\u7AEF\u53EF\u9760\u8BC6\u522B\uFF1B\u8BF7\u4F7F\u7528\u670D\u52A1\u7AEF LibreOffice/OnlyOffice \u8F6C\u6362\u540E\u9884\u89C8\u3002",
597
+ officeLegacyWordParseFailed: "Word \u4E8C\u8FDB\u5236\u89E3\u6790\u5931\u8D25\uFF1A{message}",
598
+ officeSheetParseFailed: "\u8868\u683C\u89E3\u6790\u5931\u8D25\uFF1A{message}",
599
+ officeUnsupportedTitle: "Office \u57FA\u7840\u9884\u89C8",
600
+ officeUnsupportedLegacyMessage: "\u8BE5\u683C\u5F0F\u5C5E\u4E8E\u8001\u4E8C\u8FDB\u5236\u6216\u4E13\u6709\u683C\u5F0F\uFF0C\u6D4F\u89C8\u5668\u5185\u65E0\u6CD5\u53EF\u9760\u89E3\u6790\uFF1B\u5EFA\u8BAE\u63A5\u5165 LibreOffice/OnlyOffice \u670D\u52A1\u7AEF\u8F6C\u6362\u4E3A PDF/HTML \u540E\u9884\u89C8\u3002",
601
+ officeUnsupportedGenericMessage: "\u8BE5\u683C\u5F0F\u901A\u5E38\u9700\u8981\u670D\u52A1\u7AEF\u8F6C\u6362\u6216\u4E13\u7528\u89E3\u6790\u5668\u624D\u80FD\u9AD8\u4FDD\u771F\u9884\u89C8\u3002",
602
+ officeUnsupportedIntro: "\u5DF2\u8FDB\u5165 Office \u63D2\u4EF6\u3002{message}",
603
+ officeUnsupportedSupportedFormats: "\u5F53\u524D\u7248\u672C\u4F18\u5148\u652F\u6301 docx\u3001rtf\u3001odt/fodt\u3001xlsx/xls/csv/ods\u3001pptx/ppsx\u3001odp/fodp \u7684\u57FA\u7840\u5185\u5BB9\u9884\u89C8\u3002",
604
+ officeErrorWithMessage: "\u89E3\u6790\u5668\u8FD4\u56DE\uFF1A{message}",
605
+ officeErrorWithoutMessage: "\u89E3\u6790\u5668\u672A\u8FD4\u56DE\u5177\u4F53\u9519\u8BEF\u4FE1\u606F\u3002"
573
606
  },
574
607
  "en-US": {
575
608
  loading: "Loading preview...",
@@ -585,7 +618,40 @@ var defaultMessages = {
585
618
  size: "Size",
586
619
  source: "Source",
587
620
  remoteUrl: "Remote URL",
588
- localFile: "Local or in-memory file"
621
+ localFile: "Local or in-memory file",
622
+ textPlainLanguage: "plain text",
623
+ textLineCount: "{count} lines",
624
+ textWrap: "Wrap",
625
+ textCopy: "Copy",
626
+ textCopied: "Copied",
627
+ textCopyFailed: "Copy failed",
628
+ textDownload: "Download",
629
+ textDownloadReady: "Download ready",
630
+ textLargeFileNotice: "Large file, showing the first {size}. Copy and download still use the full content.",
631
+ textHighlightSkipped: "Large content, syntax highlighting was skipped to keep scrolling smooth.",
632
+ textPreviewFailedTitle: "Text preview failed",
633
+ textPreviewFailedMessage: "Unable to read this text content. The remote file may be unreachable or returned an invalid response.",
634
+ textOpenOriginal: "Open original file",
635
+ officeLegacyConversionTitle: "Office conversion guidance",
636
+ officeLegacyBinaryNotice: "belongs to a legacy Microsoft Office binary format that cannot be rendered with high fidelity in the browser. The preview shows trusted text fragments and structural fingerprints; use a LibreOffice/OnlyOffice server conversion to PDF/HTML for complete layout fidelity.",
637
+ officeLegacyMetaFormatType: "Format type",
638
+ officeLegacyMetaFileStructure: "File structure",
639
+ officeLegacyOleDetected: "OLE Compound File signature detected",
640
+ officeLegacyOleMissing: "No standard OLE signature detected; extracting from raw binary data",
641
+ officeLegacyMetaTextFragments: "Text fragments",
642
+ officeLegacyTextFragmentCount: "{count} fragments",
643
+ officeLegacyMetaParseStatus: "Parse status",
644
+ officeLegacyReadableFragments: "Readable text fragments",
645
+ officeLegacyNoText: "No stable readable text was extracted. The file may be compressed, encrypted, or use text encoding that cannot be reliably recognized in the browser; use LibreOffice/OnlyOffice server conversion before previewing.",
646
+ officeLegacyWordParseFailed: "Word binary parse failed: {message}",
647
+ officeSheetParseFailed: "Spreadsheet parse failed: {message}",
648
+ officeUnsupportedTitle: "Office basic preview",
649
+ officeUnsupportedLegacyMessage: "This is a legacy binary or proprietary format that cannot be reliably parsed in the browser; convert it to PDF/HTML through LibreOffice/OnlyOffice on the server before previewing.",
650
+ officeUnsupportedGenericMessage: "This format usually needs server-side conversion or a dedicated parser for high-fidelity preview.",
651
+ officeUnsupportedIntro: "is handled by the Office plugin. {message}",
652
+ officeUnsupportedSupportedFormats: "This version prioritizes basic previews for docx, rtf, odt/fodt, xlsx/xls/csv/ods, pptx/ppsx, and odp/fodp files.",
653
+ officeErrorWithMessage: "Parser returned: {message}",
654
+ officeErrorWithoutMessage: "Parser did not return a specific error."
589
655
  }
590
656
  };
591
657
  function resolveMessages(options) {
@@ -594,6 +660,9 @@ function resolveMessages(options) {
594
660
  ...options.messages
595
661
  };
596
662
  }
663
+ function formatPreviewMessage(template, values) {
664
+ return template.replace(/\{(\w+)\}/g, (match, key) => String(values[key] ?? match));
665
+ }
597
666
 
598
667
  // src/plugins/fallback.ts
599
668
  function fallbackPlugin() {
@@ -664,9 +733,10 @@ function formatFallbackBytes(bytes) {
664
733
  function createViewer(options) {
665
734
  const container = resolveContainer(options.container);
666
735
  applyBoxSize(container, options.width, options.height);
736
+ const customClassNames = parseClassNameTokens(options.className);
667
737
  container.classList.add("ofv-root");
668
- if (options.className) {
669
- container.classList.add(options.className);
738
+ if (customClassNames.length > 0) {
739
+ container.classList.add(...customClassNames);
670
740
  }
671
741
  const theme = applyTheme(container, options.theme || "light");
672
742
  const host = document.createElement("div");
@@ -810,12 +880,15 @@ function createViewer(options) {
810
880
  theme.destroy();
811
881
  container.replaceChildren();
812
882
  container.classList.remove("ofv-root");
813
- if (options.className) {
814
- container.classList.remove(options.className);
883
+ if (customClassNames.length > 0) {
884
+ container.classList.remove(...customClassNames);
815
885
  }
816
886
  }
817
887
  };
818
888
  }
889
+ function parseClassNameTokens(className) {
890
+ return className?.trim().split(/\s+/).filter(Boolean) ?? [];
891
+ }
819
892
  function destroyPreviewInstance(instance) {
820
893
  if (!instance) {
821
894
  return;
@@ -941,6 +1014,7 @@ function createToolbar(toolbar, viewport, queue) {
941
1014
  let previousButton;
942
1015
  let nextButton;
943
1016
  let zoomResetButton;
1017
+ let fullscreenButton;
944
1018
  let currentZoom;
945
1019
  const commandButtons = [];
946
1020
  const customButtons = [];
@@ -1046,13 +1120,14 @@ function createToolbar(toolbar, viewport, queue) {
1046
1120
  return;
1047
1121
  }
1048
1122
  if (id === "fullscreen" && options.fullscreen !== false) {
1049
- addButton(
1123
+ fullscreenButton = addButton(
1050
1124
  getToolbarLabel(options, id),
1051
1125
  getToolbarTitle(options, id),
1052
1126
  () => getContext().fullscreen(),
1053
1127
  void 0,
1054
1128
  options.icons?.fullscreen
1055
1129
  );
1130
+ updateFullscreenButton();
1056
1131
  return;
1057
1132
  }
1058
1133
  if (id === "print" && options.print) {
@@ -1154,6 +1229,32 @@ function createToolbar(toolbar, viewport, queue) {
1154
1229
  updateCustomButtons();
1155
1230
  refreshCustomRender();
1156
1231
  }
1232
+ function isFullscreenActive() {
1233
+ return Boolean(
1234
+ typeof document !== "undefined" && element.parentElement && document.fullscreenElement === element.parentElement
1235
+ );
1236
+ }
1237
+ function updateFullscreenButton() {
1238
+ if (!fullscreenButton) {
1239
+ return;
1240
+ }
1241
+ const active = isFullscreenActive();
1242
+ const id = active ? "exit-fullscreen" : "fullscreen";
1243
+ const icon = active ? options.icons?.["exit-fullscreen"] ?? options.icons?.fullscreen : options.icons?.fullscreen;
1244
+ setToolbarButtonContent(fullscreenButton, getToolbarLabel(options, id), icon);
1245
+ const title = getToolbarTitle(options, id);
1246
+ fullscreenButton.title = title;
1247
+ fullscreenButton.setAttribute("aria-label", title);
1248
+ fullscreenButton.setAttribute("aria-pressed", String(active));
1249
+ }
1250
+ const handleFullscreenChange = () => {
1251
+ updateFullscreenButton();
1252
+ refreshCustomRender();
1253
+ };
1254
+ if (typeof document !== "undefined") {
1255
+ document.addEventListener("fullscreenchange", handleFullscreenChange);
1256
+ disposers.push(() => document.removeEventListener("fullscreenchange", handleFullscreenChange));
1257
+ }
1157
1258
  const refreshCustomRender = () => {
1158
1259
  if (!options.render) {
1159
1260
  return;
@@ -1227,6 +1328,9 @@ function createToolbarContext({
1227
1328
  viewport,
1228
1329
  canPrevious: index > 0,
1229
1330
  canNext: index < length - 1,
1331
+ isFullscreen: Boolean(
1332
+ typeof document !== "undefined" && element.parentElement && document.fullscreenElement === element.parentElement
1333
+ ),
1230
1334
  zoom,
1231
1335
  zoomLabel: zoom === void 0 ? void 0 : formatToolbarZoom(zoom),
1232
1336
  async previous() {
@@ -1245,7 +1349,15 @@ function createToolbarContext({
1245
1349
  }
1246
1350
  },
1247
1351
  fullscreen() {
1248
- void element.parentElement?.requestFullscreen?.();
1352
+ const target = element.parentElement;
1353
+ if (!target) {
1354
+ return;
1355
+ }
1356
+ if (typeof document !== "undefined" && document.fullscreenElement === target) {
1357
+ void document.exitFullscreen?.();
1358
+ } else {
1359
+ void target.requestFullscreen?.();
1360
+ }
1249
1361
  },
1250
1362
  print() {
1251
1363
  printPreview(viewport);
@@ -1264,6 +1376,7 @@ var defaultToolbarLabels = {
1264
1376
  "rotate-right": "Rotate",
1265
1377
  download: "Download",
1266
1378
  fullscreen: "Fullscreen",
1379
+ "exit-fullscreen": "Exit fullscreen",
1267
1380
  print: "Print",
1268
1381
  search: "Search"
1269
1382
  };
@@ -1277,6 +1390,7 @@ var defaultToolbarTitles = {
1277
1390
  "rotate-right": "Rotate right",
1278
1391
  download: "Download file",
1279
1392
  fullscreen: "Open preview fullscreen",
1393
+ "exit-fullscreen": "Exit fullscreen",
1280
1394
  print: "Print preview",
1281
1395
  search: "Search preview text"
1282
1396
  };
@@ -1515,6 +1629,9 @@ function collectSearchableTextNodes(root) {
1515
1629
  if (["SCRIPT", "STYLE", "TEXTAREA", "INPUT", "BUTTON"].includes(parent.tagName)) {
1516
1630
  return NodeFilter.FILTER_REJECT;
1517
1631
  }
1632
+ if (hasHiddenSearchAncestor(parent, root)) {
1633
+ return NodeFilter.FILTER_REJECT;
1634
+ }
1518
1635
  return NodeFilter.FILTER_ACCEPT;
1519
1636
  }
1520
1637
  });
@@ -1525,6 +1642,19 @@ function collectSearchableTextNodes(root) {
1525
1642
  }
1526
1643
  return nodes;
1527
1644
  }
1645
+ function hasHiddenSearchAncestor(element, root) {
1646
+ let current = element;
1647
+ while (current) {
1648
+ if (current.hidden || current.getAttribute("aria-hidden") === "true" || current.style.display === "none" || current.style.visibility === "hidden") {
1649
+ return true;
1650
+ }
1651
+ if (current === root) {
1652
+ break;
1653
+ }
1654
+ current = current.parentElement;
1655
+ }
1656
+ return false;
1657
+ }
1528
1658
  function printPreview(viewport) {
1529
1659
  const frame = document.createElement("iframe");
1530
1660
  frame.className = "ofv-print-frame";
@@ -4022,7 +4152,7 @@ function textPlugin() {
4022
4152
  const isMarkdown = lang === "markdown";
4023
4153
  const text = await readText(ctx.file.source).catch((error) => void 0);
4024
4154
  if (text === void 0) {
4025
- const fallback = createTextFallback(ctx.file.name, ctx.file.url);
4155
+ const fallback = createTextFallback(ctx.file.name, ctx.options.messages, ctx.file.url);
4026
4156
  ctx.viewport.classList.add("ofv-center");
4027
4157
  ctx.viewport.append(fallback);
4028
4158
  return {
@@ -4194,10 +4324,11 @@ function textPlugin() {
4194
4324
  title.className = "ofv-code-title";
4195
4325
  const fileName = document.createElement("strong");
4196
4326
  fileName.textContent = ctx.file.name;
4327
+ const messages = ctx.options.messages;
4197
4328
  const meta = document.createElement("span");
4198
4329
  meta.textContent = [
4199
- lang === "none" ? "plain text" : lang,
4200
- `${totalLines.toLocaleString()} lines`,
4330
+ lang === "none" ? messages.textPlainLanguage : lang,
4331
+ formatPreviewMessage(messages.textLineCount, { count: totalLines.toLocaleString() }),
4201
4332
  formatBytes(ctx.file.size ?? (ctx.file.source instanceof Blob ? ctx.file.source.size : text.length))
4202
4333
  ].join(" \xB7 ");
4203
4334
  title.append(fileName, meta);
@@ -4209,7 +4340,7 @@ function textPlugin() {
4209
4340
  const wrapButton = document.createElement("button");
4210
4341
  wrapButton.type = "button";
4211
4342
  wrapButton.className = "ofv-code-action";
4212
- wrapButton.textContent = "Wrap";
4343
+ wrapButton.textContent = messages.textWrap;
4213
4344
  wrapButton.setAttribute("aria-pressed", String(defaultWrapped));
4214
4345
  wrapButton.addEventListener("click", () => {
4215
4346
  const wrapped = wrapper.classList.toggle("is-wrapped");
@@ -4218,14 +4349,14 @@ function textPlugin() {
4218
4349
  const copyButton = document.createElement("button");
4219
4350
  copyButton.type = "button";
4220
4351
  copyButton.className = "ofv-code-action";
4221
- copyButton.textContent = "Copy";
4352
+ copyButton.textContent = messages.textCopy;
4222
4353
  copyButton.addEventListener("click", async () => {
4223
4354
  copyButton.disabled = true;
4224
4355
  try {
4225
4356
  await copyToClipboard(text);
4226
- status.textContent = "Copied";
4357
+ status.textContent = messages.textCopied;
4227
4358
  } catch {
4228
- status.textContent = "Copy failed";
4359
+ status.textContent = messages.textCopyFailed;
4229
4360
  } finally {
4230
4361
  copyButton.disabled = false;
4231
4362
  }
@@ -4233,10 +4364,10 @@ function textPlugin() {
4233
4364
  const downloadButton = document.createElement("button");
4234
4365
  downloadButton.type = "button";
4235
4366
  downloadButton.className = "ofv-code-action";
4236
- downloadButton.textContent = "Download";
4367
+ downloadButton.textContent = messages.textDownload;
4237
4368
  downloadButton.addEventListener("click", () => {
4238
4369
  downloadText(ctx.file.name, text);
4239
- status.textContent = "Download ready";
4370
+ status.textContent = messages.textDownloadReady;
4240
4371
  });
4241
4372
  actions.append(wrapButton, copyButton, downloadButton, status);
4242
4373
  header.append(title, actions);
@@ -4261,13 +4392,13 @@ function textPlugin() {
4261
4392
  if (truncated) {
4262
4393
  const notice = document.createElement("div");
4263
4394
  notice.className = "ofv-code-notice";
4264
- notice.textContent = `\u6587\u4EF6\u8F83\u5927\uFF0C\u5F53\u524D\u5C55\u793A\u524D ${formatBytes(codeText.length)}\uFF0C\u590D\u5236\u548C\u4E0B\u8F7D\u4ECD\u4F1A\u4F7F\u7528\u5B8C\u6574\u5185\u5BB9\u3002`;
4395
+ notice.textContent = formatPreviewMessage(messages.textLargeFileNotice, { size: formatBytes(codeText.length) });
4265
4396
  wrapper.append(notice);
4266
4397
  }
4267
4398
  if (!shouldHighlight) {
4268
4399
  const notice = document.createElement("div");
4269
4400
  notice.className = "ofv-code-notice";
4270
- notice.textContent = "\u5185\u5BB9\u8F83\u5927\uFF0C\u5DF2\u8DF3\u8FC7\u8BED\u6CD5\u9AD8\u4EAE\u4EE5\u4FDD\u6301\u6EDA\u52A8\u6D41\u7545\u3002";
4401
+ notice.textContent = messages.textHighlightSkipped;
4271
4402
  wrapper.append(notice);
4272
4403
  }
4273
4404
  wrapper.appendChild(body);
@@ -4334,19 +4465,19 @@ function normalizeFileName2(name) {
4334
4465
  const baseName = name.split(/[\\/]/).pop() || name;
4335
4466
  return baseName.toLowerCase();
4336
4467
  }
4337
- function createTextFallback(fileName, url) {
4468
+ function createTextFallback(fileName, messages, url) {
4338
4469
  const fallback = document.createElement("div");
4339
4470
  fallback.className = "ofv-fallback";
4340
4471
  const title = document.createElement("strong");
4341
- title.textContent = "\u6587\u672C\u9884\u89C8\u5931\u8D25";
4472
+ title.textContent = messages.textPreviewFailedTitle;
4342
4473
  const meta = document.createElement("span");
4343
- meta.textContent = "\u65E0\u6CD5\u8BFB\u53D6\u8BE5\u6587\u672C\u5185\u5BB9\uFF0C\u53EF\u80FD\u662F\u8FDC\u7A0B\u6587\u4EF6\u4E0D\u53EF\u8BBF\u95EE\u6216\u54CD\u5E94\u72B6\u6001\u5F02\u5E38\u3002";
4474
+ meta.textContent = messages.textPreviewFailedMessage;
4344
4475
  fallback.append(title, meta);
4345
4476
  if (url) {
4346
4477
  const download = document.createElement("a");
4347
4478
  download.href = url;
4348
4479
  download.download = fileName;
4349
- download.textContent = "\u6253\u5F00\u539F\u6587\u4EF6";
4480
+ download.textContent = messages.textOpenOriginal;
4350
4481
  fallback.append(download);
4351
4482
  }
4352
4483
  return fallback;
@@ -7408,7 +7539,7 @@ function officePlugin(options = {}) {
7408
7539
  } else if (packageFormat === "docx" && !fileIsDocx(extension)) {
7409
7540
  disposeDocxFit = await renderDocx(panel, arrayBuffer, ctx.options.fit);
7410
7541
  } else if (packageFormat === "xlsx" && !sheetExtensions.has(extension)) {
7411
- await renderSheet(panel, arrayBuffer, "xlsx");
7542
+ await renderSheet(panel, arrayBuffer, "xlsx", ctx.options.messages);
7412
7543
  } else if (packageFormat === "pptx" && !["pptx", "pptm", "ppsx", "ppsm", "potx", "potm"].includes(extension)) {
7413
7544
  await renderPptx(panel, arrayBuffer);
7414
7545
  } else if (fileIsDocx(extension)) {
@@ -7421,9 +7552,9 @@ function officePlugin(options = {}) {
7421
7552
  renderOpenDocumentXml(panel, "FODT \u6587\u6863", await readTextFromBuffer(arrayBuffer));
7422
7553
  } else if (extension === "fods") {
7423
7554
  renderFlatOds(panel, await readTextFromBuffer(arrayBuffer));
7424
- } else if (packagedOfficeCandidates.has(extension) && await renderPackagedOfficePreview(panel, arrayBuffer, extension, ctx.options.fit)) {
7555
+ } else if (packagedOfficeCandidates.has(extension) && await renderPackagedOfficePreview(panel, arrayBuffer, extension, ctx.options.fit, ctx.options.messages)) {
7425
7556
  } else if (sheetExtensions.has(extension)) {
7426
- await renderSheet(panel, arrayBuffer, extension);
7557
+ await renderSheet(panel, arrayBuffer, extension, ctx.options.messages);
7427
7558
  } else if (["pptx", "pptm", "ppsx", "ppsm", "potx", "potm"].includes(extension)) {
7428
7559
  await renderPptx(panel, arrayBuffer);
7429
7560
  } else if (extension === "odp") {
@@ -7431,11 +7562,11 @@ function officePlugin(options = {}) {
7431
7562
  } else if (extension === "fodp") {
7432
7563
  renderOpenDocumentPresentationXml(panel, await readTextFromBuffer(arrayBuffer));
7433
7564
  } else if (extension === "doc" || extension === "dot") {
7434
- renderLegacyWordBinary(panel, extension, arrayBuffer);
7565
+ renderLegacyWordBinary(panel, extension, arrayBuffer, ctx.options.messages);
7435
7566
  } else if (isLegacyOfficeBinary(extension)) {
7436
- renderLegacyOfficeBinary(panel, extension, arrayBuffer);
7567
+ renderLegacyOfficeBinary(panel, extension, arrayBuffer, ctx.options.messages);
7437
7568
  } else {
7438
- renderUnsupportedOffice(panel, extension || ctx.file.extension || "office");
7569
+ renderUnsupportedOffice(panel, extension || ctx.file.extension || "office", ctx.options.messages);
7439
7570
  }
7440
7571
  const controller = createOfficeZoomController(panel, ctx);
7441
7572
  ctx.toolbar?.refreshCommandSupport();
@@ -8780,7 +8911,7 @@ function renderPlainDocument(panel, title, text) {
8780
8911
  section.append(pre);
8781
8912
  panel.append(section);
8782
8913
  }
8783
- async function renderSheet(panel, arrayBuffer, extension) {
8914
+ async function renderSheet(panel, arrayBuffer, extension, messages) {
8784
8915
  const xlsx = await import("xlsx");
8785
8916
  let workbook;
8786
8917
  try {
@@ -8793,10 +8924,16 @@ async function renderSheet(panel, arrayBuffer, extension) {
8793
8924
  }) : xlsx.read(arrayBuffer, { type: "array", cellDates: true, cellNF: true, cellStyles: true });
8794
8925
  } catch (error) {
8795
8926
  if (isLegacyOfficeBinary(extension)) {
8796
- renderLegacyOfficeBinary(panel, extension, arrayBuffer, `\u8868\u683C\u89E3\u6790\u5931\u8D25\uFF1A${normalizeOfficeError(error)}`);
8927
+ renderLegacyOfficeBinary(
8928
+ panel,
8929
+ extension,
8930
+ arrayBuffer,
8931
+ messages,
8932
+ formatPreviewMessage(messages.officeSheetParseFailed, { message: normalizeOfficeError(error, messages) })
8933
+ );
8797
8934
  return;
8798
8935
  }
8799
- renderSheetFallback(panel, extension, normalizeOfficeError(error));
8936
+ renderSheetFallback(panel, extension, normalizeOfficeError(error, messages));
8800
8937
  return;
8801
8938
  }
8802
8939
  const chartPreviews = await readWorkbookCharts(arrayBuffer).catch(() => []);
@@ -10057,10 +10194,15 @@ function pptxRenderTimeoutMs() {
10057
10194
  function normalizePptxLayout(container) {
10058
10195
  const slideCanvases = findPptxSlideCanvases(container);
10059
10196
  for (const slide of slideCanvases) {
10060
- slide.style.backgroundColor = "#FFFFFF";
10197
+ if (!hasInlineBackground(slide)) {
10198
+ slide.style.backgroundColor = "#FFFFFF";
10199
+ }
10061
10200
  }
10062
10201
  normalizePptxMirroredText(container);
10063
10202
  }
10203
+ function hasInlineBackground(element) {
10204
+ return Boolean(element.style.background || element.style.backgroundColor || element.style.backgroundImage);
10205
+ }
10064
10206
  function normalizePptxMirroredText(container) {
10065
10207
  const mirroredContainers = Array.from(container.querySelectorAll("div")).filter((element) => {
10066
10208
  const text = element.textContent?.trim();
@@ -10152,7 +10294,7 @@ function renderOpenDocumentPresentationXml(panel, xml) {
10152
10294
  renderPresentationInsight(panel, inspectOpenDocumentPresentation("FODP \u6F14\u793A\u6587\u7A3F", xml, 0));
10153
10295
  renderOpenDocumentPresentation(panel, "FODP \u6F14\u793A\u6587\u7A3F", xml, []);
10154
10296
  }
10155
- async function renderPackagedOfficePreview(panel, arrayBuffer, extension, fit) {
10297
+ async function renderPackagedOfficePreview(panel, arrayBuffer, extension, fit, messages) {
10156
10298
  let zip;
10157
10299
  try {
10158
10300
  zip = await JSZip3.loadAsync(arrayBuffer);
@@ -10167,7 +10309,7 @@ async function renderPackagedOfficePreview(panel, arrayBuffer, extension, fit) {
10167
10309
  return true;
10168
10310
  }
10169
10311
  if (hasEntry("xl/workbook.xml")) {
10170
- await renderSheet(panel, arrayBuffer, extension);
10312
+ await renderSheet(panel, arrayBuffer, extension, messages);
10171
10313
  return true;
10172
10314
  }
10173
10315
  if (hasEntry("ppt/presentation.xml")) {
@@ -10598,41 +10740,50 @@ function legacyOfficeFormatLabel(extension) {
10598
10740
  }
10599
10741
  return "PowerPoint Binary File Format";
10600
10742
  }
10601
- function renderLegacyWordBinary(panel, extension, arrayBuffer) {
10743
+ function renderLegacyWordBinary(panel, extension, arrayBuffer, messages) {
10602
10744
  try {
10603
10745
  renderLegacyWordDocument(panel, parseLegacyWordDocument(arrayBuffer));
10604
10746
  } catch (error) {
10605
- renderLegacyOfficeBinary(panel, extension, arrayBuffer, `Word \u4E8C\u8FDB\u5236\u89E3\u6790\u5931\u8D25\uFF1A${normalizeOfficeError(error)}`);
10747
+ renderLegacyOfficeBinary(
10748
+ panel,
10749
+ extension,
10750
+ arrayBuffer,
10751
+ messages,
10752
+ formatPreviewMessage(messages.officeLegacyWordParseFailed, { message: normalizeOfficeError(error, messages) })
10753
+ );
10606
10754
  }
10607
10755
  }
10608
- function renderLegacyOfficeBinary(panel, extension, arrayBuffer, parseError) {
10756
+ function renderLegacyOfficeBinary(panel, extension, arrayBuffer, messages, parseError) {
10609
10757
  const fragments = extractLegacyOfficeText(arrayBuffer);
10610
10758
  panel.replaceChildren();
10611
- const section = createSection("Office \u8F6C\u6362\u63D0\u793A");
10759
+ const section = createSection(messages.officeLegacyConversionTitle);
10612
10760
  section.classList.add("ofv-office-conversion");
10613
10761
  const format = document.createElement("p");
10614
10762
  const strong = document.createElement("strong");
10615
10763
  strong.textContent = `.${extension}`;
10616
- format.append(
10617
- strong,
10618
- document.createTextNode(
10619
- " \u5C5E\u4E8E\u65E7\u7248 Microsoft Office \u4E8C\u8FDB\u5236\u683C\u5F0F\uFF0C\u6D4F\u89C8\u5668\u5185\u65E0\u6CD5\u9AD8\u4FDD\u771F\u89E3\u6790\uFF1B\u5F53\u524D\u4EC5\u5C55\u793A\u53EF\u4FE1\u6587\u672C\u7247\u6BB5\u548C\u7ED3\u6784\u6307\u7EB9\uFF0C\u5B8C\u6574\u6392\u7248\u5EFA\u8BAE\u63A5\u5165 LibreOffice/OnlyOffice \u670D\u52A1\u7AEF\u8F6C\u6362\u4E3A PDF/HTML\u3002"
10620
- )
10621
- );
10764
+ format.append(strong, document.createTextNode(" "), document.createTextNode(messages.officeLegacyBinaryNotice));
10622
10765
  const meta = document.createElement("dl");
10623
10766
  meta.className = "ofv-office-binary-meta";
10624
- appendOfficeBinaryMeta(meta, "\u683C\u5F0F\u7C7B\u578B", legacyOfficeFormatLabel(extension));
10625
- appendOfficeBinaryMeta(meta, "\u6587\u4EF6\u7ED3\u6784", hasOleSignature(arrayBuffer) ? "\u68C0\u6D4B\u5230 OLE Compound File \u7B7E\u540D" : "\u672A\u68C0\u6D4B\u5230\u6807\u51C6 OLE \u7B7E\u540D\uFF0C\u6309\u539F\u59CB\u4E8C\u8FDB\u5236\u5C1D\u8BD5\u63D0\u53D6");
10626
- appendOfficeBinaryMeta(meta, "\u6587\u672C\u7247\u6BB5", `${fragments.length} \u6BB5`);
10767
+ appendOfficeBinaryMeta(meta, messages.officeLegacyMetaFormatType, legacyOfficeFormatLabel(extension));
10768
+ appendOfficeBinaryMeta(
10769
+ meta,
10770
+ messages.officeLegacyMetaFileStructure,
10771
+ hasOleSignature(arrayBuffer) ? messages.officeLegacyOleDetected : messages.officeLegacyOleMissing
10772
+ );
10773
+ appendOfficeBinaryMeta(
10774
+ meta,
10775
+ messages.officeLegacyMetaTextFragments,
10776
+ formatPreviewMessage(messages.officeLegacyTextFragmentCount, { count: fragments.length.toLocaleString() })
10777
+ );
10627
10778
  if (parseError) {
10628
- appendOfficeBinaryMeta(meta, "\u89E3\u6790\u72B6\u6001", parseError);
10779
+ appendOfficeBinaryMeta(meta, messages.officeLegacyMetaParseStatus, parseError);
10629
10780
  }
10630
10781
  section.append(format, meta);
10631
10782
  if (fragments.length > 0) {
10632
10783
  const article = document.createElement("article");
10633
10784
  article.className = "ofv-document ofv-office-binary-fragments";
10634
10785
  const heading = document.createElement("h4");
10635
- heading.textContent = "\u53EF\u8BFB\u6587\u672C\u7247\u6BB5";
10786
+ heading.textContent = messages.officeLegacyReadableFragments;
10636
10787
  article.append(heading);
10637
10788
  for (const fragment of fragments.slice(0, 80)) {
10638
10789
  const paragraph = document.createElement("p");
@@ -10643,7 +10794,7 @@ function renderLegacyOfficeBinary(panel, extension, arrayBuffer, parseError) {
10643
10794
  } else {
10644
10795
  const empty = document.createElement("p");
10645
10796
  empty.className = "ofv-office-binary-empty";
10646
- empty.textContent = "\u672A\u63D0\u53D6\u5230\u7A33\u5B9A\u53EF\u8BFB\u6587\u672C\u3002\u8BE5\u6587\u4EF6\u53EF\u80FD\u7ECF\u8FC7\u538B\u7F29\u3001\u52A0\u5BC6\uFF0C\u6216\u6587\u672C\u7F16\u7801\u65E0\u6CD5\u5728\u6D4F\u89C8\u5668\u7AEF\u53EF\u9760\u8BC6\u522B\uFF1B\u8BF7\u4F7F\u7528\u670D\u52A1\u7AEF LibreOffice/OnlyOffice \u8F6C\u6362\u540E\u9884\u89C8\u3002";
10797
+ empty.textContent = messages.officeLegacyNoText;
10647
10798
  section.append(empty);
10648
10799
  }
10649
10800
  panel.append(section);
@@ -10655,23 +10806,27 @@ function appendOfficeBinaryMeta(list, label, value) {
10655
10806
  detail.textContent = value;
10656
10807
  list.append(term, detail);
10657
10808
  }
10658
- function renderUnsupportedOffice(panel, extension) {
10809
+ function renderUnsupportedOffice(panel, extension, messages) {
10659
10810
  const legacyBinary = /* @__PURE__ */ new Set(["doc", "dot", "wps", "ppt", "pps", "key", "dps"]);
10660
- const message = legacyBinary.has(extension) ? "\u8BE5\u683C\u5F0F\u5C5E\u4E8E\u8001\u4E8C\u8FDB\u5236\u6216\u4E13\u6709\u683C\u5F0F\uFF0C\u6D4F\u89C8\u5668\u5185\u65E0\u6CD5\u53EF\u9760\u89E3\u6790\uFF1B\u5EFA\u8BAE\u63A5\u5165 LibreOffice/OnlyOffice \u670D\u52A1\u7AEF\u8F6C\u6362\u4E3A PDF/HTML \u540E\u9884\u89C8\u3002" : "\u8BE5\u683C\u5F0F\u901A\u5E38\u9700\u8981\u670D\u52A1\u7AEF\u8F6C\u6362\u6216\u4E13\u7528\u89E3\u6790\u5668\u624D\u80FD\u9AD8\u4FDD\u771F\u9884\u89C8\u3002";
10811
+ const message = legacyBinary.has(extension) ? messages.officeUnsupportedLegacyMessage : messages.officeUnsupportedGenericMessage;
10661
10812
  panel.replaceChildren();
10662
- const section = createSection("Office \u57FA\u7840\u9884\u89C8");
10813
+ const section = createSection(messages.officeUnsupportedTitle);
10663
10814
  const format = document.createElement("p");
10664
10815
  const strong = document.createElement("strong");
10665
10816
  strong.textContent = `.${extension}`;
10666
- format.append(strong, document.createTextNode(` \u5DF2\u8FDB\u5165 Office \u63D2\u4EF6\u3002${message}`));
10817
+ format.append(
10818
+ strong,
10819
+ document.createTextNode(" "),
10820
+ document.createTextNode(formatPreviewMessage(messages.officeUnsupportedIntro, { message }))
10821
+ );
10667
10822
  const support = document.createElement("p");
10668
- support.textContent = "\u5F53\u524D\u7248\u672C\u4F18\u5148\u652F\u6301 docx\u3001rtf\u3001odt/fodt\u3001xlsx/xls/csv/ods\u3001pptx/ppsx\u3001odp/fodp \u7684\u57FA\u7840\u5185\u5BB9\u9884\u89C8\u3002";
10823
+ support.textContent = messages.officeUnsupportedSupportedFormats;
10669
10824
  section.append(format, support);
10670
10825
  panel.append(section);
10671
10826
  }
10672
- function normalizeOfficeError(error) {
10827
+ function normalizeOfficeError(error, messages) {
10673
10828
  const message = error instanceof Error ? error.message : String(error || "");
10674
- return message ? `\u89E3\u6790\u5668\u8FD4\u56DE\uFF1A${message}` : "\u89E3\u6790\u5668\u672A\u8FD4\u56DE\u5177\u4F53\u9519\u8BEF\u4FE1\u606F\u3002";
10829
+ return message ? formatPreviewMessage(messages.officeErrorWithMessage, { message }) : messages.officeErrorWithoutMessage;
10675
10830
  }
10676
10831
  function extractLegacyOfficeText(arrayBuffer) {
10677
10832
  const bytes = new Uint8Array(arrayBuffer);
@@ -11913,11 +12068,13 @@ function archivePlugin() {
11913
12068
  sidebarToggle.addEventListener("click", () => {
11914
12069
  setSidebarCollapsed(!layout.classList.contains("is-sidebar-collapsed"));
11915
12070
  });
11916
- const showDefaultSummary = () => {
12071
+ const showDefaultSummary = (isSupplemental = true) => {
11917
12072
  mainPanel.replaceChildren();
11918
12073
  const summary = document.createElement("div");
11919
12074
  summary.className = "ofv-archive-info";
11920
- hideSupplementalInfo4(summary);
12075
+ if (isSupplemental) {
12076
+ hideSupplementalInfo4(summary);
12077
+ }
11921
12078
  const heading = document.createElement("h3");
11922
12079
  heading.textContent = ctx.file.name;
11923
12080
  const info = document.createElement("div");
@@ -11927,12 +12084,16 @@ function archivePlugin() {
11927
12084
  appendArchiveInfo(info, "\u683C\u5F0F\u7C7B\u578B", `.${ext.toUpperCase()} \u538B\u7F29\u6587\u4EF6`);
11928
12085
  appendArchiveInfo(info, "\u5305\u542B\u6587\u4EF6\u6570", `${fileCount} \u4E2A`);
11929
12086
  appendArchiveInfo(info, "\u5305\u542B\u76EE\u5F55\u6570", `${dirCount} \u4E2A`);
11930
- appendArchiveInfo(info, "\u64CD\u4F5C\u63D0\u793A", "\u8BF7\u70B9\u51FB\u5DE6\u4FA7\u680F\u4E2D\u7684\u6587\u4EF6\u8FDB\u884C\u8054\u52A8\u9884\u89C8\u3002");
12087
+ appendArchiveInfo(
12088
+ info,
12089
+ "\u64CD\u4F5C\u63D0\u793A",
12090
+ fileCount === 0 ? "\u538B\u7F29\u5305\u5185\u6CA1\u6709\u53EF\u9884\u89C8\u7684\u6587\u4EF6\u3002" : "\u8BF7\u70B9\u51FB\u5DE6\u4FA7\u680F\u4E2D\u7684\u6587\u4EF6\u8FDB\u884C\u8054\u52A8\u9884\u89C8\u3002"
12091
+ );
11931
12092
  summary.append(heading, info, createArchiveSummary(archiveEntries));
11932
12093
  mainPanel.append(summary);
11933
12094
  };
11934
- showDefaultSummary();
11935
12095
  const visibleEntries = archiveEntries.filter((e) => !e.dir).slice(0, 500);
12096
+ showDefaultSummary(visibleEntries.length > 0);
11936
12097
  let destroyed = false;
11937
12098
  let renderToken = 0;
11938
12099
  const openArchiveEntry = async (entry, item) => {