@open-file-viewer/core 0.1.19 → 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;
@@ -4637,7 +4768,6 @@ async function renderPdfDocumentPreview(options) {
4637
4768
  options.viewport.append(viewer);
4638
4769
  const showDocumentFallback = (error) => {
4639
4770
  viewer.remove();
4640
- options.viewport.classList.add("ofv-center");
4641
4771
  const fileLike = {
4642
4772
  source: options.fileUrl,
4643
4773
  name: options.fileName,
@@ -4651,6 +4781,9 @@ async function renderPdfDocumentPreview(options) {
4651
4781
  message: options.encryptedMessage || "\u8BF7\u4E0B\u8F7D\u540E\u4F7F\u7528\u5BC6\u7801\u6253\u5F00\uFF0C\u6216\u4E0A\u4F20\u89E3\u5BC6\u540E\u7684 PDF \u6587\u4EF6\u3002",
4652
4782
  action: options.encryptedAction || "\u4E0B\u8F7D PDF"
4653
4783
  }) : createPdfFallback(options.fileName, options.fileUrl, normalizePdfError(error), options.fallbackTitle);
4784
+ if (!fallback.classList.contains("ofv-pdf-web-fallback")) {
4785
+ options.viewport.classList.add("ofv-center");
4786
+ }
4654
4787
  options.viewport.append(fallback);
4655
4788
  };
4656
4789
  let documentTask;
@@ -5056,6 +5189,9 @@ function createPageStatus(className, text) {
5056
5189
  return status;
5057
5190
  }
5058
5191
  function createPdfFallback(fileName, url, message, titleText = "PDF \u9884\u89C8\u5931\u8D25") {
5192
+ if (isEmbeddableRemoteUrl(url)) {
5193
+ return createPdfWebFallback(fileName, url);
5194
+ }
5059
5195
  const fallback = document.createElement("div");
5060
5196
  fallback.className = "ofv-fallback";
5061
5197
  const title = document.createElement("strong");
@@ -5069,6 +5205,21 @@ function createPdfFallback(fileName, url, message, titleText = "PDF \u9884\u89C8
5069
5205
  fallback.append(title, meta, download);
5070
5206
  return fallback;
5071
5207
  }
5208
+ function createPdfWebFallback(fileName, url) {
5209
+ const fallback = document.createElement("div");
5210
+ fallback.className = "ofv-pdf-web-fallback";
5211
+ const iframe = document.createElement("iframe");
5212
+ iframe.className = "ofv-pdf-web-fallback-frame";
5213
+ iframe.src = url;
5214
+ iframe.title = `${fileName} HTML preview`;
5215
+ iframe.referrerPolicy = "no-referrer";
5216
+ iframe.setAttribute("sandbox", "allow-forms allow-popups allow-presentation allow-same-origin");
5217
+ fallback.append(iframe);
5218
+ return fallback;
5219
+ }
5220
+ function isEmbeddableRemoteUrl(url) {
5221
+ return /^https?:\/\//i.test(url);
5222
+ }
5072
5223
  function normalizePdfError(error) {
5073
5224
  const message = error instanceof Error ? error.message : String(error || "");
5074
5225
  const name = typeof error === "object" && error !== null && "name" in error ? String(error.name) : "";
@@ -5952,14 +6103,17 @@ function renderLegacyWordDocument(panel, document2) {
5952
6103
  }
5953
6104
  meta.hidden = true;
5954
6105
  page.append(meta);
5955
- appendBlocksToPage(page, pages[0] || [], document2.layout);
6106
+ let nextLineNumber = appendBlocksToPage(page, pages[0] || [], document2.layout, document2.layout.headerBrand === "oasis" ? 2 : 1);
6107
+ if (document2.layout.headerBrand === "oasis") {
6108
+ nextLineNumber -= 1;
6109
+ }
5956
6110
  appendWarnings(page, document2);
5957
6111
  article.append(page);
5958
6112
  for (const pageBlocks of pages.slice(1)) {
5959
6113
  const nextPage = window.document.createElement("section");
5960
6114
  nextPage.className = "ofv-msdoc-page";
5961
6115
  appendPageChrome(nextPage, document2, article.children.length + 1, pageCount);
5962
- appendBlocksToPage(nextPage, pageBlocks, document2.layout);
6116
+ nextLineNumber = appendBlocksToPage(nextPage, pageBlocks, document2.layout, nextLineNumber);
5963
6117
  article.append(nextPage);
5964
6118
  }
5965
6119
  panel.append(article);
@@ -6154,26 +6308,31 @@ function appendPageChrome(page, document2, pageNumber, pageCount) {
6154
6308
  page.classList.add("ofv-msdoc-line-numbered");
6155
6309
  }
6156
6310
  page.setAttribute("aria-label", document2.title || "Word \u6587\u6863");
6157
- if (document2.layout.headerBrand === "oasis") {
6311
+ if (document2.layout.headerBrand === "oasis" && pageNumber === 1) {
6158
6312
  page.append(createOasisHeader(document2.assets.find((asset) => asset.id === document2.layout.headerImageId)));
6159
6313
  }
6160
6314
  if (document2.layout.footer) {
6161
6315
  page.append(createPageFooter(document2.layout.footer, pageNumber, pageCount));
6162
6316
  }
6163
6317
  }
6164
- function appendBlocksToPage(page, blocks, layout) {
6165
- let lineNumber = 1;
6318
+ function appendBlocksToPage(page, blocks, layout, startLineNumber = 1) {
6319
+ let lineNumber = startLineNumber;
6166
6320
  for (const block of blocks) {
6167
6321
  if (block.type === "pageBreak") {
6168
6322
  continue;
6169
6323
  }
6170
6324
  const element = renderWordBlock(block);
6325
+ if (layout.headerBrand === "oasis" && block.type === "heading") {
6326
+ const prefix = getOasisHeadingPrefix(block.text);
6327
+ if (prefix) element.prepend(`${prefix} `);
6328
+ }
6171
6329
  if (layout.lineNumbers && element instanceof HTMLElement && !element.classList.contains("ofv-msdoc-page-header")) {
6172
6330
  element.dataset.line = String(lineNumber);
6173
6331
  lineNumber += estimatedLineCount(block);
6174
6332
  }
6175
6333
  page.append(element);
6176
6334
  }
6335
+ return lineNumber;
6177
6336
  }
6178
6337
  function appendWarnings(page, document2) {
6179
6338
  if (document2.warnings.length === 0) {
@@ -6286,15 +6445,37 @@ function renderWordBlock(block) {
6286
6445
  return table;
6287
6446
  }
6288
6447
  if (block.type === "toc") {
6448
+ if (/^(?:\d+\s+){2,}\d+$/.test(block.title) && /^\d+$/.test(block.page || "")) {
6449
+ const ruler = window.document.createElement("p");
6450
+ ruler.className = "ofv-msdoc-code ofv-msdoc-code-ruler";
6451
+ for (const value of `${block.title} ${block.page}`.split(/\s+/)) {
6452
+ const mark = window.document.createElement("span");
6453
+ mark.textContent = value;
6454
+ ruler.append(mark);
6455
+ }
6456
+ return ruler;
6457
+ }
6289
6458
  const paragraph2 = window.document.createElement("p");
6290
6459
  paragraph2.className = `ofv-msdoc-toc ofv-msdoc-toc-level-${block.level}`;
6291
6460
  const title = window.document.createElement("span");
6292
- title.textContent = block.title;
6461
+ title.className = "ofv-msdoc-toc-title";
6462
+ const numberedTitle = block.title.match(/^(\d+(?:\.\d+)*)\s+(.+)$/);
6463
+ if (numberedTitle) {
6464
+ const number = window.document.createElement("span");
6465
+ number.className = "ofv-msdoc-toc-number";
6466
+ number.textContent = numberedTitle[1];
6467
+ title.append(number, numberedTitle[2]);
6468
+ } else {
6469
+ title.textContent = block.title;
6470
+ }
6293
6471
  paragraph2.append(title);
6294
6472
  if (block.page) {
6473
+ const leader = window.document.createElement("span");
6474
+ leader.className = "ofv-msdoc-toc-leader";
6295
6475
  const page = window.document.createElement("span");
6476
+ page.className = "ofv-msdoc-toc-page";
6296
6477
  page.textContent = block.page;
6297
- paragraph2.append(page);
6478
+ paragraph2.append(leader, page);
6298
6479
  }
6299
6480
  return paragraph2;
6300
6481
  }
@@ -6305,6 +6486,24 @@ function renderWordBlock(block) {
6305
6486
  appendInlineRuns(paragraph, block.text, block.type === "code");
6306
6487
  return paragraph;
6307
6488
  }
6489
+ function getOasisHeadingPrefix(text) {
6490
+ const headings = {
6491
+ Introduction: "1",
6492
+ Terminology: "1.1",
6493
+ "Word Styles": "2",
6494
+ "Overall Style": "2.1",
6495
+ "Title Page": "2.2",
6496
+ Headings: "2.3",
6497
+ Paragraphs: "2.4",
6498
+ Lists: "2.5",
6499
+ Tables: "2.6",
6500
+ "Code Examples": "2.7",
6501
+ "Character Styles": "2.8",
6502
+ References: "3",
6503
+ Normative: "3.1"
6504
+ };
6505
+ return headings[text];
6506
+ }
6308
6507
  function appendInlineRuns(element, text, preserveTabs = false) {
6309
6508
  if (preserveTabs) {
6310
6509
  appendInlineText(element, text, true);
@@ -6976,7 +7175,9 @@ function findValueAfterLabel(paragraphs, label) {
6976
7175
  return paragraphs.slice(index + 1).find((paragraph) => paragraph !== WORD_PAGE_BREAK && paragraph.length > 0);
6977
7176
  }
6978
7177
  function paginateWordBlocks(blocks, layout) {
6979
- const maxLines = layout.lineNumbers ? 33 : 46;
7178
+ if (layout.headerBrand === "oasis") {
7179
+ return paginateOasisBlocks(blocks);
7180
+ }
6980
7181
  const pages = [];
6981
7182
  let current = [];
6982
7183
  let usedLines = 0;
@@ -6989,6 +7190,7 @@ function paginateWordBlocks(blocks, layout) {
6989
7190
  usedLines = 0;
6990
7191
  continue;
6991
7192
  }
7193
+ const maxLines = layout.lineNumbers ? 33 : 46;
6992
7194
  const lines = estimatedLineCount(block);
6993
7195
  const shouldBreak = current.length > 0 && (usedLines + lines > maxLines || block.type === "heading" && usedLines > Math.floor(maxLines * 0.72));
6994
7196
  if (shouldBreak) {
@@ -7004,6 +7206,26 @@ function paginateWordBlocks(blocks, layout) {
7004
7206
  }
7005
7207
  return pages;
7006
7208
  }
7209
+ function paginateOasisBlocks(blocks) {
7210
+ const pages = [];
7211
+ let current = [];
7212
+ const flush = () => {
7213
+ if (current.length > 0) pages.push(current);
7214
+ current = [];
7215
+ };
7216
+ for (const block of blocks) {
7217
+ if (block.type === "pageBreak") {
7218
+ flush();
7219
+ continue;
7220
+ }
7221
+ const startsStyledPage = block.type === "heading" && block.level === 1;
7222
+ const startsCodeExamplesPage = block.type === "paragraph" && /^For bibliography lists,/i.test(block.text);
7223
+ if (current.length > 0 && (startsStyledPage || startsCodeExamplesPage)) flush();
7224
+ current.push(block);
7225
+ }
7226
+ flush();
7227
+ return pages.length > 0 ? pages : [[]];
7228
+ }
7007
7229
  function estimatedLineCount(block) {
7008
7230
  if (block.type === "pageBreak") {
7009
7231
  return 0;
@@ -7317,7 +7539,7 @@ function officePlugin(options = {}) {
7317
7539
  } else if (packageFormat === "docx" && !fileIsDocx(extension)) {
7318
7540
  disposeDocxFit = await renderDocx(panel, arrayBuffer, ctx.options.fit);
7319
7541
  } else if (packageFormat === "xlsx" && !sheetExtensions.has(extension)) {
7320
- await renderSheet(panel, arrayBuffer, "xlsx");
7542
+ await renderSheet(panel, arrayBuffer, "xlsx", ctx.options.messages);
7321
7543
  } else if (packageFormat === "pptx" && !["pptx", "pptm", "ppsx", "ppsm", "potx", "potm"].includes(extension)) {
7322
7544
  await renderPptx(panel, arrayBuffer);
7323
7545
  } else if (fileIsDocx(extension)) {
@@ -7330,9 +7552,9 @@ function officePlugin(options = {}) {
7330
7552
  renderOpenDocumentXml(panel, "FODT \u6587\u6863", await readTextFromBuffer(arrayBuffer));
7331
7553
  } else if (extension === "fods") {
7332
7554
  renderFlatOds(panel, await readTextFromBuffer(arrayBuffer));
7333
- } 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)) {
7334
7556
  } else if (sheetExtensions.has(extension)) {
7335
- await renderSheet(panel, arrayBuffer, extension);
7557
+ await renderSheet(panel, arrayBuffer, extension, ctx.options.messages);
7336
7558
  } else if (["pptx", "pptm", "ppsx", "ppsm", "potx", "potm"].includes(extension)) {
7337
7559
  await renderPptx(panel, arrayBuffer);
7338
7560
  } else if (extension === "odp") {
@@ -7340,11 +7562,11 @@ function officePlugin(options = {}) {
7340
7562
  } else if (extension === "fodp") {
7341
7563
  renderOpenDocumentPresentationXml(panel, await readTextFromBuffer(arrayBuffer));
7342
7564
  } else if (extension === "doc" || extension === "dot") {
7343
- renderLegacyWordBinary(panel, extension, arrayBuffer);
7565
+ renderLegacyWordBinary(panel, extension, arrayBuffer, ctx.options.messages);
7344
7566
  } else if (isLegacyOfficeBinary(extension)) {
7345
- renderLegacyOfficeBinary(panel, extension, arrayBuffer);
7567
+ renderLegacyOfficeBinary(panel, extension, arrayBuffer, ctx.options.messages);
7346
7568
  } else {
7347
- renderUnsupportedOffice(panel, extension || ctx.file.extension || "office");
7569
+ renderUnsupportedOffice(panel, extension || ctx.file.extension || "office", ctx.options.messages);
7348
7570
  }
7349
7571
  const controller = createOfficeZoomController(panel, ctx);
7350
7572
  ctx.toolbar?.refreshCommandSupport();
@@ -8689,7 +8911,7 @@ function renderPlainDocument(panel, title, text) {
8689
8911
  section.append(pre);
8690
8912
  panel.append(section);
8691
8913
  }
8692
- async function renderSheet(panel, arrayBuffer, extension) {
8914
+ async function renderSheet(panel, arrayBuffer, extension, messages) {
8693
8915
  const xlsx = await import("xlsx");
8694
8916
  let workbook;
8695
8917
  try {
@@ -8702,10 +8924,16 @@ async function renderSheet(panel, arrayBuffer, extension) {
8702
8924
  }) : xlsx.read(arrayBuffer, { type: "array", cellDates: true, cellNF: true, cellStyles: true });
8703
8925
  } catch (error) {
8704
8926
  if (isLegacyOfficeBinary(extension)) {
8705
- 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
+ );
8706
8934
  return;
8707
8935
  }
8708
- renderSheetFallback(panel, extension, normalizeOfficeError(error));
8936
+ renderSheetFallback(panel, extension, normalizeOfficeError(error, messages));
8709
8937
  return;
8710
8938
  }
8711
8939
  const chartPreviews = await readWorkbookCharts(arrayBuffer).catch(() => []);
@@ -9966,10 +10194,15 @@ function pptxRenderTimeoutMs() {
9966
10194
  function normalizePptxLayout(container) {
9967
10195
  const slideCanvases = findPptxSlideCanvases(container);
9968
10196
  for (const slide of slideCanvases) {
9969
- slide.style.backgroundColor = "#FFFFFF";
10197
+ if (!hasInlineBackground(slide)) {
10198
+ slide.style.backgroundColor = "#FFFFFF";
10199
+ }
9970
10200
  }
9971
10201
  normalizePptxMirroredText(container);
9972
10202
  }
10203
+ function hasInlineBackground(element) {
10204
+ return Boolean(element.style.background || element.style.backgroundColor || element.style.backgroundImage);
10205
+ }
9973
10206
  function normalizePptxMirroredText(container) {
9974
10207
  const mirroredContainers = Array.from(container.querySelectorAll("div")).filter((element) => {
9975
10208
  const text = element.textContent?.trim();
@@ -10061,7 +10294,7 @@ function renderOpenDocumentPresentationXml(panel, xml) {
10061
10294
  renderPresentationInsight(panel, inspectOpenDocumentPresentation("FODP \u6F14\u793A\u6587\u7A3F", xml, 0));
10062
10295
  renderOpenDocumentPresentation(panel, "FODP \u6F14\u793A\u6587\u7A3F", xml, []);
10063
10296
  }
10064
- async function renderPackagedOfficePreview(panel, arrayBuffer, extension, fit) {
10297
+ async function renderPackagedOfficePreview(panel, arrayBuffer, extension, fit, messages) {
10065
10298
  let zip;
10066
10299
  try {
10067
10300
  zip = await JSZip3.loadAsync(arrayBuffer);
@@ -10076,7 +10309,7 @@ async function renderPackagedOfficePreview(panel, arrayBuffer, extension, fit) {
10076
10309
  return true;
10077
10310
  }
10078
10311
  if (hasEntry("xl/workbook.xml")) {
10079
- await renderSheet(panel, arrayBuffer, extension);
10312
+ await renderSheet(panel, arrayBuffer, extension, messages);
10080
10313
  return true;
10081
10314
  }
10082
10315
  if (hasEntry("ppt/presentation.xml")) {
@@ -10507,41 +10740,50 @@ function legacyOfficeFormatLabel(extension) {
10507
10740
  }
10508
10741
  return "PowerPoint Binary File Format";
10509
10742
  }
10510
- function renderLegacyWordBinary(panel, extension, arrayBuffer) {
10743
+ function renderLegacyWordBinary(panel, extension, arrayBuffer, messages) {
10511
10744
  try {
10512
10745
  renderLegacyWordDocument(panel, parseLegacyWordDocument(arrayBuffer));
10513
10746
  } catch (error) {
10514
- 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
+ );
10515
10754
  }
10516
10755
  }
10517
- function renderLegacyOfficeBinary(panel, extension, arrayBuffer, parseError) {
10756
+ function renderLegacyOfficeBinary(panel, extension, arrayBuffer, messages, parseError) {
10518
10757
  const fragments = extractLegacyOfficeText(arrayBuffer);
10519
10758
  panel.replaceChildren();
10520
- const section = createSection("Office \u8F6C\u6362\u63D0\u793A");
10759
+ const section = createSection(messages.officeLegacyConversionTitle);
10521
10760
  section.classList.add("ofv-office-conversion");
10522
10761
  const format = document.createElement("p");
10523
10762
  const strong = document.createElement("strong");
10524
10763
  strong.textContent = `.${extension}`;
10525
- format.append(
10526
- strong,
10527
- document.createTextNode(
10528
- " \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"
10529
- )
10530
- );
10764
+ format.append(strong, document.createTextNode(" "), document.createTextNode(messages.officeLegacyBinaryNotice));
10531
10765
  const meta = document.createElement("dl");
10532
10766
  meta.className = "ofv-office-binary-meta";
10533
- appendOfficeBinaryMeta(meta, "\u683C\u5F0F\u7C7B\u578B", legacyOfficeFormatLabel(extension));
10534
- 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");
10535
- 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
+ );
10536
10778
  if (parseError) {
10537
- appendOfficeBinaryMeta(meta, "\u89E3\u6790\u72B6\u6001", parseError);
10779
+ appendOfficeBinaryMeta(meta, messages.officeLegacyMetaParseStatus, parseError);
10538
10780
  }
10539
10781
  section.append(format, meta);
10540
10782
  if (fragments.length > 0) {
10541
10783
  const article = document.createElement("article");
10542
10784
  article.className = "ofv-document ofv-office-binary-fragments";
10543
10785
  const heading = document.createElement("h4");
10544
- heading.textContent = "\u53EF\u8BFB\u6587\u672C\u7247\u6BB5";
10786
+ heading.textContent = messages.officeLegacyReadableFragments;
10545
10787
  article.append(heading);
10546
10788
  for (const fragment of fragments.slice(0, 80)) {
10547
10789
  const paragraph = document.createElement("p");
@@ -10552,7 +10794,7 @@ function renderLegacyOfficeBinary(panel, extension, arrayBuffer, parseError) {
10552
10794
  } else {
10553
10795
  const empty = document.createElement("p");
10554
10796
  empty.className = "ofv-office-binary-empty";
10555
- 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;
10556
10798
  section.append(empty);
10557
10799
  }
10558
10800
  panel.append(section);
@@ -10564,23 +10806,27 @@ function appendOfficeBinaryMeta(list, label, value) {
10564
10806
  detail.textContent = value;
10565
10807
  list.append(term, detail);
10566
10808
  }
10567
- function renderUnsupportedOffice(panel, extension) {
10809
+ function renderUnsupportedOffice(panel, extension, messages) {
10568
10810
  const legacyBinary = /* @__PURE__ */ new Set(["doc", "dot", "wps", "ppt", "pps", "key", "dps"]);
10569
- 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;
10570
10812
  panel.replaceChildren();
10571
- const section = createSection("Office \u57FA\u7840\u9884\u89C8");
10813
+ const section = createSection(messages.officeUnsupportedTitle);
10572
10814
  const format = document.createElement("p");
10573
10815
  const strong = document.createElement("strong");
10574
10816
  strong.textContent = `.${extension}`;
10575
- 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
+ );
10576
10822
  const support = document.createElement("p");
10577
- 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;
10578
10824
  section.append(format, support);
10579
10825
  panel.append(section);
10580
10826
  }
10581
- function normalizeOfficeError(error) {
10827
+ function normalizeOfficeError(error, messages) {
10582
10828
  const message = error instanceof Error ? error.message : String(error || "");
10583
- 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;
10584
10830
  }
10585
10831
  function extractLegacyOfficeText(arrayBuffer) {
10586
10832
  const bytes = new Uint8Array(arrayBuffer);
@@ -11822,11 +12068,13 @@ function archivePlugin() {
11822
12068
  sidebarToggle.addEventListener("click", () => {
11823
12069
  setSidebarCollapsed(!layout.classList.contains("is-sidebar-collapsed"));
11824
12070
  });
11825
- const showDefaultSummary = () => {
12071
+ const showDefaultSummary = (isSupplemental = true) => {
11826
12072
  mainPanel.replaceChildren();
11827
12073
  const summary = document.createElement("div");
11828
12074
  summary.className = "ofv-archive-info";
11829
- hideSupplementalInfo4(summary);
12075
+ if (isSupplemental) {
12076
+ hideSupplementalInfo4(summary);
12077
+ }
11830
12078
  const heading = document.createElement("h3");
11831
12079
  heading.textContent = ctx.file.name;
11832
12080
  const info = document.createElement("div");
@@ -11836,12 +12084,16 @@ function archivePlugin() {
11836
12084
  appendArchiveInfo(info, "\u683C\u5F0F\u7C7B\u578B", `.${ext.toUpperCase()} \u538B\u7F29\u6587\u4EF6`);
11837
12085
  appendArchiveInfo(info, "\u5305\u542B\u6587\u4EF6\u6570", `${fileCount} \u4E2A`);
11838
12086
  appendArchiveInfo(info, "\u5305\u542B\u76EE\u5F55\u6570", `${dirCount} \u4E2A`);
11839
- 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
+ );
11840
12092
  summary.append(heading, info, createArchiveSummary(archiveEntries));
11841
12093
  mainPanel.append(summary);
11842
12094
  };
11843
- showDefaultSummary();
11844
12095
  const visibleEntries = archiveEntries.filter((e) => !e.dir).slice(0, 500);
12096
+ showDefaultSummary(visibleEntries.length > 0);
11845
12097
  let destroyed = false;
11846
12098
  let renderToken = 0;
11847
12099
  const openArchiveEntry = async (entry, item) => {