@file-viewer/core 3.2.2 → 3.3.1

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.
Files changed (112) hide show
  1. package/README.en.md +26 -15
  2. package/README.md +47 -41
  3. package/README.zh-CN.md +175 -0
  4. package/dist/assets.d.ts +2 -2
  5. package/dist/assets.js +5 -1
  6. package/dist/browser/controller.d.ts +2 -1
  7. package/dist/browser/controller.js +156 -32
  8. package/dist/browser/source-feedback.d.ts +4 -0
  9. package/dist/browser/source-feedback.js +40 -0
  10. package/dist/browser/toolbar-focus.d.ts +2 -0
  11. package/dist/browser/toolbar-focus.js +46 -0
  12. package/dist/browser.d.ts +2 -2
  13. package/dist/browser.js +2 -2
  14. package/dist/config/options.d.ts +1 -1
  15. package/dist/contracts/types.d.ts +479 -1
  16. package/dist/contracts/xml.d.ts +11 -0
  17. package/dist/export.d.ts +10 -3
  18. package/dist/export.js +9 -2
  19. package/dist/exportDocument.d.ts +12 -1
  20. package/dist/exportDocument.js +31 -2
  21. package/dist/features/document/dom/anchors.d.ts +1 -1
  22. package/dist/features/document/dom/index.d.ts +5 -5
  23. package/dist/features/document/dom/index.js +3 -3
  24. package/dist/features/document/dom/providers.d.ts +1 -1
  25. package/dist/features/document/events.d.ts +2 -2
  26. package/dist/features/document/events.js +3 -3
  27. package/dist/features/document/fit.d.ts +1 -1
  28. package/dist/features/document/fit.js +1 -1
  29. package/dist/features/document/imageHandTool.d.ts +3 -0
  30. package/dist/features/document/imageHandTool.js +141 -0
  31. package/dist/features/document/model.d.ts +1 -1
  32. package/dist/features/document/search.d.ts +1 -1
  33. package/dist/features/document/search.js +2 -2
  34. package/dist/features/document/viewState.d.ts +1 -1
  35. package/dist/features/document/viewState.js +2 -2
  36. package/dist/features/document/zoom.d.ts +1 -1
  37. package/dist/features/document/zoom.js +3 -3
  38. package/dist/features/document/zoomGestures.d.ts +2 -2
  39. package/dist/features/document/zoomGestures.js +2 -0
  40. package/dist/features/printMask.d.ts +1 -1
  41. package/dist/features/printMaskDesigner.d.ts +6 -2
  42. package/dist/features/printMaskDesigner.js +11 -2
  43. package/dist/features/printMaskLoader.d.ts +7 -1
  44. package/dist/features/printMaskLoader.js +7 -1
  45. package/dist/features/watermark.d.ts +1 -1
  46. package/dist/headless.d.ts +29 -29
  47. package/dist/headless.js +21 -16
  48. package/dist/i18n/messages.d.ts +1 -1
  49. package/dist/i18n/messages.de.d.ts +5 -1
  50. package/dist/i18n/messages.de.js +4 -0
  51. package/dist/i18n/messages.ja.d.ts +6 -1
  52. package/dist/i18n/messages.ja.js +5 -0
  53. package/dist/i18n/messages.js +2 -2
  54. package/dist/index.d.ts +77 -75
  55. package/dist/index.js +39 -37
  56. package/dist/lifecycle/facade.d.ts +4 -4
  57. package/dist/lifecycle/facade.js +2 -2
  58. package/dist/lifecycle/operations.d.ts +6 -4
  59. package/dist/lifecycle/operations.js +21 -4
  60. package/dist/output/canvasImage.d.ts +2 -1
  61. package/dist/output/canvasImage.js +4 -1
  62. package/dist/output/export.d.ts +3 -1
  63. package/dist/output/export.js +12 -0
  64. package/dist/output/printLayout.d.ts +5 -0
  65. package/dist/output/printLayout.js +4 -0
  66. package/dist/output/snapshotDownload.d.ts +2 -1
  67. package/dist/output/snapshotDownload.js +3 -2
  68. package/dist/platform/assets.d.ts +9 -2
  69. package/dist/platform/assets.js +15 -1
  70. package/dist/platform/copiedAssets.d.ts +1 -0
  71. package/dist/platform/copiedAssets.js +2 -1
  72. package/dist/presentation/colorScheme.d.ts +3 -0
  73. package/dist/presentation/colorScheme.js +40 -0
  74. package/dist/presentation/state.d.ts +1 -1
  75. package/dist/presentation/state.js +3 -3
  76. package/dist/print-mask.d.ts +2 -2
  77. package/dist/print-mask.js +1 -1
  78. package/dist/registry/capabilities.d.ts +14 -1
  79. package/dist/registry/capabilities.js +15 -2
  80. package/dist/registry/formats.d.ts +1 -1
  81. package/dist/registry/formats.generated.d.ts +1 -0
  82. package/dist/registry/formats.generated.js +1 -0
  83. package/dist/registry/formats.js +2 -2
  84. package/dist/registry/registry.d.ts +15 -1
  85. package/dist/registry/registry.js +10 -2
  86. package/dist/renderers/image.d.ts +1 -1
  87. package/dist/renderers/image.js +13 -7
  88. package/dist/renderers/index.d.ts +1 -1
  89. package/dist/renderers/index.js +11 -5
  90. package/dist/rendering/dispatcher.d.ts +6 -1
  91. package/dist/rendering/dispatcher.js +3 -3
  92. package/dist/rendering/handler.d.ts +13 -3
  93. package/dist/rendering/handler.js +21 -9
  94. package/dist/security/svgResources.d.ts +10 -0
  95. package/dist/security/svgResources.js +12 -0
  96. package/dist/source/index.d.ts +3 -3
  97. package/dist/source/index.js +1 -1
  98. package/dist/source/loading.d.ts +12 -7
  99. package/dist/source/loading.js +22 -17
  100. package/dist/source/precheck.d.ts +13 -1
  101. package/dist/source/precheck.js +9 -2
  102. package/dist/source/textEncoding.d.ts +18 -0
  103. package/dist/source/textEncoding.js +15 -0
  104. package/dist/viewer/createViewer.d.ts +1 -1
  105. package/dist/viewer/createViewer.js +30 -17
  106. package/dist/viewer/loading.d.ts +6 -2
  107. package/dist/viewer/loading.js +9 -1
  108. package/dist/viewer/operations.d.ts +3 -3
  109. package/dist/viewer/operations.js +14 -6
  110. package/dist/viewer/state.d.ts +6 -2
  111. package/dist/viewer/state.js +8 -3
  112. package/package.json +14 -5
@@ -1,7 +1,8 @@
1
- import { registerFileViewerZoomProvider, unregisterFileViewerZoomProvider, } from "../features/document/dom/index.js";
2
- import { resolveFileViewerFitScale, } from "../features/document/fit.js";
3
- import { createFileViewerTranslator } from "../i18n/messages.js";
4
- import { createFileViewerZoomChangeEmitter as createZoomChangeEmitter } from "../features/document/zoom.js";
1
+ import { registerFileViewerZoomProvider, unregisterFileViewerZoomProvider, } from '../features/document/dom/index.js';
2
+ import { installFileViewerImageHandTool } from '../features/document/imageHandTool.js';
3
+ import { resolveFileViewerFitScale, } from '../features/document/fit.js';
4
+ import { createFileViewerTranslator } from '../i18n/messages.js';
5
+ import { createFileViewerZoomChangeEmitter as createZoomChangeEmitter } from '../features/document/zoom.js';
5
6
  const imageMimeMap = {
6
7
  avif: 'image/avif',
7
8
  bmp: 'image/bmp',
@@ -20,7 +21,7 @@ const imageMimeMap = {
20
21
  };
21
22
  const imageStyle = `
22
23
  .image-viewer{position:relative;width:100%;height:100%;overflow:auto;background:var(--file-viewer-render-surface-background,#eef1f4);box-sizing:border-box}
23
- .image-stage{min-width:100%;min-height:100%;display:flex;align-items:center;justify-content:center;padding:24px;box-sizing:border-box}
24
+ .image-stage{width:max-content;min-width:100%;min-height:100%;display:flex;align-items:center;justify-content:center;padding:24px;box-sizing:border-box}
24
25
  .image-stage img{display:block;width:auto;max-width:none;margin:0 auto;border:0;box-shadow:0 18px 48px rgba(15,23,42,.16);background:#fff;cursor:zoom-in}
25
26
  .image-stage img:focus-visible{outline:3px solid #2563eb;outline-offset:4px}
26
27
  .image-lightbox{position:absolute;inset:0;z-index:40;display:flex;align-items:center;justify-content:center;padding:40px;background:rgba(15,23,42,.9);box-sizing:border-box;opacity:0;visibility:hidden;pointer-events:none;transition:opacity .18s ease,visibility 0s linear .18s}
@@ -127,7 +128,7 @@ const createLightbox = (documentRef, src, t) => {
127
128
  };
128
129
  };
129
130
  export default async function renderImage(buffer, target, type, context) {
130
- var _a, _b;
131
+ var _a, _b, _c, _d;
131
132
  const t = createFileViewerTranslator(context === null || context === void 0 ? void 0 : context.options);
132
133
  const documentRef = target.ownerDocument || document;
133
134
  const targetWindow = documentRef.defaultView;
@@ -155,6 +156,8 @@ export default async function renderImage(buffer, target, type, context) {
155
156
  image.setAttribute('aria-haspopup', 'dialog');
156
157
  stage.append(image);
157
158
  root.append(stage);
159
+ const disposeHandTool = ((_c = (_b = context === null || context === void 0 ? void 0 : context.options) === null || _b === void 0 ? void 0 : _b.image) === null || _c === void 0 ? void 0 : _c.handTool) === true
160
+ ? installFileViewerImageHandTool(root, context.signal) : () => { };
158
161
  const lightbox = createLightbox(documentRef, src, t);
159
162
  const openLightbox = () => lightbox.open(image);
160
163
  const openLightboxFromKeyboard = (event) => {
@@ -198,6 +201,8 @@ export default async function renderImage(buffer, target, type, context) {
198
201
  applyImageZoom();
199
202
  zoomEmitter.emit();
200
203
  };
204
+ // Zoom subscribers may resize the parent toolbar. Keep geometry writes out
205
+ // of ResizeObserver delivery so ancestor notifications remain deliverable.
201
206
  const scheduleViewportUpdate = () => {
202
207
  if (destroyed || viewportResizeFrame !== null)
203
208
  return;
@@ -290,7 +295,7 @@ export default async function renderImage(buffer, target, type, context) {
290
295
  subscribe: zoomEmitter.subscribe,
291
296
  });
292
297
  target.replaceChildren(createStyle(documentRef), root);
293
- (((_b = context === null || context === void 0 ? void 0 : context.surface) === null || _b === void 0 ? void 0 : _b.shadowRoot) || target).append(lightbox.element);
298
+ (((_d = context === null || context === void 0 ? void 0 : context.surface) === null || _d === void 0 ? void 0 : _d.shadowRoot) || target).append(lightbox.element);
294
299
  updateViewportSize();
295
300
  return {
296
301
  $el: target,
@@ -303,6 +308,7 @@ export default async function renderImage(buffer, target, type, context) {
303
308
  }
304
309
  (_a = context === null || context === void 0 ? void 0 : context.registerThumbnailAdapter) === null || _a === void 0 ? void 0 : _a.call(context, null);
305
310
  unregisterFileViewerZoomProvider(root);
311
+ disposeHandTool();
306
312
  resizeObserver.disconnect();
307
313
  image.removeEventListener('load', updateViewportSize);
308
314
  image.removeEventListener('click', openLightbox);
@@ -1,4 +1,4 @@
1
- import type { FileViewerBuiltinRendererPreset, FileRenderHandler, FileViewerRenderedInstance } from "../contracts/types.js";
1
+ import type { FileViewerBuiltinRendererPreset, FileRenderHandler, FileViewerRenderedInstance } from '../contracts/types.js';
2
2
  type CoreBrowserRendererHandler = FileRenderHandler<FileViewerRenderedInstance, HTMLDivElement>;
3
3
  interface CoreBrowserRendererHandlerEntry {
4
4
  rendererId: string;
@@ -1,17 +1,23 @@
1
- import { DEFAULT_RENDERER_DEFINITIONS } from "../registry/formats.js";
2
- import { createFileRenderHandlerRegistry } from "../rendering/handler.js";
3
- import { createFileViewerRendererDispatcher } from "../rendering/dispatcher.js";
4
- import { createFileViewerUnsupportedState } from "../viewer/state.js";
1
+ // Browser renderer strategy registry.
2
+ //
3
+ // Each entry lazy-loads a concrete renderer only after its format is selected.
4
+ // Keep renderer-specific dependencies inside the leaf renderer files so the
5
+ // core shell and framework wrappers stay fast on first load.
6
+ import { DEFAULT_RENDERER_DEFINITIONS } from '../registry/formats.js';
7
+ import { createFileRenderHandlerRegistry } from '../rendering/handler.js';
8
+ import { createFileViewerRendererDispatcher } from '../rendering/dispatcher.js';
9
+ import { createFileViewerUnsupportedState } from '../viewer/state.js';
5
10
  const createWrapper = (el) => ({
6
11
  $el: el,
7
12
  unmount() {
13
+ // DOM renderers clean themselves up through their own returned instance.
8
14
  },
9
15
  });
10
16
  export const coreBrowserRendererHandlers = [
11
17
  {
12
18
  rendererId: 'image',
13
19
  handler: async (buffer, target, type, context) => {
14
- const { default: renderImage } = await import("./image.js");
20
+ const { default: renderImage } = await import('./image.js');
15
21
  return renderImage(buffer, target, type, context);
16
22
  },
17
23
  },
@@ -1,8 +1,13 @@
1
- import type { RendererRegistry } from "../contracts/types.js";
1
+ import type { RendererRegistry } from '../contracts/types.js';
2
2
  export interface FileViewerRendererHandlerEntry<Handler> {
3
3
  rendererId: string;
4
4
  handler: Handler;
5
5
  }
6
+ /**
7
+ * Content-signature aware renderers can throw this shape when the filename
8
+ * extension points at the wrong container. The orchestration layer follows the
9
+ * redirect once, using the handler registered for the detected renderer id.
10
+ */
6
11
  export interface FileViewerRendererRedirectError extends Error {
7
12
  actualRendererId: string;
8
13
  }
@@ -1,6 +1,6 @@
1
- import { DEFAULT_RENDERER_DEFINITIONS } from "../registry/formats.js";
2
- import { createRendererRegistry } from "../registry/registry.js";
3
- import { normalizeFileExtension } from "../source/index.js";
1
+ import { DEFAULT_RENDERER_DEFINITIONS } from '../registry/formats.js';
2
+ import { createRendererRegistry } from '../registry/registry.js';
3
+ import { normalizeFileExtension } from '../source/index.js';
4
4
  export const resolveFileViewerRendererRedirectId = (error) => {
5
5
  if (!error || typeof error !== 'object' || !('actualRendererId' in error)) {
6
6
  return undefined;
@@ -1,6 +1,6 @@
1
- import type { FileViewerRendererDispatcher } from "./dispatcher.js";
2
- import { type FileViewerMutableAccessor, type MutableFileViewerRenderReadinessState } from "../source/loading.js";
3
- import type { FileRenderExportAdapter, FileRenderThumbnailAdapter, FileRenderContext, FileRenderHandler, FileViewerLifecycleContext, FileViewerOptions, FileViewerStyleIsolation, RenderSurface, RendererDefinition, RendererLoadContext, RendererLoader, RendererRegistry, RendererSession } from "../contracts/types.js";
1
+ import type { FileViewerRendererDispatcher } from './dispatcher.js';
2
+ import { type FileViewerMutableAccessor, type MutableFileViewerRenderReadinessState } from '../source/loading.js';
3
+ import type { FileRenderExportAdapter, FileRenderThumbnailAdapter, FileRenderContext, FileRenderHandler, FileViewerLifecycleContext, FileViewerOptions, FileViewerStyleIsolation, RenderSurface, RendererDefinition, RendererLoadContext, RendererLoader, RendererRegistry, RendererSession } from '../contracts/types.js';
4
4
  export declare const FILE_VIEWER_RENDER_SESSION_DISPOSE_ERROR_MESSAGE = "\u9884\u89C8\u5185\u5BB9\u5378\u8F7D\u5931\u8D25";
5
5
  export interface ResolveFileViewerRenderSessionDisposeErrorMessageInput {
6
6
  message?: string;
@@ -21,6 +21,7 @@ export interface RenderFileViewerHandlerInput<Rendered = unknown, Target extends
21
21
  export interface CreateFileRenderHandlerLoaderOptions<Rendered = unknown, Target extends HTMLElement = HTMLElement> {
22
22
  handler: FileRenderHandler<Rendered, Target>;
23
23
  rendererId?: string;
24
+ /** Allows stream-preferred handlers to receive an empty buffer plus context.streamUrl. */
24
25
  allowUrlSource?: boolean;
25
26
  getTarget?: (context: RendererLoadContext) => Target;
26
27
  createContext?: (context: RendererLoadContext) => FileRenderContext;
@@ -60,6 +61,10 @@ export interface FileViewerStyleHandle {
60
61
  remove(): void;
61
62
  }
62
63
  export interface AppendFileViewerStyleOptions {
64
+ /**
65
+ * Constructable stylesheets are useful for long-lived ShadowRoots. Keep this
66
+ * opt-in so renderer styles still clean up naturally with their target node.
67
+ */
63
68
  adoptedStyleSheet?: boolean;
64
69
  }
65
70
  export interface ResetFileViewerRenderSurfaceInput<Session extends RendererSession = RendererSession> {
@@ -164,6 +169,11 @@ export interface CreateFileViewerRenderSurfaceActionHandlersInput<Session extend
164
169
  export declare const DEFAULT_FILE_VIEWER_RENDER_TARGET_CLASS = "file-render";
165
170
  export declare const FILE_VIEWER_RENDER_SURFACE_BACKGROUND_PROPERTY = "--file-viewer-render-surface-background";
166
171
  export declare const normalizeFileViewerRenderSurfaceBackground: (value: unknown) => string | null;
172
+ /**
173
+ * Keeps the public UI option and the renderer-facing CSS custom property in
174
+ * sync. Custom properties inherit through Shadow DOM, so setting this on the
175
+ * render host covers both scoped and shadow-isolated renderer targets.
176
+ */
167
177
  export declare const syncFileViewerRenderSurfaceBackground: (target: HTMLElement | null | undefined, options?: Pick<FileViewerOptions, "ui"> | null) => string | null;
168
178
  export declare const isFileViewerShadowRoot: (value: unknown) => value is ShadowRoot;
169
179
  export declare const getFileViewerShadowRootForNode: (node: Node | null | undefined) => ShadowRoot | null;
@@ -1,12 +1,17 @@
1
- import { DEFAULT_RENDERER_DEFINITIONS } from "../registry/formats.js";
2
- import { waitForFileViewerNextPaint } from "../output/export.js";
3
- import { createFileViewerRendererDispatcher } from "./dispatcher.js";
4
- import { resolveFileViewerRendererRedirectId } from "./dispatcher.js";
5
- import { createRendererRegistry } from "../registry/registry.js";
6
- import { getExtension, normalizeFileExtension } from "../source/index.js";
7
- import { applyFileViewerRenderReadinessState, } from "../source/loading.js";
8
- import { findFileViewerViewStateProvider } from "../features/document/dom/index.js";
9
- import { registerFileViewerGenericViewStateProvider } from "../features/document/viewState.js";
1
+ // Rendering orchestration layer.
2
+ //
3
+ // This module adapts registry entries into disposable renderer sessions. It is
4
+ // the only place that should bridge raw buffers, DOM targets, render contexts,
5
+ // export adapters, and readiness state.
6
+ import { DEFAULT_RENDERER_DEFINITIONS } from '../registry/formats.js';
7
+ import { waitForFileViewerNextPaint } from '../output/export.js';
8
+ import { createFileViewerRendererDispatcher } from './dispatcher.js';
9
+ import { resolveFileViewerRendererRedirectId } from './dispatcher.js';
10
+ import { createRendererRegistry } from '../registry/registry.js';
11
+ import { getExtension, normalizeFileExtension } from '../source/index.js';
12
+ import { applyFileViewerRenderReadinessState, } from '../source/loading.js';
13
+ import { findFileViewerViewStateProvider } from '../features/document/dom/index.js';
14
+ import { registerFileViewerGenericViewStateProvider } from '../features/document/viewState.js';
10
15
  export const FILE_VIEWER_RENDER_SESSION_DISPOSE_ERROR_MESSAGE = '预览内容卸载失败';
11
16
  export const DEFAULT_FILE_VIEWER_RENDER_TARGET_CLASS = 'file-render';
12
17
  export const FILE_VIEWER_RENDER_SURFACE_BACKGROUND_PROPERTY = '--file-viewer-render-surface-background';
@@ -19,6 +24,11 @@ export const normalizeFileViewerRenderSurfaceBackground = (value) => {
19
24
  const normalized = value.trim();
20
25
  return normalized && normalized.toLowerCase() !== 'auto' ? normalized : null;
21
26
  };
27
+ /**
28
+ * Keeps the public UI option and the renderer-facing CSS custom property in
29
+ * sync. Custom properties inherit through Shadow DOM, so setting this on the
30
+ * render host covers both scoped and shadow-isolated renderer targets.
31
+ */
22
32
  export const syncFileViewerRenderSurfaceBackground = (target, options) => {
23
33
  var _a;
24
34
  if (!target) {
@@ -151,6 +161,8 @@ export const appendFileViewerStyle = (target, css, options = {}) => {
151
161
  const shadowRoot = getFileViewerShadowRootForNode(target);
152
162
  const StyleSheet = shadowRoot ? getConstructableStyleSheet(shadowRoot) : null;
153
163
  if (options.adoptedStyleSheet && shadowRoot && StyleSheet) {
164
+ // Use the target document's constructor. A stylesheet constructed in the
165
+ // parent window cannot be adopted by a ShadowRoot owned by an iframe.
154
166
  const sheet = new StyleSheet();
155
167
  sheet.replaceSync(css);
156
168
  shadowRoot.adoptedStyleSheets = [...shadowRoot.adoptedStyleSheets, sheet];
@@ -1,3 +1,13 @@
1
+ /**
2
+ * Keep ordinary SVG styles while preventing imports and resource requests.
3
+ * Obfuscated URL tokens fail closed instead of depending on parser quirks.
4
+ */
1
5
  export declare const sanitizeFileViewerSvgCss: (value: string) => string;
6
+ /** Remove executable attributes and every SVG resource URL that can load data remotely. */
2
7
  export declare const sanitizeFileViewerSvgResources: (root: ParentNode) => void;
8
+ /**
9
+ * Mermaid 11 resolves image-node URLs with `new Image()` while rendering,
10
+ * before its SVG can be sanitized. Reject image metadata and explicit remote
11
+ * URLs before calling Mermaid so an untrusted diagram cannot initiate I/O.
12
+ */
3
13
  export declare const assertFileViewerMermaidSourceHasNoExternalResources: (source: string) => void;
@@ -44,6 +44,10 @@ const isSafeSvgCssUrl = (value) => {
44
44
  /^blob:/i.test(normalized) ||
45
45
  SAFE_EMBEDDED_IMAGE_DATA_URL.test(normalized);
46
46
  };
47
+ /**
48
+ * Keep ordinary SVG styles while preventing imports and resource requests.
49
+ * Obfuscated URL tokens fail closed instead of depending on parser quirks.
50
+ */
47
51
  export const sanitizeFileViewerSvgCss = (value) => {
48
52
  const normalizedCharacters = [];
49
53
  for (const character of value) {
@@ -89,6 +93,7 @@ export const sanitizeFileViewerSvgCss = (value) => {
89
93
  chunks.push(normalized.slice(segmentStart));
90
94
  return chunks.join('');
91
95
  };
96
+ /** Remove executable attributes and every SVG resource URL that can load data remotely. */
92
97
  export const sanitizeFileViewerSvgResources = (root) => {
93
98
  root.querySelectorAll('*').forEach(element => {
94
99
  for (const attribute of Array.from(element.attributes)) {
@@ -155,10 +160,17 @@ const canonicalizeMermaidSourceForResourceScan = (source) => {
155
160
  }
156
161
  return output;
157
162
  };
163
+ /**
164
+ * Mermaid 11 resolves image-node URLs with `new Image()` while rendering,
165
+ * before its SVG can be sanitized. Reject image metadata and explicit remote
166
+ * URLs before calling Mermaid so an untrusted diagram cannot initiate I/O.
167
+ */
158
168
  export const assertFileViewerMermaidSourceHasNoExternalResources = (source) => {
159
169
  const normalized = canonicalizeMermaidSourceForResourceScan(String(source || ''));
160
170
  const compact = normalized.replace(/[\t\r\n ]+/g, '');
161
171
  const hasImageMetadata = /(?:^|[,{])\s*(?:img|["']img["'])\s*:/im.test(normalized);
172
+ // HTML math labels are created by Mermaid before its SVG is returned. Reject
173
+ // loading elements even when their URL uses entities or a relative path.
162
174
  const hasLoadingElement = /<\s*(?:img|image|iframe|object|embed|video|audio|source|link|script|style)\b/i.test(normalized);
163
175
  const hasRemoteUrl = /(?:https?|ftp):\/\//i.test(compact) ||
164
176
  /(?:^|[\s"'(=:,])(?:\/\/|\\\\)[A-Za-z0-9]/m.test(normalized);
@@ -1,6 +1,6 @@
1
- import type { FileViewerFileRef, FileViewerSource, NormalizedFileViewerSource } from "../contracts/types.js";
2
- export { DEFAULT_FILE_VIEWER_TEXT_FALLBACK_ENCODING, createFileViewerTextDecoder, decodeFileViewerTextBuffer, isSingleByteFileViewerTextEncoding, isValidFileViewerUtf8, resolveFileViewerTextEncoding, } from "./textEncoding.js";
3
- export type { DecodedFileViewerText, FileViewerSingleByteTextEncoding, FileViewerTextEncoding, ResolvedFileViewerTextEncoding, ResolvedFileViewerTextSource, } from "./textEncoding.js";
1
+ import type { FileViewerFileRef, FileViewerSource, NormalizedFileViewerSource } from '../contracts/types.js';
2
+ export { DEFAULT_FILE_VIEWER_TEXT_FALLBACK_ENCODING, createFileViewerTextDecoder, decodeFileViewerTextBuffer, isSingleByteFileViewerTextEncoding, isValidFileViewerUtf8, resolveFileViewerTextEncoding, } from './textEncoding.js';
3
+ export type { DecodedFileViewerText, FileViewerSingleByteTextEncoding, FileViewerTextEncoding, ResolvedFileViewerTextEncoding, ResolvedFileViewerTextSource, } from './textEncoding.js';
4
4
  export type FileViewerReadResult = string | ArrayBuffer | undefined | null;
5
5
  export declare const DEFAULT_FILE_VIEWER_SOURCE_FILENAME = "preview.bin";
6
6
  export declare const normalizeFileExtension: (extension: string) => string;
@@ -1,4 +1,4 @@
1
- export { DEFAULT_FILE_VIEWER_TEXT_FALLBACK_ENCODING, createFileViewerTextDecoder, decodeFileViewerTextBuffer, isSingleByteFileViewerTextEncoding, isValidFileViewerUtf8, resolveFileViewerTextEncoding, } from "./textEncoding.js";
1
+ export { DEFAULT_FILE_VIEWER_TEXT_FALLBACK_ENCODING, createFileViewerTextDecoder, decodeFileViewerTextBuffer, isSingleByteFileViewerTextEncoding, isValidFileViewerUtf8, resolveFileViewerTextEncoding, } from './textEncoding.js';
2
2
  export const DEFAULT_FILE_VIEWER_SOURCE_FILENAME = 'preview.bin';
3
3
  export const normalizeFileExtension = (extension) => {
4
4
  return extension.trim().replace(/^\./, '').toLowerCase();
@@ -1,7 +1,7 @@
1
- import type { FileViewerFileRef, FileViewerLifecycleContext, FileViewerPdfOptions } from "../contracts/types.js";
2
- import { type FileViewerLifecycleStateController } from "../lifecycle/operations.js";
3
- import { type FileViewerErrorMessageFormatter } from "../viewer/state.js";
4
- import type { FileViewerI18nInput } from "../i18n/messages.js";
1
+ import type { FileViewerFileRef, FileViewerLifecycleContext, FileViewerPdfOptions } from '../contracts/types.js';
2
+ import { type FileViewerLifecycleStateController } from '../lifecycle/operations.js';
3
+ import { type FileViewerErrorMessageFormatter } from '../viewer/state.js';
4
+ import type { FileViewerI18nInput } from '../i18n/messages.js';
5
5
  export declare const DEFAULT_PDF_RANGE_CHUNK_SIZE: number;
6
6
  export declare const DEFAULT_FILE_VIEWER_STREAMING_PDF_FILENAME = "preview.pdf";
7
7
  export declare const FILE_VIEWER_REMOTE_MISSING_DATA_ERROR_MESSAGE = "\u6587\u4EF6\u4E0B\u8F7D\u5931\u8D25";
@@ -314,6 +314,8 @@ export declare const reportFileViewerPreviewLoadError: ({ onLogError, onErrorMes
314
314
  export declare const reportFileViewerMissingRemoteData: ({ onErrorMessage, ...messageInput }?: ReportFileViewerMissingRemoteDataInput) => string;
315
315
  export interface RunFileViewerRemoteFilePreviewInput<Session = unknown> {
316
316
  url: string;
317
+ filename?: string;
318
+ type?: string;
317
319
  version: number;
318
320
  pageHref?: string;
319
321
  streaming?: FileViewerPdfOptions['streaming'];
@@ -471,6 +473,8 @@ export interface CreateFileViewerSourceLoadingActionHandlersInput<Session = unkn
471
473
  getFile: () => FileViewerFileRef | null | undefined;
472
474
  getUrl: () => string | null | undefined;
473
475
  getCurrentFilename?: () => string | undefined;
476
+ getSourceFilename?: () => string | undefined;
477
+ getSourceType?: () => string | undefined;
474
478
  shouldStreamLocalFile?: (input: {
475
479
  file: File;
476
480
  filename: string;
@@ -550,8 +554,8 @@ export declare const commitFileViewerRenderCompleteState: <Session = unknown>({
550
554
  export declare const runFileViewerReadAndRenderFile: <Session = unknown>({ file, version, signal, sourceUrl, source, fallbackFilename, previewTarget, isCurrent, mountRenderedContent, destroyRenderSession, buildRenderCompleteState, onSession, onActiveDocumentContext, onLifecycle, onClearLoadStarted, }: RunFileViewerReadAndRenderFileInput<Session>) => Promise<FileViewerReadAndRenderFileState<Session>>;
551
555
  export declare const runFileViewerStreamingPdfPreview: <Session = unknown>({ url, version, filename, previewTarget, isCurrent, mountRenderedContent, destroyRenderSession, buildRenderCompleteState, loadingMessage, signal, typeOverride, i18n, onStartLoading, onSession, onActiveDocumentContext, onLifecycle, onClearLoadStarted, onStopLoading, onError, }: RunFileViewerStreamingPdfPreviewInput<Session>) => Promise<FileViewerStreamingPdfPreviewState<Session>>;
552
556
  export declare const runFileViewerLocalFilePreview: <Session = unknown>({ source, version, currentFilename, fallbackFilename, shouldStreamLocalFile, requestController, previewTarget, isCurrent, mountRenderedContent, destroyRenderSession, buildLoadStartState, buildRenderCompleteState, onMarkLoadStarted, onStartLoading, onSession, onActiveDocumentContext, onLifecycle, onClearLoadStarted, onStopLoading, onError, }: RunFileViewerLocalFilePreviewInput<Session>) => Promise<FileViewerLocalFilePreviewState<Session>>;
553
- export declare const runFileViewerRemoteFilePreview: <Session = unknown>({ url, version, pageHref, streaming, shouldStreamRemoteUrl, previewTarget, requestController, isCurrent, downloadFile, mountRenderedContent, destroyRenderSession, buildLoadStartState, buildRenderCompleteState, i18n, onMarkLoadStarted, onStartLoading, onSetLoadingMessage, onSession, onActiveDocumentContext, onLifecycle, onClearLoadStarted, onStopLoading, onMissingData, onError, }: RunFileViewerRemoteFilePreviewInput<Session>) => Promise<FileViewerRemoteFilePreviewState<Session>>;
554
- export declare const createFileViewerSourceLoadingActionHandlers: <Session = unknown>({ getFile, getUrl, getCurrentFilename, shouldStreamLocalFile, getPdfStreaming, shouldStreamRemoteUrl, getI18n, getPageHref, previewTarget, requestController, downloadFile, mountRenderedContent, destroyRenderSession, buildLoadStartState, buildRenderCompleteState, formatErrorMessage, onMarkLoadStarted, onClearLoadStarted, onStartLoading, onSetLoadingMessage, onStopLoading, onShowError, onClearError, onResetLoading, onClearRenderedContent, onSession, onActiveDocumentContext, onLifecycle, }: CreateFileViewerSourceLoadingActionHandlersInput<Session>) => FileViewerSourceLoadingActionHandlers<Session>;
557
+ export declare const runFileViewerRemoteFilePreview: <Session = unknown>({ url, filename, type, version, pageHref, streaming, shouldStreamRemoteUrl, previewTarget, requestController, isCurrent, downloadFile, mountRenderedContent, destroyRenderSession, buildLoadStartState, buildRenderCompleteState, i18n, onMarkLoadStarted, onStartLoading, onSetLoadingMessage, onSession, onActiveDocumentContext, onLifecycle, onClearLoadStarted, onStopLoading, onMissingData, onError, }: RunFileViewerRemoteFilePreviewInput<Session>) => Promise<FileViewerRemoteFilePreviewState<Session>>;
558
+ export declare const createFileViewerSourceLoadingActionHandlers: <Session = unknown>({ getFile, getUrl, getCurrentFilename, getSourceFilename, getSourceType, shouldStreamLocalFile, getPdfStreaming, shouldStreamRemoteUrl, getI18n, getPageHref, previewTarget, requestController, downloadFile, mountRenderedContent, destroyRenderSession, buildLoadStartState, buildRenderCompleteState, formatErrorMessage, onMarkLoadStarted, onClearLoadStarted, onStartLoading, onSetLoadingMessage, onStopLoading, onShowError, onClearError, onResetLoading, onClearRenderedContent, onSession, onActiveDocumentContext, onLifecycle, }: CreateFileViewerSourceLoadingActionHandlersInput<Session>) => FileViewerSourceLoadingActionHandlers<Session>;
555
559
  export declare const finalizeFileViewerPreviewLoadState: ({ version, isCurrent, onClearLoadStarted, onStopLoading, }: FinalizeFileViewerPreviewLoadStateInput) => void;
556
560
  export declare const resolveFileViewerLoadStartMessage: (source: FileViewerLifecycleContext["source"], i18n?: FileViewerI18nInput) => string;
557
561
  export declare const commitFileViewerLoadStartState: ({ version, filename, fallbackFilename, filenameTarget, buildState, onMarkLoadStarted, onLifecycle, onStartLoading, }: CommitFileViewerLoadStartStateInput) => FileViewerLoadStartState;
@@ -601,8 +605,9 @@ export interface FileViewerLocationLike {
601
605
  href?: string | null;
602
606
  }
603
607
  export declare const resolveFileViewerPageHref: (locationLike?: FileViewerLocationLike) => string | undefined;
604
- export declare const resolveFileViewerRemoteSourcePlan: ({ filename, fallbackFilename, pageHref, streaming, shouldStreamRemoteUrl, url, }: {
608
+ export declare const resolveFileViewerRemoteSourcePlan: ({ filename, type, fallbackFilename, pageHref, streaming, shouldStreamRemoteUrl, url, }: {
605
609
  filename?: string;
610
+ type?: string;
606
611
  fallbackFilename?: string;
607
612
  pageHref?: string;
608
613
  streaming?: FileViewerPdfOptions["streaming"];
@@ -1,7 +1,7 @@
1
- import { buildFileViewerLifecycleContext, } from "../lifecycle/operations.js";
2
- import { resolveFileViewerPreviewMessages, } from "../viewer/state.js";
3
- import { translateFileViewerMessage } from "../i18n/messages.js";
4
- import { DEFAULT_FILE_VIEWER_SOURCE_FILENAME, getExtension, normalizeFilename, readFileViewerBuffer, resolveFileViewerSourceFilename, wrapFileViewerFileRef, } from "./index.js";
1
+ import { buildFileViewerLifecycleContext, } from '../lifecycle/operations.js';
2
+ import { resolveFileViewerPreviewMessages, } from '../viewer/state.js';
3
+ import { translateFileViewerMessage } from '../i18n/messages.js';
4
+ import { DEFAULT_FILE_VIEWER_SOURCE_FILENAME, getExtension, normalizeFilename, normalizeFileExtension, readFileViewerBuffer, resolveFileViewerSourceFilename, wrapFileViewerFileRef, } from './index.js';
5
5
  export const DEFAULT_PDF_RANGE_CHUNK_SIZE = 64 * 1024;
6
6
  export const DEFAULT_FILE_VIEWER_STREAMING_PDF_FILENAME = 'preview.pdf';
7
7
  export const FILE_VIEWER_REMOTE_MISSING_DATA_ERROR_MESSAGE = '文件下载失败';
@@ -117,6 +117,7 @@ const encodeFileViewerUrlPathSegment = (segment) => {
117
117
  decoded = decodeURIComponent(segment);
118
118
  }
119
119
  catch {
120
+ // Invalid percent escapes are treated as literal filename characters.
120
121
  }
121
122
  return encodeURIComponent(decoded)
122
123
  .replace(/%3A/gi, ':')
@@ -596,14 +597,18 @@ export const runFileViewerLocalFilePreview = async ({ source, version, currentFi
596
597
  });
597
598
  }
598
599
  };
599
- export const runFileViewerRemoteFilePreview = async ({ url, version, pageHref, streaming, shouldStreamRemoteUrl, previewTarget, requestController, isCurrent, downloadFile, mountRenderedContent, destroyRenderSession, buildLoadStartState, buildRenderCompleteState, i18n, onMarkLoadStarted, onStartLoading, onSetLoadingMessage, onSession, onActiveDocumentContext, onLifecycle, onClearLoadStarted, onStopLoading, onMissingData, onError, }) => {
600
+ export const runFileViewerRemoteFilePreview = async ({ url, filename, type, version, pageHref, streaming, shouldStreamRemoteUrl, previewTarget, requestController, isCurrent, downloadFile, mountRenderedContent, destroyRenderSession, buildLoadStartState, buildRenderCompleteState, i18n, onMarkLoadStarted, onStartLoading, onSetLoadingMessage, onSession, onActiveDocumentContext, onLifecycle, onClearLoadStarted, onStopLoading, onMissingData, onError, }) => {
600
601
  let remoteSource = resolveFileViewerRemoteSourcePlan({
602
+ filename,
603
+ type,
601
604
  pageHref,
602
605
  streaming,
603
606
  url,
604
607
  });
605
608
  const sourceUrl = remoteSource.url;
606
609
  const controller = requestController.createAbortController();
610
+ // The streaming helper finalizes its own load state; probe/download paths
611
+ // are finalized here, including cancellation before a renderer is mounted.
607
612
  let streamFinalized = false;
608
613
  try {
609
614
  if (!remoteSource.streamPdf && shouldStreamRemoteUrl) {
@@ -664,7 +669,7 @@ export const runFileViewerRemoteFilePreview = async ({ url, version, pageHref, s
664
669
  ? undefined
665
670
  : resolveFileViewerPreviewMessages(i18n).downloading,
666
671
  signal: controller === null || controller === void 0 ? void 0 : controller.signal,
667
- typeOverride: remoteSource.streamType,
672
+ typeOverride: remoteSource.streamPdf ? 'pdf' : remoteSource.streamType,
668
673
  onStartLoading,
669
674
  onSession,
670
675
  onActiveDocumentContext,
@@ -808,7 +813,7 @@ export const runFileViewerRemoteFilePreview = async ({ url, version, pageHref, s
808
813
  }
809
814
  }
810
815
  };
811
- export const createFileViewerSourceLoadingActionHandlers = ({ getFile, getUrl, getCurrentFilename, shouldStreamLocalFile, getPdfStreaming, shouldStreamRemoteUrl, getI18n, getPageHref, previewTarget, requestController, downloadFile, mountRenderedContent, destroyRenderSession, buildLoadStartState, buildRenderCompleteState, formatErrorMessage, onMarkLoadStarted, onClearLoadStarted, onStartLoading, onSetLoadingMessage, onStopLoading, onShowError, onClearError, onResetLoading, onClearRenderedContent, onSession, onActiveDocumentContext, onLifecycle, }) => {
816
+ export const createFileViewerSourceLoadingActionHandlers = ({ getFile, getUrl, getCurrentFilename, getSourceFilename, getSourceType, shouldStreamLocalFile, getPdfStreaming, shouldStreamRemoteUrl, getI18n, getPageHref, previewTarget, requestController, downloadFile, mountRenderedContent, destroyRenderSession, buildLoadStartState, buildRenderCompleteState, formatErrorMessage, onMarkLoadStarted, onClearLoadStarted, onStartLoading, onSetLoadingMessage, onStopLoading, onShowError, onClearError, onResetLoading, onClearRenderedContent, onSession, onActiveDocumentContext, onLifecycle, }) => {
812
817
  const isCurrentRequest = (version) => requestController.isCurrent(version);
813
818
  const previewLocalFile = async (source, version) => {
814
819
  var _a;
@@ -853,6 +858,8 @@ export const createFileViewerSourceLoadingActionHandlers = ({ getFile, getUrl, g
853
858
  const previewRemoteFile = async (url, version) => {
854
859
  return await runFileViewerRemoteFilePreview({
855
860
  url,
861
+ filename: getSourceFilename === null || getSourceFilename === void 0 ? void 0 : getSourceFilename(),
862
+ type: getSourceType === null || getSourceType === void 0 ? void 0 : getSourceType(),
856
863
  version,
857
864
  pageHref: getPageHref === null || getPageHref === void 0 ? void 0 : getPageHref(),
858
865
  streaming: getPdfStreaming === null || getPdfStreaming === void 0 ? void 0 : getPdfStreaming(),
@@ -1034,18 +1041,16 @@ export const shouldStreamPdfUrl = ({ extension, pageHref, streaming, url, }) =>
1034
1041
  export const resolveFileViewerPageHref = (locationLike) => {
1035
1042
  return (locationLike === null || locationLike === void 0 ? void 0 : locationLike.href) || undefined;
1036
1043
  };
1037
- export const resolveFileViewerRemoteSourcePlan = ({ filename, fallbackFilename = DEFAULT_FILE_VIEWER_SOURCE_FILENAME, pageHref, streaming, shouldStreamRemoteUrl, url, }) => {
1044
+ export const resolveFileViewerRemoteSourcePlan = ({ filename, type, fallbackFilename = DEFAULT_FILE_VIEWER_SOURCE_FILENAME, pageHref, streaming, shouldStreamRemoteUrl, url, }) => {
1038
1045
  const sourceUrl = normalizeFileViewerSourceUrl(url) || url;
1039
1046
  const nextFilename = normalizeFilename(filename || url, fallbackFilename);
1040
- const extension = getExtension(nextFilename);
1041
- const streamPdf = pageHref
1042
- ? shouldStreamPdfUrl({
1043
- extension,
1044
- pageHref,
1045
- streaming,
1046
- url: sourceUrl,
1047
- })
1048
- : false;
1047
+ const extension = normalizeFileExtension(type || getExtension(nextFilename));
1048
+ const streamPdf = shouldStreamPdfUrl({
1049
+ extension,
1050
+ pageHref: pageHref || '',
1051
+ streaming,
1052
+ url: sourceUrl,
1053
+ });
1049
1054
  const streamDecision = !streamPdf
1050
1055
  ? shouldStreamRemoteUrl === null || shouldStreamRemoteUrl === void 0 ? void 0 : shouldStreamRemoteUrl({
1051
1056
  url: sourceUrl,
@@ -1,10 +1,14 @@
1
- import type { FileViewerFileRef, FileViewerSource } from "../contracts/types.js";
1
+ import type { FileViewerFileRef, FileViewerSource } from '../contracts/types.js';
2
2
  export type FileViewerPrecheckStatus = 'ready' | 'invalid' | 'unsupported';
3
3
  export type FileViewerPrecheckReason = 'content-valid' | 'content-not-inspected' | 'content-inconclusive' | 'empty-structured-file' | 'signature-mismatch' | 'invalid-package' | 'missing-package-part' | 'unsupported-extension';
4
4
  export interface FileViewerPrecheckOptions {
5
+ /** Filename used when a Blob or ArrayBuffer has no name. */
5
6
  filename?: string;
7
+ /** Explicit extension, with or without a leading dot. */
6
8
  type?: string;
9
+ /** Restricts the result to the renderers installed by the host application. */
7
10
  supportedExtensions?: Iterable<string>;
11
+ /** Maximum ZIP central-directory bytes read from a Blob. Defaults to 8 MiB. */
8
12
  maxPackageMetadataBytes?: number;
9
13
  }
10
14
  export interface FileViewerPrecheckResult {
@@ -13,6 +17,7 @@ export interface FileViewerPrecheckResult {
13
17
  rendererId: string | null;
14
18
  supported: boolean;
15
19
  inspected: boolean;
20
+ /** `null` means the extension is supported but no structural validator exists. */
16
21
  valid: boolean | null;
17
22
  previewable: boolean;
18
23
  status: FileViewerPrecheckStatus;
@@ -20,5 +25,12 @@ export interface FileViewerPrecheckResult {
20
25
  missingParts: string[];
21
26
  }
22
27
  type FileViewerPrecheckInput = FileViewerSource | FileViewerFileRef;
28
+ /**
29
+ * Performs a cheap, renderer-free capability and structure check.
30
+ *
31
+ * This intentionally does not promise rendering fidelity. A `ready` result
32
+ * means the extension is available and any known container/signature checks
33
+ * passed; the real renderer remains the final authority for malformed content.
34
+ */
23
35
  export declare const precheckFileViewerSource: (input: FileViewerPrecheckInput, options?: FileViewerPrecheckOptions) => Promise<FileViewerPrecheckResult>;
24
36
  export {};
@@ -1,5 +1,5 @@
1
- import { DEFAULT_RENDERER_DEFINITIONS, DEFAULT_SUPPORTED_EXTENSIONS } from "../registry/formats.js";
2
- import { getExtension, normalizeFileExtension, normalizeFilename, normalizeSource, } from "./index.js";
1
+ import { DEFAULT_RENDERER_DEFINITIONS, DEFAULT_SUPPORTED_EXTENSIONS } from '../registry/formats.js';
2
+ import { getExtension, normalizeFileExtension, normalizeFilename, normalizeSource, } from './index.js';
3
3
  const DEFAULT_MAX_PACKAGE_METADATA_BYTES = 8 * 1024 * 1024;
4
4
  const ZIP_LOCAL_SIGNATURE = 0x04034b50;
5
5
  const ZIP_CENTRAL_SIGNATURE = 0x02014b50;
@@ -203,6 +203,13 @@ const inspectStructuredContent = async (source, extension, maxPackageMetadataByt
203
203
  }
204
204
  return { inspected: false, valid: null, reason: 'content-not-inspected' };
205
205
  };
206
+ /**
207
+ * Performs a cheap, renderer-free capability and structure check.
208
+ *
209
+ * This intentionally does not promise rendering fidelity. A `ready` result
210
+ * means the extension is available and any known container/signature checks
211
+ * passed; the real renderer remains the final authority for malformed content.
212
+ */
206
213
  export const precheckFileViewerSource = async (input, options = {}) => {
207
214
  var _a;
208
215
  const source = toSource(input, options);
@@ -1,6 +1,15 @@
1
+ /**
2
+ * Single-byte legacy encodings supported by the browser TextDecoder API.
3
+ * Each byte decodes to one UTF-16 code unit, so the virtual
4
+ * large-text renderer can slice them at any byte offset.
5
+ */
1
6
  export type FileViewerSingleByteTextEncoding = 'iso-8859-1' | 'iso-8859-2' | 'iso-8859-15' | 'windows-1250' | 'windows-1251' | 'windows-1252';
2
7
  export type FileViewerTextEncoding = 'auto' | 'utf-8' | 'utf-16le' | 'utf-16be' | 'gbk' | 'gb18030' | FileViewerSingleByteTextEncoding;
3
8
  export type ResolvedFileViewerTextEncoding = Exclude<FileViewerTextEncoding, 'auto' | 'gbk'>;
9
+ /**
10
+ * Encoding used by `auto` when the bytes carry no BOM, do not look like
11
+ * UTF-16, and are not valid UTF-8. GB18030 keeps the historical behavior.
12
+ */
4
13
  export declare const DEFAULT_FILE_VIEWER_TEXT_FALLBACK_ENCODING: ResolvedFileViewerTextEncoding;
5
14
  export declare const isSingleByteFileViewerTextEncoding: (encoding: string | undefined) => encoding is FileViewerSingleByteTextEncoding;
6
15
  export interface DecodedFileViewerText {
@@ -12,6 +21,15 @@ export interface ResolvedFileViewerTextSource {
12
21
  bomLength: number;
13
22
  }
14
23
  export declare const isValidFileViewerUtf8: (bytes: Uint8Array) => boolean;
24
+ /**
25
+ * Picks the decoder for a text buffer.
26
+ *
27
+ * `encoding` other than `auto` is used as-is. In `auto` mode the order is:
28
+ * BOM, UTF-16 byte structure, strict UTF-8, then `fallbackEncoding`. The
29
+ * fallback defaults to GB18030; pass `windows-1252`, `windows-1251`, or
30
+ * another supported label for Latin, Cyrillic, or Central European legacy
31
+ * files, which are otherwise misread as CJK text.
32
+ */
15
33
  export declare const resolveFileViewerTextEncoding: (bytes: Uint8Array, encoding?: FileViewerTextEncoding | string, fallbackEncoding?: FileViewerTextEncoding | string) => ResolvedFileViewerTextSource;
16
34
  export declare const createFileViewerTextDecoder: (encoding: ResolvedFileViewerTextEncoding) => TextDecoder;
17
35
  export declare const decodeFileViewerTextBuffer: (data: ArrayBuffer, encoding?: FileViewerTextEncoding | string, fallbackEncoding?: FileViewerTextEncoding | string) => DecodedFileViewerText;
@@ -1,3 +1,7 @@
1
+ /**
2
+ * Encoding used by `auto` when the bytes carry no BOM, do not look like
3
+ * UTF-16, and are not valid UTF-8. GB18030 keeps the historical behavior.
4
+ */
1
5
  export const DEFAULT_FILE_VIEWER_TEXT_FALLBACK_ENCODING = 'gb18030';
2
6
  const SINGLE_BYTE_ENCODING_ALIASES = new Map([
3
7
  ['iso-8859-1', 'iso-8859-1'],
@@ -72,6 +76,8 @@ const normalizeFallbackEncoding = (fallbackEncoding) => {
72
76
  const isContinuationByte = (value) => {
73
77
  return value !== undefined && value >= 0x80 && value <= 0xbf;
74
78
  };
79
+ // Validate bytes directly because older WebViews can accept the fatal option
80
+ // while still replacing malformed input. That would hide the GB18030 fallback.
75
81
  export const isValidFileViewerUtf8 = (bytes) => {
76
82
  for (let index = 0; index < bytes.length;) {
77
83
  const first = bytes[index];
@@ -152,6 +158,15 @@ const inferBomlessUtf16 = (bytes) => {
152
158
  }
153
159
  return null;
154
160
  };
161
+ /**
162
+ * Picks the decoder for a text buffer.
163
+ *
164
+ * `encoding` other than `auto` is used as-is. In `auto` mode the order is:
165
+ * BOM, UTF-16 byte structure, strict UTF-8, then `fallbackEncoding`. The
166
+ * fallback defaults to GB18030; pass `windows-1252`, `windows-1251`, or
167
+ * another supported label for Latin, Cyrillic, or Central European legacy
168
+ * files, which are otherwise misread as CJK text.
169
+ */
155
170
  export const resolveFileViewerTextEncoding = (bytes, encoding = 'auto', fallbackEncoding = DEFAULT_FILE_VIEWER_TEXT_FALLBACK_ENCODING) => {
156
171
  const normalized = normalizeEncoding(encoding);
157
172
  if (normalized !== 'auto') {
@@ -1,4 +1,4 @@
1
- import type { FileViewerEventHandler, FileViewerInstance, FileViewerOptions, FileViewerRenderPurpose, RendererRegistry } from "../contracts/types.js";
1
+ import type { FileViewerEventHandler, FileViewerInstance, FileViewerOptions, FileViewerRenderPurpose, RendererRegistry } from '../contracts/types.js';
2
2
  export interface CreateViewerOptions {
3
3
  registry?: RendererRegistry;
4
4
  options?: FileViewerOptions;