@open-file-viewer/core 0.1.14 → 0.1.15

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/README.md CHANGED
@@ -249,6 +249,23 @@ createViewer({
249
249
 
250
250
  Use `toolbar.render(ctx)` when you need to replace the toolbar completely. The context exposes file metadata, queue navigation, preview commands, download, fullscreen, print and search helpers.
251
251
 
252
+ ## Locale and Fallback Text
253
+
254
+ Fallback text defaults to Simplified Chinese for compatibility. Set `locale: "en-US"` for English built-in loading and unsupported-file messages, or override individual strings with `messages`:
255
+
256
+ ```ts
257
+ createViewer({
258
+ container: "#viewer",
259
+ file,
260
+ locale: "en-US",
261
+ messages: {
262
+ unsupportedTitle: "No inline preview available",
263
+ downloadFile: "Download original file"
264
+ },
265
+ plugins
266
+ });
267
+ ```
268
+
252
269
  ## License
253
270
 
254
271
  MIT
package/dist/index.cjs CHANGED
@@ -606,6 +606,48 @@ function revokeObjectUrl(url, isExternal) {
606
606
  }
607
607
  }
608
608
 
609
+ // src/messages.ts
610
+ var defaultMessages = {
611
+ "zh-CN": {
612
+ loading: "\u6B63\u5728\u52A0\u8F7D\u9884\u89C8...",
613
+ unsupportedTitle: "\u5F53\u524D\u6587\u4EF6\u6682\u4E0D\u652F\u6301\u5728\u7EBF\u9884\u89C8",
614
+ downloadTitle: "\u5F53\u524D\u6587\u4EF6\u53EF\u4E0B\u8F7D\u540E\u67E5\u770B",
615
+ downloadFile: "\u4E0B\u8F7D\u6587\u4EF6",
616
+ file: "\u6587\u4EF6",
617
+ unnamedFile: "\u672A\u547D\u540D\u6587\u4EF6",
618
+ format: "\u683C\u5F0F",
619
+ unknown: "\u672A\u77E5",
620
+ mime: "MIME",
621
+ undeclared: "\u672A\u58F0\u660E",
622
+ size: "\u5927\u5C0F",
623
+ source: "\u6765\u6E90",
624
+ remoteUrl: "\u8FDC\u7A0B URL",
625
+ localFile: "\u672C\u5730/\u5185\u5B58\u6587\u4EF6"
626
+ },
627
+ "en-US": {
628
+ loading: "Loading preview...",
629
+ unsupportedTitle: "Preview is not available for this file",
630
+ downloadTitle: "This file can be downloaded and opened locally",
631
+ downloadFile: "Download file",
632
+ file: "File",
633
+ unnamedFile: "Untitled file",
634
+ format: "Format",
635
+ unknown: "Unknown",
636
+ mime: "MIME",
637
+ undeclared: "Not declared",
638
+ size: "Size",
639
+ source: "Source",
640
+ remoteUrl: "Remote URL",
641
+ localFile: "Local or in-memory file"
642
+ }
643
+ };
644
+ function resolveMessages(options) {
645
+ return {
646
+ ...defaultMessages[options.locale || "zh-CN"],
647
+ ...options.messages
648
+ };
649
+ }
650
+
609
651
  // src/plugins/fallback.ts
610
652
  function fallbackPlugin() {
611
653
  return {
@@ -623,12 +665,12 @@ function fallbackPlugin() {
623
665
  const panel = document.createElement("div");
624
666
  panel.className = "ofv-fallback";
625
667
  const title = document.createElement("strong");
626
- title.textContent = ctx.options.fallback === "download" ? "\u5F53\u524D\u6587\u4EF6\u53EF\u4E0B\u8F7D\u540E\u67E5\u770B" : "\u5F53\u524D\u6587\u4EF6\u6682\u4E0D\u652F\u6301\u5728\u7EBF\u9884\u89C8";
627
- const meta = createFallbackMeta(ctx.file);
668
+ title.textContent = ctx.options.fallback === "download" ? ctx.options.messages.downloadTitle : ctx.options.messages.unsupportedTitle;
669
+ const meta = createFallbackMeta(ctx.file, ctx.options.messages);
628
670
  const download = document.createElement("a");
629
671
  download.href = url;
630
672
  download.download = ctx.file.name;
631
- download.textContent = "\u4E0B\u8F7D\u6587\u4EF6";
673
+ download.textContent = ctx.options.messages.downloadFile;
632
674
  panel.append(title, meta, download);
633
675
  ctx.viewport.classList.add("ofv-center");
634
676
  ctx.viewport.append(panel);
@@ -644,14 +686,14 @@ function fallbackPlugin() {
644
686
  }
645
687
  };
646
688
  }
647
- function createFallbackMeta(file) {
689
+ function createFallbackMeta(file, messages) {
648
690
  const meta = document.createElement("dl");
649
691
  meta.className = "ofv-fallback-meta";
650
- appendFallbackMeta(meta, "\u6587\u4EF6", file.name || "\u672A\u547D\u540D\u6587\u4EF6");
651
- appendFallbackMeta(meta, "\u683C\u5F0F", file.extension ? `.${file.extension}` : "\u672A\u77E5");
652
- appendFallbackMeta(meta, "MIME", file.mimeType || "\u672A\u58F0\u660E");
653
- appendFallbackMeta(meta, "\u5927\u5C0F", file.size === void 0 ? "\u672A\u77E5" : formatFallbackBytes(file.size));
654
- appendFallbackMeta(meta, "\u6765\u6E90", file.url ? "\u8FDC\u7A0B URL" : "\u672C\u5730/\u5185\u5B58\u6587\u4EF6");
692
+ appendFallbackMeta(meta, messages.file, file.name || messages.unnamedFile);
693
+ appendFallbackMeta(meta, messages.format, file.extension ? `.${file.extension}` : messages.unknown);
694
+ appendFallbackMeta(meta, messages.mime, file.mimeType || messages.undeclared);
695
+ appendFallbackMeta(meta, messages.size, file.size === void 0 ? messages.unknown : formatFallbackBytes(file.size));
696
+ appendFallbackMeta(meta, messages.source, file.url ? messages.remoteUrl : messages.localFile);
655
697
  return meta;
656
698
  }
657
699
  function appendFallbackMeta(parent, label, value) {
@@ -709,15 +751,17 @@ function createViewer(options) {
709
751
  host.append(status, viewport);
710
752
  container.replaceChildren(host);
711
753
  const normalizedOptions = {
754
+ ...options,
712
755
  fit: options.fit || "contain",
713
756
  fallback: options.fallback || "inline",
714
- ...options
757
+ zoom: normalizeInitialZoom(options.zoom),
758
+ messages: resolveMessages(options)
715
759
  };
716
760
  let destroyed = false;
717
761
  let renderToken = 0;
718
762
  const setLoading = (loading) => {
719
763
  status.hidden = !loading;
720
- status.textContent = loading ? "Loading preview..." : "";
764
+ status.textContent = loading ? normalizedOptions.messages.loading : "";
721
765
  };
722
766
  const setError = (error) => {
723
767
  status.hidden = false;
@@ -873,6 +917,9 @@ function clampIndex(index, length) {
873
917
  }
874
918
  return Math.min(Math.max(index, 0), length - 1);
875
919
  }
920
+ function normalizeInitialZoom(zoom) {
921
+ return typeof zoom === "number" && Number.isFinite(zoom) && zoom > 0 ? zoom : 1;
922
+ }
876
923
  function applyTheme(container, theme) {
877
924
  const media = window.matchMedia?.("(prefers-color-scheme: dark)");
878
925
  const classes = ["ofv-theme-light", "ofv-theme-dark"];
@@ -1749,6 +1796,98 @@ async function findPlugin(plugins, file) {
1749
1796
  return fallbackPlugin();
1750
1797
  }
1751
1798
 
1799
+ // src/plugins/utils.ts
1800
+ async function readArrayBuffer(file) {
1801
+ if (file.source instanceof ArrayBuffer) {
1802
+ return file.source;
1803
+ }
1804
+ if (file.blob) {
1805
+ return file.blob.arrayBuffer();
1806
+ }
1807
+ if (typeof file.source === "string") {
1808
+ const response = await fetch(file.source);
1809
+ if (!response.ok) {
1810
+ throw new Error(`Failed to fetch file: ${response.status}`);
1811
+ }
1812
+ return response.arrayBuffer();
1813
+ }
1814
+ throw new Error("Unsupported file source.");
1815
+ }
1816
+ async function readTextFile(file) {
1817
+ const decode = (buffer) => decodeTextBuffer(buffer);
1818
+ if (typeof file.source === "string") {
1819
+ const response = await fetch(file.source);
1820
+ if (!response.ok) {
1821
+ throw new Error(`Failed to fetch text file: ${response.status}`);
1822
+ }
1823
+ return decode(await response.arrayBuffer());
1824
+ }
1825
+ if (file.blob) {
1826
+ return decode(await file.blob.arrayBuffer());
1827
+ }
1828
+ if (file.source instanceof ArrayBuffer) {
1829
+ return decode(file.source);
1830
+ }
1831
+ return String(file.source);
1832
+ }
1833
+ function createPanel(className = "") {
1834
+ const panel = document.createElement("div");
1835
+ panel.className = `ofv-panel ${className}`.trim();
1836
+ return panel;
1837
+ }
1838
+ function getInitialZoom(ctx, min = 0.1, max = 8) {
1839
+ return Math.min(max, Math.max(min, ctx.options.zoom));
1840
+ }
1841
+ function createSection(title) {
1842
+ const section = document.createElement("section");
1843
+ section.className = "ofv-section";
1844
+ const heading = document.createElement("h3");
1845
+ heading.textContent = title;
1846
+ section.append(heading);
1847
+ return section;
1848
+ }
1849
+ function appendMeta(parent, label, value) {
1850
+ const row = document.createElement("div");
1851
+ row.className = "ofv-meta-row";
1852
+ const key = document.createElement("span");
1853
+ key.textContent = label;
1854
+ const content = document.createElement("strong");
1855
+ content.textContent = String(value);
1856
+ row.append(key, content);
1857
+ parent.append(row);
1858
+ }
1859
+ function decodeTextBuffer(buffer) {
1860
+ return decodeTextBytes(new Uint8Array(buffer));
1861
+ }
1862
+ function decodeTextBytes(bytes) {
1863
+ if (bytes.length >= 3 && bytes[0] === 239 && bytes[1] === 187 && bytes[2] === 191) {
1864
+ return new TextDecoder("utf-8").decode(bytes.subarray(3));
1865
+ }
1866
+ if (bytes.length >= 2) {
1867
+ if (bytes[0] === 255 && bytes[1] === 254) {
1868
+ return new TextDecoder("utf-16le").decode(bytes.subarray(2));
1869
+ }
1870
+ if (bytes[0] === 254 && bytes[1] === 255) {
1871
+ return new TextDecoder("utf-16be").decode(bytes.subarray(2));
1872
+ }
1873
+ }
1874
+ try {
1875
+ return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
1876
+ } catch {
1877
+ return decodeWithFallback(bytes, "gb18030") || decodeWithFallback(bytes, "gbk") || new TextDecoder("utf-8").decode(bytes);
1878
+ }
1879
+ }
1880
+ function decodeWithFallback(bytes, encoding) {
1881
+ try {
1882
+ return new TextDecoder(encoding).decode(bytes);
1883
+ } catch {
1884
+ return void 0;
1885
+ }
1886
+ }
1887
+ function resolveFormat(file, mimeMap) {
1888
+ return file.extension || mimeMap[file.mimeType] || "";
1889
+ }
1890
+
1752
1891
  // src/plugins/image.ts
1753
1892
  var imageExtensions = /* @__PURE__ */ new Set([
1754
1893
  "jpg",
@@ -1862,7 +2001,7 @@ function imagePlugin() {
1862
2001
  canvasSource.setAttribute("role", "img");
1863
2002
  canvasSource.setAttribute("aria-label", ctx.file.name);
1864
2003
  }
1865
- let scale = 1;
2004
+ let scale = getInitialZoom(ctx);
1866
2005
  let rotation = 0;
1867
2006
  let offsetX = 0;
1868
2007
  let offsetY = 0;
@@ -3690,95 +3829,6 @@ function readAiffExtended(bytes, offset) {
3690
3829
  return mantissa * Math.pow(2, exponent - 16383 - 63);
3691
3830
  }
3692
3831
 
3693
- // src/plugins/utils.ts
3694
- async function readArrayBuffer(file) {
3695
- if (file.source instanceof ArrayBuffer) {
3696
- return file.source;
3697
- }
3698
- if (file.blob) {
3699
- return file.blob.arrayBuffer();
3700
- }
3701
- if (typeof file.source === "string") {
3702
- const response = await fetch(file.source);
3703
- if (!response.ok) {
3704
- throw new Error(`Failed to fetch file: ${response.status}`);
3705
- }
3706
- return response.arrayBuffer();
3707
- }
3708
- throw new Error("Unsupported file source.");
3709
- }
3710
- async function readTextFile(file) {
3711
- const decode = (buffer) => decodeTextBuffer(buffer);
3712
- if (typeof file.source === "string") {
3713
- const response = await fetch(file.source);
3714
- if (!response.ok) {
3715
- throw new Error(`Failed to fetch text file: ${response.status}`);
3716
- }
3717
- return decode(await response.arrayBuffer());
3718
- }
3719
- if (file.blob) {
3720
- return decode(await file.blob.arrayBuffer());
3721
- }
3722
- if (file.source instanceof ArrayBuffer) {
3723
- return decode(file.source);
3724
- }
3725
- return String(file.source);
3726
- }
3727
- function createPanel(className = "") {
3728
- const panel = document.createElement("div");
3729
- panel.className = `ofv-panel ${className}`.trim();
3730
- return panel;
3731
- }
3732
- function createSection(title) {
3733
- const section = document.createElement("section");
3734
- section.className = "ofv-section";
3735
- const heading = document.createElement("h3");
3736
- heading.textContent = title;
3737
- section.append(heading);
3738
- return section;
3739
- }
3740
- function appendMeta(parent, label, value) {
3741
- const row = document.createElement("div");
3742
- row.className = "ofv-meta-row";
3743
- const key = document.createElement("span");
3744
- key.textContent = label;
3745
- const content = document.createElement("strong");
3746
- content.textContent = String(value);
3747
- row.append(key, content);
3748
- parent.append(row);
3749
- }
3750
- function decodeTextBuffer(buffer) {
3751
- return decodeTextBytes(new Uint8Array(buffer));
3752
- }
3753
- function decodeTextBytes(bytes) {
3754
- if (bytes.length >= 3 && bytes[0] === 239 && bytes[1] === 187 && bytes[2] === 191) {
3755
- return new TextDecoder("utf-8").decode(bytes.subarray(3));
3756
- }
3757
- if (bytes.length >= 2) {
3758
- if (bytes[0] === 255 && bytes[1] === 254) {
3759
- return new TextDecoder("utf-16le").decode(bytes.subarray(2));
3760
- }
3761
- if (bytes[0] === 254 && bytes[1] === 255) {
3762
- return new TextDecoder("utf-16be").decode(bytes.subarray(2));
3763
- }
3764
- }
3765
- try {
3766
- return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
3767
- } catch {
3768
- return decodeWithFallback(bytes, "gb18030") || decodeWithFallback(bytes, "gbk") || new TextDecoder("utf-8").decode(bytes);
3769
- }
3770
- }
3771
- function decodeWithFallback(bytes, encoding) {
3772
- try {
3773
- return new TextDecoder(encoding).decode(bytes);
3774
- } catch {
3775
- return void 0;
3776
- }
3777
- }
3778
- function resolveFormat(file, mimeMap) {
3779
- return file.extension || mimeMap[file.mimeType] || "";
3780
- }
3781
-
3782
3832
  // src/plugins/text.ts
3783
3833
  var langMap = {
3784
3834
  js: "javascript",
@@ -4247,7 +4297,7 @@ function textPlugin() {
4247
4297
  };
4248
4298
  }
4249
4299
  function createTextZoomController(target, cssVariable, ctx) {
4250
- let zoom = 1;
4300
+ let zoom = getInitialZoom(ctx, 0.5, 3);
4251
4301
  const apply = () => {
4252
4302
  const normalized = Math.round(zoom * 100) / 100;
4253
4303
  target.style.setProperty(cssVariable, String(normalized));
@@ -4557,6 +4607,7 @@ function pdfPlugin(options = {}) {
4557
4607
  viewport: ctx.viewport,
4558
4608
  size: ctx.size,
4559
4609
  fit: ctx.options.fit,
4610
+ zoom: ctx.options.zoom,
4560
4611
  toolbar: ctx.toolbar,
4561
4612
  encryptedTitle: "PDF \u5DF2\u52A0\u5BC6\uFF0C\u65E0\u6CD5\u5728\u7EBF\u9884\u89C8",
4562
4613
  encryptedMessage: "\u8BF7\u4E0B\u8F7D\u540E\u4F7F\u7528\u5BC6\u7801\u6253\u5F00\uFF0C\u6216\u4E0A\u4F20\u89E3\u5BC6\u540E\u7684 PDF \u6587\u4EF6\u3002",
@@ -4662,7 +4713,7 @@ async function renderPdfDocumentPreview(options) {
4662
4713
  const pageStates = [];
4663
4714
  let observer = null;
4664
4715
  let currentSize = options.size;
4665
- let zoomFactor = 1;
4716
+ let zoomFactor = getInitialZoom({ options: { zoom: options.zoom ?? 1 } }, 0.25, 4);
4666
4717
  let rotation = 0;
4667
4718
  const updateSummary = () => {
4668
4719
  renderPdfSummary(summary, pdfDocument.numPages, pagesMeta, options.fit, zoomFactor);
@@ -5987,6 +6038,7 @@ async function renderConvertedOfficePreview(panel, ctx, options, conversionConte
5987
6038
  viewport: panel,
5988
6039
  size: ctx.size,
5989
6040
  fit: ctx.options.fit,
6041
+ zoom: ctx.options.zoom,
5990
6042
  toolbar: ctx.toolbar,
5991
6043
  title: "Office \u9AD8\u4FDD\u771F\u8F6C\u6362\u9884\u89C8",
5992
6044
  fallbackTitle: "Office \u8F6C\u6362\u540E\u7684 PDF \u65E0\u6CD5\u9884\u89C8",
@@ -6050,7 +6102,7 @@ function createOfficeZoomController(panel, ctx) {
6050
6102
  if (!canZoom) {
6051
6103
  return void 0;
6052
6104
  }
6053
- let zoom = 1;
6105
+ let zoom = getInitialZoom(ctx, 0.5, 3);
6054
6106
  const apply = () => {
6055
6107
  panel.style.setProperty("--ofv-office-zoom", String(zoom));
6056
6108
  panel.dispatchEvent(new CustomEvent("ofv-office-zoom"));
@@ -18870,7 +18922,7 @@ async function createPostScriptPreview(bytes, url, fileName, size, fit, toolbar)
18870
18922
  preview.append(summary);
18871
18923
  return { element: preview };
18872
18924
  }
18873
- async function createPdfCompatibleAiPreview(bytes, url, fileName, size, fit, toolbar, pdfOffset = 0) {
18925
+ async function createPdfCompatibleAiPreview(bytes, url, fileName, size, fit, toolbar, pdfOffset = 0, zoom = 1) {
18874
18926
  const wrapper = document.createElement("div");
18875
18927
  wrapper.className = "ofv-ai-pdf-preview";
18876
18928
  let pdfUrl = url;
@@ -18885,6 +18937,7 @@ async function createPdfCompatibleAiPreview(bytes, url, fileName, size, fit, too
18885
18937
  viewport: wrapper,
18886
18938
  size,
18887
18939
  fit,
18940
+ zoom,
18888
18941
  toolbar,
18889
18942
  fallbackTitle: "AI PDF \u517C\u5BB9\u9884\u89C8\u5931\u8D25",
18890
18943
  revokeUrlOnDestroy: false