@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.cjs CHANGED
@@ -622,7 +622,40 @@ var defaultMessages = {
622
622
  size: "\u5927\u5C0F",
623
623
  source: "\u6765\u6E90",
624
624
  remoteUrl: "\u8FDC\u7A0B URL",
625
- localFile: "\u672C\u5730/\u5185\u5B58\u6587\u4EF6"
625
+ localFile: "\u672C\u5730/\u5185\u5B58\u6587\u4EF6",
626
+ textPlainLanguage: "\u7EAF\u6587\u672C",
627
+ textLineCount: "{count} \u884C",
628
+ textWrap: "\u6362\u884C",
629
+ textCopy: "\u590D\u5236",
630
+ textCopied: "\u5DF2\u590D\u5236",
631
+ textCopyFailed: "\u590D\u5236\u5931\u8D25",
632
+ textDownload: "\u4E0B\u8F7D",
633
+ textDownloadReady: "\u4E0B\u8F7D\u5DF2\u51C6\u5907",
634
+ 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",
635
+ textHighlightSkipped: "\u5185\u5BB9\u8F83\u5927\uFF0C\u5DF2\u8DF3\u8FC7\u8BED\u6CD5\u9AD8\u4EAE\u4EE5\u4FDD\u6301\u6EDA\u52A8\u6D41\u7545\u3002",
636
+ textPreviewFailedTitle: "\u6587\u672C\u9884\u89C8\u5931\u8D25",
637
+ 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",
638
+ textOpenOriginal: "\u6253\u5F00\u539F\u6587\u4EF6",
639
+ officeLegacyConversionTitle: "Office \u8F6C\u6362\u63D0\u793A",
640
+ 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",
641
+ officeLegacyMetaFormatType: "\u683C\u5F0F\u7C7B\u578B",
642
+ officeLegacyMetaFileStructure: "\u6587\u4EF6\u7ED3\u6784",
643
+ officeLegacyOleDetected: "\u68C0\u6D4B\u5230 OLE Compound File \u7B7E\u540D",
644
+ officeLegacyOleMissing: "\u672A\u68C0\u6D4B\u5230\u6807\u51C6 OLE \u7B7E\u540D\uFF0C\u6309\u539F\u59CB\u4E8C\u8FDB\u5236\u5C1D\u8BD5\u63D0\u53D6",
645
+ officeLegacyMetaTextFragments: "\u6587\u672C\u7247\u6BB5",
646
+ officeLegacyTextFragmentCount: "{count} \u6BB5",
647
+ officeLegacyMetaParseStatus: "\u89E3\u6790\u72B6\u6001",
648
+ officeLegacyReadableFragments: "\u53EF\u8BFB\u6587\u672C\u7247\u6BB5",
649
+ 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",
650
+ officeLegacyWordParseFailed: "Word \u4E8C\u8FDB\u5236\u89E3\u6790\u5931\u8D25\uFF1A{message}",
651
+ officeSheetParseFailed: "\u8868\u683C\u89E3\u6790\u5931\u8D25\uFF1A{message}",
652
+ officeUnsupportedTitle: "Office \u57FA\u7840\u9884\u89C8",
653
+ 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",
654
+ 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",
655
+ officeUnsupportedIntro: "\u5DF2\u8FDB\u5165 Office \u63D2\u4EF6\u3002{message}",
656
+ 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",
657
+ officeErrorWithMessage: "\u89E3\u6790\u5668\u8FD4\u56DE\uFF1A{message}",
658
+ officeErrorWithoutMessage: "\u89E3\u6790\u5668\u672A\u8FD4\u56DE\u5177\u4F53\u9519\u8BEF\u4FE1\u606F\u3002"
626
659
  },
627
660
  "en-US": {
628
661
  loading: "Loading preview...",
@@ -638,7 +671,40 @@ var defaultMessages = {
638
671
  size: "Size",
639
672
  source: "Source",
640
673
  remoteUrl: "Remote URL",
641
- localFile: "Local or in-memory file"
674
+ localFile: "Local or in-memory file",
675
+ textPlainLanguage: "plain text",
676
+ textLineCount: "{count} lines",
677
+ textWrap: "Wrap",
678
+ textCopy: "Copy",
679
+ textCopied: "Copied",
680
+ textCopyFailed: "Copy failed",
681
+ textDownload: "Download",
682
+ textDownloadReady: "Download ready",
683
+ textLargeFileNotice: "Large file, showing the first {size}. Copy and download still use the full content.",
684
+ textHighlightSkipped: "Large content, syntax highlighting was skipped to keep scrolling smooth.",
685
+ textPreviewFailedTitle: "Text preview failed",
686
+ textPreviewFailedMessage: "Unable to read this text content. The remote file may be unreachable or returned an invalid response.",
687
+ textOpenOriginal: "Open original file",
688
+ officeLegacyConversionTitle: "Office conversion guidance",
689
+ 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.",
690
+ officeLegacyMetaFormatType: "Format type",
691
+ officeLegacyMetaFileStructure: "File structure",
692
+ officeLegacyOleDetected: "OLE Compound File signature detected",
693
+ officeLegacyOleMissing: "No standard OLE signature detected; extracting from raw binary data",
694
+ officeLegacyMetaTextFragments: "Text fragments",
695
+ officeLegacyTextFragmentCount: "{count} fragments",
696
+ officeLegacyMetaParseStatus: "Parse status",
697
+ officeLegacyReadableFragments: "Readable text fragments",
698
+ 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.",
699
+ officeLegacyWordParseFailed: "Word binary parse failed: {message}",
700
+ officeSheetParseFailed: "Spreadsheet parse failed: {message}",
701
+ officeUnsupportedTitle: "Office basic preview",
702
+ 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.",
703
+ officeUnsupportedGenericMessage: "This format usually needs server-side conversion or a dedicated parser for high-fidelity preview.",
704
+ officeUnsupportedIntro: "is handled by the Office plugin. {message}",
705
+ officeUnsupportedSupportedFormats: "This version prioritizes basic previews for docx, rtf, odt/fodt, xlsx/xls/csv/ods, pptx/ppsx, and odp/fodp files.",
706
+ officeErrorWithMessage: "Parser returned: {message}",
707
+ officeErrorWithoutMessage: "Parser did not return a specific error."
642
708
  }
643
709
  };
644
710
  function resolveMessages(options) {
@@ -647,6 +713,9 @@ function resolveMessages(options) {
647
713
  ...options.messages
648
714
  };
649
715
  }
716
+ function formatPreviewMessage(template, values) {
717
+ return template.replace(/\{(\w+)\}/g, (match, key) => String(values[key] ?? match));
718
+ }
650
719
 
651
720
  // src/plugins/fallback.ts
652
721
  function fallbackPlugin() {
@@ -717,9 +786,10 @@ function formatFallbackBytes(bytes) {
717
786
  function createViewer(options) {
718
787
  const container = resolveContainer(options.container);
719
788
  applyBoxSize(container, options.width, options.height);
789
+ const customClassNames = parseClassNameTokens(options.className);
720
790
  container.classList.add("ofv-root");
721
- if (options.className) {
722
- container.classList.add(options.className);
791
+ if (customClassNames.length > 0) {
792
+ container.classList.add(...customClassNames);
723
793
  }
724
794
  const theme = applyTheme(container, options.theme || "light");
725
795
  const host = document.createElement("div");
@@ -863,12 +933,15 @@ function createViewer(options) {
863
933
  theme.destroy();
864
934
  container.replaceChildren();
865
935
  container.classList.remove("ofv-root");
866
- if (options.className) {
867
- container.classList.remove(options.className);
936
+ if (customClassNames.length > 0) {
937
+ container.classList.remove(...customClassNames);
868
938
  }
869
939
  }
870
940
  };
871
941
  }
942
+ function parseClassNameTokens(className) {
943
+ return className?.trim().split(/\s+/).filter(Boolean) ?? [];
944
+ }
872
945
  function destroyPreviewInstance(instance) {
873
946
  if (!instance) {
874
947
  return;
@@ -994,6 +1067,7 @@ function createToolbar(toolbar, viewport, queue) {
994
1067
  let previousButton;
995
1068
  let nextButton;
996
1069
  let zoomResetButton;
1070
+ let fullscreenButton;
997
1071
  let currentZoom;
998
1072
  const commandButtons = [];
999
1073
  const customButtons = [];
@@ -1099,13 +1173,14 @@ function createToolbar(toolbar, viewport, queue) {
1099
1173
  return;
1100
1174
  }
1101
1175
  if (id === "fullscreen" && options.fullscreen !== false) {
1102
- addButton(
1176
+ fullscreenButton = addButton(
1103
1177
  getToolbarLabel(options, id),
1104
1178
  getToolbarTitle(options, id),
1105
1179
  () => getContext().fullscreen(),
1106
1180
  void 0,
1107
1181
  options.icons?.fullscreen
1108
1182
  );
1183
+ updateFullscreenButton();
1109
1184
  return;
1110
1185
  }
1111
1186
  if (id === "print" && options.print) {
@@ -1207,6 +1282,32 @@ function createToolbar(toolbar, viewport, queue) {
1207
1282
  updateCustomButtons();
1208
1283
  refreshCustomRender();
1209
1284
  }
1285
+ function isFullscreenActive() {
1286
+ return Boolean(
1287
+ typeof document !== "undefined" && element.parentElement && document.fullscreenElement === element.parentElement
1288
+ );
1289
+ }
1290
+ function updateFullscreenButton() {
1291
+ if (!fullscreenButton) {
1292
+ return;
1293
+ }
1294
+ const active = isFullscreenActive();
1295
+ const id = active ? "exit-fullscreen" : "fullscreen";
1296
+ const icon = active ? options.icons?.["exit-fullscreen"] ?? options.icons?.fullscreen : options.icons?.fullscreen;
1297
+ setToolbarButtonContent(fullscreenButton, getToolbarLabel(options, id), icon);
1298
+ const title = getToolbarTitle(options, id);
1299
+ fullscreenButton.title = title;
1300
+ fullscreenButton.setAttribute("aria-label", title);
1301
+ fullscreenButton.setAttribute("aria-pressed", String(active));
1302
+ }
1303
+ const handleFullscreenChange = () => {
1304
+ updateFullscreenButton();
1305
+ refreshCustomRender();
1306
+ };
1307
+ if (typeof document !== "undefined") {
1308
+ document.addEventListener("fullscreenchange", handleFullscreenChange);
1309
+ disposers.push(() => document.removeEventListener("fullscreenchange", handleFullscreenChange));
1310
+ }
1210
1311
  const refreshCustomRender = () => {
1211
1312
  if (!options.render) {
1212
1313
  return;
@@ -1280,6 +1381,9 @@ function createToolbarContext({
1280
1381
  viewport,
1281
1382
  canPrevious: index > 0,
1282
1383
  canNext: index < length - 1,
1384
+ isFullscreen: Boolean(
1385
+ typeof document !== "undefined" && element.parentElement && document.fullscreenElement === element.parentElement
1386
+ ),
1283
1387
  zoom,
1284
1388
  zoomLabel: zoom === void 0 ? void 0 : formatToolbarZoom(zoom),
1285
1389
  async previous() {
@@ -1298,7 +1402,15 @@ function createToolbarContext({
1298
1402
  }
1299
1403
  },
1300
1404
  fullscreen() {
1301
- void element.parentElement?.requestFullscreen?.();
1405
+ const target = element.parentElement;
1406
+ if (!target) {
1407
+ return;
1408
+ }
1409
+ if (typeof document !== "undefined" && document.fullscreenElement === target) {
1410
+ void document.exitFullscreen?.();
1411
+ } else {
1412
+ void target.requestFullscreen?.();
1413
+ }
1302
1414
  },
1303
1415
  print() {
1304
1416
  printPreview(viewport);
@@ -1317,6 +1429,7 @@ var defaultToolbarLabels = {
1317
1429
  "rotate-right": "Rotate",
1318
1430
  download: "Download",
1319
1431
  fullscreen: "Fullscreen",
1432
+ "exit-fullscreen": "Exit fullscreen",
1320
1433
  print: "Print",
1321
1434
  search: "Search"
1322
1435
  };
@@ -1330,6 +1443,7 @@ var defaultToolbarTitles = {
1330
1443
  "rotate-right": "Rotate right",
1331
1444
  download: "Download file",
1332
1445
  fullscreen: "Open preview fullscreen",
1446
+ "exit-fullscreen": "Exit fullscreen",
1333
1447
  print: "Print preview",
1334
1448
  search: "Search preview text"
1335
1449
  };
@@ -1568,6 +1682,9 @@ function collectSearchableTextNodes(root) {
1568
1682
  if (["SCRIPT", "STYLE", "TEXTAREA", "INPUT", "BUTTON"].includes(parent.tagName)) {
1569
1683
  return NodeFilter.FILTER_REJECT;
1570
1684
  }
1685
+ if (hasHiddenSearchAncestor(parent, root)) {
1686
+ return NodeFilter.FILTER_REJECT;
1687
+ }
1571
1688
  return NodeFilter.FILTER_ACCEPT;
1572
1689
  }
1573
1690
  });
@@ -1578,6 +1695,19 @@ function collectSearchableTextNodes(root) {
1578
1695
  }
1579
1696
  return nodes;
1580
1697
  }
1698
+ function hasHiddenSearchAncestor(element, root) {
1699
+ let current = element;
1700
+ while (current) {
1701
+ if (current.hidden || current.getAttribute("aria-hidden") === "true" || current.style.display === "none" || current.style.visibility === "hidden") {
1702
+ return true;
1703
+ }
1704
+ if (current === root) {
1705
+ break;
1706
+ }
1707
+ current = current.parentElement;
1708
+ }
1709
+ return false;
1710
+ }
1581
1711
  function printPreview(viewport) {
1582
1712
  const frame = document.createElement("iframe");
1583
1713
  frame.className = "ofv-print-frame";
@@ -4075,7 +4205,7 @@ function textPlugin() {
4075
4205
  const isMarkdown = lang === "markdown";
4076
4206
  const text = await readText(ctx.file.source).catch((error) => void 0);
4077
4207
  if (text === void 0) {
4078
- const fallback = createTextFallback(ctx.file.name, ctx.file.url);
4208
+ const fallback = createTextFallback(ctx.file.name, ctx.options.messages, ctx.file.url);
4079
4209
  ctx.viewport.classList.add("ofv-center");
4080
4210
  ctx.viewport.append(fallback);
4081
4211
  return {
@@ -4247,10 +4377,11 @@ function textPlugin() {
4247
4377
  title.className = "ofv-code-title";
4248
4378
  const fileName = document.createElement("strong");
4249
4379
  fileName.textContent = ctx.file.name;
4380
+ const messages = ctx.options.messages;
4250
4381
  const meta = document.createElement("span");
4251
4382
  meta.textContent = [
4252
- lang === "none" ? "plain text" : lang,
4253
- `${totalLines.toLocaleString()} lines`,
4383
+ lang === "none" ? messages.textPlainLanguage : lang,
4384
+ formatPreviewMessage(messages.textLineCount, { count: totalLines.toLocaleString() }),
4254
4385
  formatBytes(ctx.file.size ?? (ctx.file.source instanceof Blob ? ctx.file.source.size : text.length))
4255
4386
  ].join(" \xB7 ");
4256
4387
  title.append(fileName, meta);
@@ -4262,7 +4393,7 @@ function textPlugin() {
4262
4393
  const wrapButton = document.createElement("button");
4263
4394
  wrapButton.type = "button";
4264
4395
  wrapButton.className = "ofv-code-action";
4265
- wrapButton.textContent = "Wrap";
4396
+ wrapButton.textContent = messages.textWrap;
4266
4397
  wrapButton.setAttribute("aria-pressed", String(defaultWrapped));
4267
4398
  wrapButton.addEventListener("click", () => {
4268
4399
  const wrapped = wrapper.classList.toggle("is-wrapped");
@@ -4271,14 +4402,14 @@ function textPlugin() {
4271
4402
  const copyButton = document.createElement("button");
4272
4403
  copyButton.type = "button";
4273
4404
  copyButton.className = "ofv-code-action";
4274
- copyButton.textContent = "Copy";
4405
+ copyButton.textContent = messages.textCopy;
4275
4406
  copyButton.addEventListener("click", async () => {
4276
4407
  copyButton.disabled = true;
4277
4408
  try {
4278
4409
  await copyToClipboard(text);
4279
- status.textContent = "Copied";
4410
+ status.textContent = messages.textCopied;
4280
4411
  } catch {
4281
- status.textContent = "Copy failed";
4412
+ status.textContent = messages.textCopyFailed;
4282
4413
  } finally {
4283
4414
  copyButton.disabled = false;
4284
4415
  }
@@ -4286,10 +4417,10 @@ function textPlugin() {
4286
4417
  const downloadButton = document.createElement("button");
4287
4418
  downloadButton.type = "button";
4288
4419
  downloadButton.className = "ofv-code-action";
4289
- downloadButton.textContent = "Download";
4420
+ downloadButton.textContent = messages.textDownload;
4290
4421
  downloadButton.addEventListener("click", () => {
4291
4422
  downloadText(ctx.file.name, text);
4292
- status.textContent = "Download ready";
4423
+ status.textContent = messages.textDownloadReady;
4293
4424
  });
4294
4425
  actions.append(wrapButton, copyButton, downloadButton, status);
4295
4426
  header.append(title, actions);
@@ -4314,13 +4445,13 @@ function textPlugin() {
4314
4445
  if (truncated) {
4315
4446
  const notice = document.createElement("div");
4316
4447
  notice.className = "ofv-code-notice";
4317
- 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`;
4448
+ notice.textContent = formatPreviewMessage(messages.textLargeFileNotice, { size: formatBytes(codeText.length) });
4318
4449
  wrapper.append(notice);
4319
4450
  }
4320
4451
  if (!shouldHighlight) {
4321
4452
  const notice = document.createElement("div");
4322
4453
  notice.className = "ofv-code-notice";
4323
- notice.textContent = "\u5185\u5BB9\u8F83\u5927\uFF0C\u5DF2\u8DF3\u8FC7\u8BED\u6CD5\u9AD8\u4EAE\u4EE5\u4FDD\u6301\u6EDA\u52A8\u6D41\u7545\u3002";
4454
+ notice.textContent = messages.textHighlightSkipped;
4324
4455
  wrapper.append(notice);
4325
4456
  }
4326
4457
  wrapper.appendChild(body);
@@ -4387,19 +4518,19 @@ function normalizeFileName2(name) {
4387
4518
  const baseName = name.split(/[\\/]/).pop() || name;
4388
4519
  return baseName.toLowerCase();
4389
4520
  }
4390
- function createTextFallback(fileName, url) {
4521
+ function createTextFallback(fileName, messages, url) {
4391
4522
  const fallback = document.createElement("div");
4392
4523
  fallback.className = "ofv-fallback";
4393
4524
  const title = document.createElement("strong");
4394
- title.textContent = "\u6587\u672C\u9884\u89C8\u5931\u8D25";
4525
+ title.textContent = messages.textPreviewFailedTitle;
4395
4526
  const meta = document.createElement("span");
4396
- 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";
4527
+ meta.textContent = messages.textPreviewFailedMessage;
4397
4528
  fallback.append(title, meta);
4398
4529
  if (url) {
4399
4530
  const download = document.createElement("a");
4400
4531
  download.href = url;
4401
4532
  download.download = fileName;
4402
- download.textContent = "\u6253\u5F00\u539F\u6587\u4EF6";
4533
+ download.textContent = messages.textOpenOriginal;
4403
4534
  fallback.append(download);
4404
4535
  }
4405
4536
  return fallback;
@@ -4690,7 +4821,6 @@ async function renderPdfDocumentPreview(options) {
4690
4821
  options.viewport.append(viewer);
4691
4822
  const showDocumentFallback = (error) => {
4692
4823
  viewer.remove();
4693
- options.viewport.classList.add("ofv-center");
4694
4824
  const fileLike = {
4695
4825
  source: options.fileUrl,
4696
4826
  name: options.fileName,
@@ -4704,6 +4834,9 @@ async function renderPdfDocumentPreview(options) {
4704
4834
  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",
4705
4835
  action: options.encryptedAction || "\u4E0B\u8F7D PDF"
4706
4836
  }) : createPdfFallback(options.fileName, options.fileUrl, normalizePdfError(error), options.fallbackTitle);
4837
+ if (!fallback.classList.contains("ofv-pdf-web-fallback")) {
4838
+ options.viewport.classList.add("ofv-center");
4839
+ }
4707
4840
  options.viewport.append(fallback);
4708
4841
  };
4709
4842
  let documentTask;
@@ -5109,6 +5242,9 @@ function createPageStatus(className, text) {
5109
5242
  return status;
5110
5243
  }
5111
5244
  function createPdfFallback(fileName, url, message, titleText = "PDF \u9884\u89C8\u5931\u8D25") {
5245
+ if (isEmbeddableRemoteUrl(url)) {
5246
+ return createPdfWebFallback(fileName, url);
5247
+ }
5112
5248
  const fallback = document.createElement("div");
5113
5249
  fallback.className = "ofv-fallback";
5114
5250
  const title = document.createElement("strong");
@@ -5122,6 +5258,21 @@ function createPdfFallback(fileName, url, message, titleText = "PDF \u9884\u89C8
5122
5258
  fallback.append(title, meta, download);
5123
5259
  return fallback;
5124
5260
  }
5261
+ function createPdfWebFallback(fileName, url) {
5262
+ const fallback = document.createElement("div");
5263
+ fallback.className = "ofv-pdf-web-fallback";
5264
+ const iframe = document.createElement("iframe");
5265
+ iframe.className = "ofv-pdf-web-fallback-frame";
5266
+ iframe.src = url;
5267
+ iframe.title = `${fileName} HTML preview`;
5268
+ iframe.referrerPolicy = "no-referrer";
5269
+ iframe.setAttribute("sandbox", "allow-forms allow-popups allow-presentation allow-same-origin");
5270
+ fallback.append(iframe);
5271
+ return fallback;
5272
+ }
5273
+ function isEmbeddableRemoteUrl(url) {
5274
+ return /^https?:\/\//i.test(url);
5275
+ }
5125
5276
  function normalizePdfError(error) {
5126
5277
  const message = error instanceof Error ? error.message : String(error || "");
5127
5278
  const name = typeof error === "object" && error !== null && "name" in error ? String(error.name) : "";
@@ -6005,14 +6156,17 @@ function renderLegacyWordDocument(panel, document2) {
6005
6156
  }
6006
6157
  meta.hidden = true;
6007
6158
  page.append(meta);
6008
- appendBlocksToPage(page, pages[0] || [], document2.layout);
6159
+ let nextLineNumber = appendBlocksToPage(page, pages[0] || [], document2.layout, document2.layout.headerBrand === "oasis" ? 2 : 1);
6160
+ if (document2.layout.headerBrand === "oasis") {
6161
+ nextLineNumber -= 1;
6162
+ }
6009
6163
  appendWarnings(page, document2);
6010
6164
  article.append(page);
6011
6165
  for (const pageBlocks of pages.slice(1)) {
6012
6166
  const nextPage = window.document.createElement("section");
6013
6167
  nextPage.className = "ofv-msdoc-page";
6014
6168
  appendPageChrome(nextPage, document2, article.children.length + 1, pageCount);
6015
- appendBlocksToPage(nextPage, pageBlocks, document2.layout);
6169
+ nextLineNumber = appendBlocksToPage(nextPage, pageBlocks, document2.layout, nextLineNumber);
6016
6170
  article.append(nextPage);
6017
6171
  }
6018
6172
  panel.append(article);
@@ -6207,26 +6361,31 @@ function appendPageChrome(page, document2, pageNumber, pageCount) {
6207
6361
  page.classList.add("ofv-msdoc-line-numbered");
6208
6362
  }
6209
6363
  page.setAttribute("aria-label", document2.title || "Word \u6587\u6863");
6210
- if (document2.layout.headerBrand === "oasis") {
6364
+ if (document2.layout.headerBrand === "oasis" && pageNumber === 1) {
6211
6365
  page.append(createOasisHeader(document2.assets.find((asset) => asset.id === document2.layout.headerImageId)));
6212
6366
  }
6213
6367
  if (document2.layout.footer) {
6214
6368
  page.append(createPageFooter(document2.layout.footer, pageNumber, pageCount));
6215
6369
  }
6216
6370
  }
6217
- function appendBlocksToPage(page, blocks, layout) {
6218
- let lineNumber = 1;
6371
+ function appendBlocksToPage(page, blocks, layout, startLineNumber = 1) {
6372
+ let lineNumber = startLineNumber;
6219
6373
  for (const block of blocks) {
6220
6374
  if (block.type === "pageBreak") {
6221
6375
  continue;
6222
6376
  }
6223
6377
  const element = renderWordBlock(block);
6378
+ if (layout.headerBrand === "oasis" && block.type === "heading") {
6379
+ const prefix = getOasisHeadingPrefix(block.text);
6380
+ if (prefix) element.prepend(`${prefix} `);
6381
+ }
6224
6382
  if (layout.lineNumbers && element instanceof HTMLElement && !element.classList.contains("ofv-msdoc-page-header")) {
6225
6383
  element.dataset.line = String(lineNumber);
6226
6384
  lineNumber += estimatedLineCount(block);
6227
6385
  }
6228
6386
  page.append(element);
6229
6387
  }
6388
+ return lineNumber;
6230
6389
  }
6231
6390
  function appendWarnings(page, document2) {
6232
6391
  if (document2.warnings.length === 0) {
@@ -6339,15 +6498,37 @@ function renderWordBlock(block) {
6339
6498
  return table;
6340
6499
  }
6341
6500
  if (block.type === "toc") {
6501
+ if (/^(?:\d+\s+){2,}\d+$/.test(block.title) && /^\d+$/.test(block.page || "")) {
6502
+ const ruler = window.document.createElement("p");
6503
+ ruler.className = "ofv-msdoc-code ofv-msdoc-code-ruler";
6504
+ for (const value of `${block.title} ${block.page}`.split(/\s+/)) {
6505
+ const mark = window.document.createElement("span");
6506
+ mark.textContent = value;
6507
+ ruler.append(mark);
6508
+ }
6509
+ return ruler;
6510
+ }
6342
6511
  const paragraph2 = window.document.createElement("p");
6343
6512
  paragraph2.className = `ofv-msdoc-toc ofv-msdoc-toc-level-${block.level}`;
6344
6513
  const title = window.document.createElement("span");
6345
- title.textContent = block.title;
6514
+ title.className = "ofv-msdoc-toc-title";
6515
+ const numberedTitle = block.title.match(/^(\d+(?:\.\d+)*)\s+(.+)$/);
6516
+ if (numberedTitle) {
6517
+ const number = window.document.createElement("span");
6518
+ number.className = "ofv-msdoc-toc-number";
6519
+ number.textContent = numberedTitle[1];
6520
+ title.append(number, numberedTitle[2]);
6521
+ } else {
6522
+ title.textContent = block.title;
6523
+ }
6346
6524
  paragraph2.append(title);
6347
6525
  if (block.page) {
6526
+ const leader = window.document.createElement("span");
6527
+ leader.className = "ofv-msdoc-toc-leader";
6348
6528
  const page = window.document.createElement("span");
6529
+ page.className = "ofv-msdoc-toc-page";
6349
6530
  page.textContent = block.page;
6350
- paragraph2.append(page);
6531
+ paragraph2.append(leader, page);
6351
6532
  }
6352
6533
  return paragraph2;
6353
6534
  }
@@ -6358,6 +6539,24 @@ function renderWordBlock(block) {
6358
6539
  appendInlineRuns(paragraph, block.text, block.type === "code");
6359
6540
  return paragraph;
6360
6541
  }
6542
+ function getOasisHeadingPrefix(text) {
6543
+ const headings = {
6544
+ Introduction: "1",
6545
+ Terminology: "1.1",
6546
+ "Word Styles": "2",
6547
+ "Overall Style": "2.1",
6548
+ "Title Page": "2.2",
6549
+ Headings: "2.3",
6550
+ Paragraphs: "2.4",
6551
+ Lists: "2.5",
6552
+ Tables: "2.6",
6553
+ "Code Examples": "2.7",
6554
+ "Character Styles": "2.8",
6555
+ References: "3",
6556
+ Normative: "3.1"
6557
+ };
6558
+ return headings[text];
6559
+ }
6361
6560
  function appendInlineRuns(element, text, preserveTabs = false) {
6362
6561
  if (preserveTabs) {
6363
6562
  appendInlineText(element, text, true);
@@ -7029,7 +7228,9 @@ function findValueAfterLabel(paragraphs, label) {
7029
7228
  return paragraphs.slice(index + 1).find((paragraph) => paragraph !== WORD_PAGE_BREAK && paragraph.length > 0);
7030
7229
  }
7031
7230
  function paginateWordBlocks(blocks, layout) {
7032
- const maxLines = layout.lineNumbers ? 33 : 46;
7231
+ if (layout.headerBrand === "oasis") {
7232
+ return paginateOasisBlocks(blocks);
7233
+ }
7033
7234
  const pages = [];
7034
7235
  let current = [];
7035
7236
  let usedLines = 0;
@@ -7042,6 +7243,7 @@ function paginateWordBlocks(blocks, layout) {
7042
7243
  usedLines = 0;
7043
7244
  continue;
7044
7245
  }
7246
+ const maxLines = layout.lineNumbers ? 33 : 46;
7045
7247
  const lines = estimatedLineCount(block);
7046
7248
  const shouldBreak = current.length > 0 && (usedLines + lines > maxLines || block.type === "heading" && usedLines > Math.floor(maxLines * 0.72));
7047
7249
  if (shouldBreak) {
@@ -7057,6 +7259,26 @@ function paginateWordBlocks(blocks, layout) {
7057
7259
  }
7058
7260
  return pages;
7059
7261
  }
7262
+ function paginateOasisBlocks(blocks) {
7263
+ const pages = [];
7264
+ let current = [];
7265
+ const flush = () => {
7266
+ if (current.length > 0) pages.push(current);
7267
+ current = [];
7268
+ };
7269
+ for (const block of blocks) {
7270
+ if (block.type === "pageBreak") {
7271
+ flush();
7272
+ continue;
7273
+ }
7274
+ const startsStyledPage = block.type === "heading" && block.level === 1;
7275
+ const startsCodeExamplesPage = block.type === "paragraph" && /^For bibliography lists,/i.test(block.text);
7276
+ if (current.length > 0 && (startsStyledPage || startsCodeExamplesPage)) flush();
7277
+ current.push(block);
7278
+ }
7279
+ flush();
7280
+ return pages.length > 0 ? pages : [[]];
7281
+ }
7060
7282
  function estimatedLineCount(block) {
7061
7283
  if (block.type === "pageBreak") {
7062
7284
  return 0;
@@ -7370,7 +7592,7 @@ function officePlugin(options = {}) {
7370
7592
  } else if (packageFormat === "docx" && !fileIsDocx(extension)) {
7371
7593
  disposeDocxFit = await renderDocx(panel, arrayBuffer, ctx.options.fit);
7372
7594
  } else if (packageFormat === "xlsx" && !sheetExtensions.has(extension)) {
7373
- await renderSheet(panel, arrayBuffer, "xlsx");
7595
+ await renderSheet(panel, arrayBuffer, "xlsx", ctx.options.messages);
7374
7596
  } else if (packageFormat === "pptx" && !["pptx", "pptm", "ppsx", "ppsm", "potx", "potm"].includes(extension)) {
7375
7597
  await renderPptx(panel, arrayBuffer);
7376
7598
  } else if (fileIsDocx(extension)) {
@@ -7383,9 +7605,9 @@ function officePlugin(options = {}) {
7383
7605
  renderOpenDocumentXml(panel, "FODT \u6587\u6863", await readTextFromBuffer(arrayBuffer));
7384
7606
  } else if (extension === "fods") {
7385
7607
  renderFlatOds(panel, await readTextFromBuffer(arrayBuffer));
7386
- } else if (packagedOfficeCandidates.has(extension) && await renderPackagedOfficePreview(panel, arrayBuffer, extension, ctx.options.fit)) {
7608
+ } else if (packagedOfficeCandidates.has(extension) && await renderPackagedOfficePreview(panel, arrayBuffer, extension, ctx.options.fit, ctx.options.messages)) {
7387
7609
  } else if (sheetExtensions.has(extension)) {
7388
- await renderSheet(panel, arrayBuffer, extension);
7610
+ await renderSheet(panel, arrayBuffer, extension, ctx.options.messages);
7389
7611
  } else if (["pptx", "pptm", "ppsx", "ppsm", "potx", "potm"].includes(extension)) {
7390
7612
  await renderPptx(panel, arrayBuffer);
7391
7613
  } else if (extension === "odp") {
@@ -7393,11 +7615,11 @@ function officePlugin(options = {}) {
7393
7615
  } else if (extension === "fodp") {
7394
7616
  renderOpenDocumentPresentationXml(panel, await readTextFromBuffer(arrayBuffer));
7395
7617
  } else if (extension === "doc" || extension === "dot") {
7396
- renderLegacyWordBinary(panel, extension, arrayBuffer);
7618
+ renderLegacyWordBinary(panel, extension, arrayBuffer, ctx.options.messages);
7397
7619
  } else if (isLegacyOfficeBinary(extension)) {
7398
- renderLegacyOfficeBinary(panel, extension, arrayBuffer);
7620
+ renderLegacyOfficeBinary(panel, extension, arrayBuffer, ctx.options.messages);
7399
7621
  } else {
7400
- renderUnsupportedOffice(panel, extension || ctx.file.extension || "office");
7622
+ renderUnsupportedOffice(panel, extension || ctx.file.extension || "office", ctx.options.messages);
7401
7623
  }
7402
7624
  const controller = createOfficeZoomController(panel, ctx);
7403
7625
  ctx.toolbar?.refreshCommandSupport();
@@ -8742,7 +8964,7 @@ function renderPlainDocument(panel, title, text) {
8742
8964
  section.append(pre);
8743
8965
  panel.append(section);
8744
8966
  }
8745
- async function renderSheet(panel, arrayBuffer, extension) {
8967
+ async function renderSheet(panel, arrayBuffer, extension, messages) {
8746
8968
  const xlsx = await import("xlsx");
8747
8969
  let workbook;
8748
8970
  try {
@@ -8755,10 +8977,16 @@ async function renderSheet(panel, arrayBuffer, extension) {
8755
8977
  }) : xlsx.read(arrayBuffer, { type: "array", cellDates: true, cellNF: true, cellStyles: true });
8756
8978
  } catch (error) {
8757
8979
  if (isLegacyOfficeBinary(extension)) {
8758
- renderLegacyOfficeBinary(panel, extension, arrayBuffer, `\u8868\u683C\u89E3\u6790\u5931\u8D25\uFF1A${normalizeOfficeError(error)}`);
8980
+ renderLegacyOfficeBinary(
8981
+ panel,
8982
+ extension,
8983
+ arrayBuffer,
8984
+ messages,
8985
+ formatPreviewMessage(messages.officeSheetParseFailed, { message: normalizeOfficeError(error, messages) })
8986
+ );
8759
8987
  return;
8760
8988
  }
8761
- renderSheetFallback(panel, extension, normalizeOfficeError(error));
8989
+ renderSheetFallback(panel, extension, normalizeOfficeError(error, messages));
8762
8990
  return;
8763
8991
  }
8764
8992
  const chartPreviews = await readWorkbookCharts(arrayBuffer).catch(() => []);
@@ -10019,10 +10247,15 @@ function pptxRenderTimeoutMs() {
10019
10247
  function normalizePptxLayout(container) {
10020
10248
  const slideCanvases = findPptxSlideCanvases(container);
10021
10249
  for (const slide of slideCanvases) {
10022
- slide.style.backgroundColor = "#FFFFFF";
10250
+ if (!hasInlineBackground(slide)) {
10251
+ slide.style.backgroundColor = "#FFFFFF";
10252
+ }
10023
10253
  }
10024
10254
  normalizePptxMirroredText(container);
10025
10255
  }
10256
+ function hasInlineBackground(element) {
10257
+ return Boolean(element.style.background || element.style.backgroundColor || element.style.backgroundImage);
10258
+ }
10026
10259
  function normalizePptxMirroredText(container) {
10027
10260
  const mirroredContainers = Array.from(container.querySelectorAll("div")).filter((element) => {
10028
10261
  const text = element.textContent?.trim();
@@ -10114,7 +10347,7 @@ function renderOpenDocumentPresentationXml(panel, xml) {
10114
10347
  renderPresentationInsight(panel, inspectOpenDocumentPresentation("FODP \u6F14\u793A\u6587\u7A3F", xml, 0));
10115
10348
  renderOpenDocumentPresentation(panel, "FODP \u6F14\u793A\u6587\u7A3F", xml, []);
10116
10349
  }
10117
- async function renderPackagedOfficePreview(panel, arrayBuffer, extension, fit) {
10350
+ async function renderPackagedOfficePreview(panel, arrayBuffer, extension, fit, messages) {
10118
10351
  let zip;
10119
10352
  try {
10120
10353
  zip = await import_jszip3.default.loadAsync(arrayBuffer);
@@ -10129,7 +10362,7 @@ async function renderPackagedOfficePreview(panel, arrayBuffer, extension, fit) {
10129
10362
  return true;
10130
10363
  }
10131
10364
  if (hasEntry("xl/workbook.xml")) {
10132
- await renderSheet(panel, arrayBuffer, extension);
10365
+ await renderSheet(panel, arrayBuffer, extension, messages);
10133
10366
  return true;
10134
10367
  }
10135
10368
  if (hasEntry("ppt/presentation.xml")) {
@@ -10560,41 +10793,50 @@ function legacyOfficeFormatLabel(extension) {
10560
10793
  }
10561
10794
  return "PowerPoint Binary File Format";
10562
10795
  }
10563
- function renderLegacyWordBinary(panel, extension, arrayBuffer) {
10796
+ function renderLegacyWordBinary(panel, extension, arrayBuffer, messages) {
10564
10797
  try {
10565
10798
  renderLegacyWordDocument(panel, parseLegacyWordDocument(arrayBuffer));
10566
10799
  } catch (error) {
10567
- renderLegacyOfficeBinary(panel, extension, arrayBuffer, `Word \u4E8C\u8FDB\u5236\u89E3\u6790\u5931\u8D25\uFF1A${normalizeOfficeError(error)}`);
10800
+ renderLegacyOfficeBinary(
10801
+ panel,
10802
+ extension,
10803
+ arrayBuffer,
10804
+ messages,
10805
+ formatPreviewMessage(messages.officeLegacyWordParseFailed, { message: normalizeOfficeError(error, messages) })
10806
+ );
10568
10807
  }
10569
10808
  }
10570
- function renderLegacyOfficeBinary(panel, extension, arrayBuffer, parseError) {
10809
+ function renderLegacyOfficeBinary(panel, extension, arrayBuffer, messages, parseError) {
10571
10810
  const fragments = extractLegacyOfficeText(arrayBuffer);
10572
10811
  panel.replaceChildren();
10573
- const section = createSection("Office \u8F6C\u6362\u63D0\u793A");
10812
+ const section = createSection(messages.officeLegacyConversionTitle);
10574
10813
  section.classList.add("ofv-office-conversion");
10575
10814
  const format = document.createElement("p");
10576
10815
  const strong = document.createElement("strong");
10577
10816
  strong.textContent = `.${extension}`;
10578
- format.append(
10579
- strong,
10580
- document.createTextNode(
10581
- " \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"
10582
- )
10583
- );
10817
+ format.append(strong, document.createTextNode(" "), document.createTextNode(messages.officeLegacyBinaryNotice));
10584
10818
  const meta = document.createElement("dl");
10585
10819
  meta.className = "ofv-office-binary-meta";
10586
- appendOfficeBinaryMeta(meta, "\u683C\u5F0F\u7C7B\u578B", legacyOfficeFormatLabel(extension));
10587
- 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");
10588
- appendOfficeBinaryMeta(meta, "\u6587\u672C\u7247\u6BB5", `${fragments.length} \u6BB5`);
10820
+ appendOfficeBinaryMeta(meta, messages.officeLegacyMetaFormatType, legacyOfficeFormatLabel(extension));
10821
+ appendOfficeBinaryMeta(
10822
+ meta,
10823
+ messages.officeLegacyMetaFileStructure,
10824
+ hasOleSignature(arrayBuffer) ? messages.officeLegacyOleDetected : messages.officeLegacyOleMissing
10825
+ );
10826
+ appendOfficeBinaryMeta(
10827
+ meta,
10828
+ messages.officeLegacyMetaTextFragments,
10829
+ formatPreviewMessage(messages.officeLegacyTextFragmentCount, { count: fragments.length.toLocaleString() })
10830
+ );
10589
10831
  if (parseError) {
10590
- appendOfficeBinaryMeta(meta, "\u89E3\u6790\u72B6\u6001", parseError);
10832
+ appendOfficeBinaryMeta(meta, messages.officeLegacyMetaParseStatus, parseError);
10591
10833
  }
10592
10834
  section.append(format, meta);
10593
10835
  if (fragments.length > 0) {
10594
10836
  const article = document.createElement("article");
10595
10837
  article.className = "ofv-document ofv-office-binary-fragments";
10596
10838
  const heading = document.createElement("h4");
10597
- heading.textContent = "\u53EF\u8BFB\u6587\u672C\u7247\u6BB5";
10839
+ heading.textContent = messages.officeLegacyReadableFragments;
10598
10840
  article.append(heading);
10599
10841
  for (const fragment of fragments.slice(0, 80)) {
10600
10842
  const paragraph = document.createElement("p");
@@ -10605,7 +10847,7 @@ function renderLegacyOfficeBinary(panel, extension, arrayBuffer, parseError) {
10605
10847
  } else {
10606
10848
  const empty = document.createElement("p");
10607
10849
  empty.className = "ofv-office-binary-empty";
10608
- 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";
10850
+ empty.textContent = messages.officeLegacyNoText;
10609
10851
  section.append(empty);
10610
10852
  }
10611
10853
  panel.append(section);
@@ -10617,23 +10859,27 @@ function appendOfficeBinaryMeta(list, label, value) {
10617
10859
  detail.textContent = value;
10618
10860
  list.append(term, detail);
10619
10861
  }
10620
- function renderUnsupportedOffice(panel, extension) {
10862
+ function renderUnsupportedOffice(panel, extension, messages) {
10621
10863
  const legacyBinary = /* @__PURE__ */ new Set(["doc", "dot", "wps", "ppt", "pps", "key", "dps"]);
10622
- 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";
10864
+ const message = legacyBinary.has(extension) ? messages.officeUnsupportedLegacyMessage : messages.officeUnsupportedGenericMessage;
10623
10865
  panel.replaceChildren();
10624
- const section = createSection("Office \u57FA\u7840\u9884\u89C8");
10866
+ const section = createSection(messages.officeUnsupportedTitle);
10625
10867
  const format = document.createElement("p");
10626
10868
  const strong = document.createElement("strong");
10627
10869
  strong.textContent = `.${extension}`;
10628
- format.append(strong, document.createTextNode(` \u5DF2\u8FDB\u5165 Office \u63D2\u4EF6\u3002${message}`));
10870
+ format.append(
10871
+ strong,
10872
+ document.createTextNode(" "),
10873
+ document.createTextNode(formatPreviewMessage(messages.officeUnsupportedIntro, { message }))
10874
+ );
10629
10875
  const support = document.createElement("p");
10630
- 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";
10876
+ support.textContent = messages.officeUnsupportedSupportedFormats;
10631
10877
  section.append(format, support);
10632
10878
  panel.append(section);
10633
10879
  }
10634
- function normalizeOfficeError(error) {
10880
+ function normalizeOfficeError(error, messages) {
10635
10881
  const message = error instanceof Error ? error.message : String(error || "");
10636
- return message ? `\u89E3\u6790\u5668\u8FD4\u56DE\uFF1A${message}` : "\u89E3\u6790\u5668\u672A\u8FD4\u56DE\u5177\u4F53\u9519\u8BEF\u4FE1\u606F\u3002";
10882
+ return message ? formatPreviewMessage(messages.officeErrorWithMessage, { message }) : messages.officeErrorWithoutMessage;
10637
10883
  }
10638
10884
  function extractLegacyOfficeText(arrayBuffer) {
10639
10885
  const bytes = new Uint8Array(arrayBuffer);
@@ -11875,11 +12121,13 @@ function archivePlugin() {
11875
12121
  sidebarToggle.addEventListener("click", () => {
11876
12122
  setSidebarCollapsed(!layout.classList.contains("is-sidebar-collapsed"));
11877
12123
  });
11878
- const showDefaultSummary = () => {
12124
+ const showDefaultSummary = (isSupplemental = true) => {
11879
12125
  mainPanel.replaceChildren();
11880
12126
  const summary = document.createElement("div");
11881
12127
  summary.className = "ofv-archive-info";
11882
- hideSupplementalInfo4(summary);
12128
+ if (isSupplemental) {
12129
+ hideSupplementalInfo4(summary);
12130
+ }
11883
12131
  const heading = document.createElement("h3");
11884
12132
  heading.textContent = ctx.file.name;
11885
12133
  const info = document.createElement("div");
@@ -11889,12 +12137,16 @@ function archivePlugin() {
11889
12137
  appendArchiveInfo(info, "\u683C\u5F0F\u7C7B\u578B", `.${ext.toUpperCase()} \u538B\u7F29\u6587\u4EF6`);
11890
12138
  appendArchiveInfo(info, "\u5305\u542B\u6587\u4EF6\u6570", `${fileCount} \u4E2A`);
11891
12139
  appendArchiveInfo(info, "\u5305\u542B\u76EE\u5F55\u6570", `${dirCount} \u4E2A`);
11892
- appendArchiveInfo(info, "\u64CD\u4F5C\u63D0\u793A", "\u8BF7\u70B9\u51FB\u5DE6\u4FA7\u680F\u4E2D\u7684\u6587\u4EF6\u8FDB\u884C\u8054\u52A8\u9884\u89C8\u3002");
12140
+ appendArchiveInfo(
12141
+ info,
12142
+ "\u64CD\u4F5C\u63D0\u793A",
12143
+ 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"
12144
+ );
11893
12145
  summary.append(heading, info, createArchiveSummary(archiveEntries));
11894
12146
  mainPanel.append(summary);
11895
12147
  };
11896
- showDefaultSummary();
11897
12148
  const visibleEntries = archiveEntries.filter((e) => !e.dir).slice(0, 500);
12149
+ showDefaultSummary(visibleEntries.length > 0);
11898
12150
  let destroyed = false;
11899
12151
  let renderToken = 0;
11900
12152
  const openArchiveEntry = async (entry, item) => {