@file-viewer/core 2.1.17 → 2.1.18

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.
@@ -13,6 +13,48 @@ import { findFileViewerViewStateProvider } from '../features/document/dom/index.
13
13
  import { registerFileViewerGenericViewStateProvider } from '../features/document/viewState.js';
14
14
  export const FILE_VIEWER_RENDER_SESSION_DISPOSE_ERROR_MESSAGE = '预览内容卸载失败';
15
15
  export const DEFAULT_FILE_VIEWER_RENDER_TARGET_CLASS = 'file-render';
16
+ const FILE_VIEWER_RENDER_HOST_CLASS = 'file-render-host';
17
+ const FILE_VIEWER_SHADOW_RENDER_TARGET_CLASS = 'file-render-shadow-target';
18
+ export const isFileViewerShadowRoot = (value) => {
19
+ if (!value || typeof value !== 'object') {
20
+ return false;
21
+ }
22
+ if (typeof ShadowRoot !== 'undefined' && value instanceof ShadowRoot) {
23
+ return true;
24
+ }
25
+ return value.nodeType === 11 &&
26
+ !!value.host;
27
+ };
28
+ const getElementRootNode = (element) => {
29
+ var _a;
30
+ return ((_a = element === null || element === void 0 ? void 0 : element.getRootNode) === null || _a === void 0 ? void 0 : _a.call(element)) || null;
31
+ };
32
+ export const getFileViewerShadowRootForNode = (node) => {
33
+ var _a;
34
+ if (!node) {
35
+ return null;
36
+ }
37
+ if (isFileViewerShadowRoot(node)) {
38
+ return node;
39
+ }
40
+ const root = (_a = node.getRootNode) === null || _a === void 0 ? void 0 : _a.call(node);
41
+ return isFileViewerShadowRoot(root) ? root : null;
42
+ };
43
+ export const normalizeFileViewerStyleIsolation = (isolation) => {
44
+ return isolation === 'shadow' || isolation === 'scoped' || isolation === 'none'
45
+ ? isolation
46
+ : 'auto';
47
+ };
48
+ export const resolveFileViewerStyleIsolation = (options, container) => {
49
+ const isolation = normalizeFileViewerStyleIsolation(options === null || options === void 0 ? void 0 : options.styleIsolation);
50
+ if (isolation === 'auto') {
51
+ return isFileViewerShadowRoot(getElementRootNode(container)) ? 'scoped' : 'none';
52
+ }
53
+ if (isolation === 'shadow' && isFileViewerShadowRoot(getElementRootNode(container))) {
54
+ return 'scoped';
55
+ }
56
+ return isolation;
57
+ };
16
58
  const isPromiseLike = (value) => {
17
59
  return !!value &&
18
60
  (typeof value === 'object' || typeof value === 'function') &&
@@ -35,6 +77,76 @@ export const createFileViewerRenderTarget = (container, options = {}) => {
35
77
  container.appendChild(target);
36
78
  return target;
37
79
  };
80
+ export const createFileViewerRenderSurface = (container, options = {}) => {
81
+ const styleIsolation = resolveFileViewerStyleIsolation({
82
+ styleIsolation: options.styleIsolation,
83
+ }, container);
84
+ const className = options.className || DEFAULT_FILE_VIEWER_RENDER_TARGET_CLASS;
85
+ if (styleIsolation === 'shadow' && typeof container.attachShadow === 'function') {
86
+ const host = container.ownerDocument.createElement('div');
87
+ host.className = `${FILE_VIEWER_RENDER_HOST_CLASS} ${className}`;
88
+ host.dataset.fileViewerStyleIsolation = 'shadow';
89
+ host.style.width = '100%';
90
+ host.style.minWidth = '0';
91
+ host.style.minHeight = '0';
92
+ const shadowRoot = host.attachShadow({ mode: 'open' });
93
+ const target = container.ownerDocument.createElement('div');
94
+ target.className = `${FILE_VIEWER_SHADOW_RENDER_TARGET_CLASS} ${className}`;
95
+ target.dataset.fileViewerStyleIsolation = 'shadow';
96
+ target.style.width = '100%';
97
+ target.style.minWidth = '0';
98
+ target.style.minHeight = '0';
99
+ shadowRoot.appendChild(target);
100
+ container.appendChild(host);
101
+ return {
102
+ host,
103
+ container: target,
104
+ shadowRoot,
105
+ styleIsolation,
106
+ };
107
+ }
108
+ const target = createFileViewerRenderTarget(container, { className });
109
+ target.dataset.fileViewerStyleIsolation = styleIsolation;
110
+ return {
111
+ host: target,
112
+ container: target,
113
+ styleIsolation,
114
+ };
115
+ };
116
+ const appendStyleElement = (target, css) => {
117
+ const documentRef = target.ownerDocument;
118
+ const style = documentRef.createElement('style');
119
+ style.textContent = css;
120
+ target.appendChild(style);
121
+ return style;
122
+ };
123
+ const canUseAdoptedStyleSheet = (root) => {
124
+ return 'adoptedStyleSheets' in root &&
125
+ typeof CSSStyleSheet !== 'undefined' &&
126
+ typeof CSSStyleSheet.prototype.replaceSync === 'function';
127
+ };
128
+ export const appendFileViewerStyle = (target, css, options = {}) => {
129
+ const shadowRoot = getFileViewerShadowRootForNode(target);
130
+ if (options.adoptedStyleSheet && shadowRoot && canUseAdoptedStyleSheet(shadowRoot)) {
131
+ const sheet = new CSSStyleSheet();
132
+ sheet.replaceSync(css);
133
+ shadowRoot.adoptedStyleSheets = [...shadowRoot.adoptedStyleSheets, sheet];
134
+ return {
135
+ sheet,
136
+ remove() {
137
+ shadowRoot.adoptedStyleSheets = shadowRoot.adoptedStyleSheets.filter(item => item !== sheet);
138
+ },
139
+ };
140
+ }
141
+ const styleTarget = isFileViewerShadowRoot(target) ? target : target;
142
+ const node = appendStyleElement(styleTarget, css);
143
+ return {
144
+ node,
145
+ remove() {
146
+ node.remove();
147
+ },
148
+ };
149
+ };
38
150
  export const removeFileViewerRenderTarget = (container, target) => {
39
151
  if (target.parentNode !== container) {
40
152
  return false;
@@ -161,7 +273,7 @@ export const resetFileViewerRenderSurface = ({ surfaceState, readinessState, con
161
273
  clearFileViewerRenderSurface(container);
162
274
  return session;
163
275
  };
164
- export const runFileViewerRenderSurfaceClear = ({ reason = 'replace', surfaceState, readinessState, container, disposeOptions, onUnloadStart, onUnloadComplete, onClearActiveDocumentContext, onClearDocumentState, onStopZoomObserver, onClearZoomProvider, onStopViewStateObserver, onClearViewStateProvider, }) => {
276
+ export const runFileViewerRenderSurfaceClear = ({ reason = 'replace', surfaceState, readinessState, container, disposeOptions, onUnloadStart, onUnloadComplete, onClearActiveDocumentContext, onClearDocumentState, onStopZoomObserver, onClearZoomProvider, onStopFitObserver, onStopViewStateObserver, onClearViewStateProvider, }) => {
165
277
  const unloadContext = onUnloadStart === null || onUnloadStart === void 0 ? void 0 : onUnloadStart(reason);
166
278
  let session;
167
279
  try {
@@ -177,6 +289,7 @@ export const runFileViewerRenderSurfaceClear = ({ reason = 'replace', surfaceSta
177
289
  onClearDocumentState === null || onClearDocumentState === void 0 ? void 0 : onClearDocumentState();
178
290
  onStopZoomObserver === null || onStopZoomObserver === void 0 ? void 0 : onStopZoomObserver();
179
291
  onClearZoomProvider === null || onClearZoomProvider === void 0 ? void 0 : onClearZoomProvider();
292
+ onStopFitObserver === null || onStopFitObserver === void 0 ? void 0 : onStopFitObserver();
180
293
  onStopViewStateObserver === null || onStopViewStateObserver === void 0 ? void 0 : onStopViewStateObserver();
181
294
  onClearViewStateProvider === null || onClearViewStateProvider === void 0 ? void 0 : onClearViewStateProvider();
182
295
  }
@@ -187,7 +300,7 @@ export const runFileViewerRenderSurfaceClear = ({ reason = 'replace', surfaceSta
187
300
  session,
188
301
  };
189
302
  };
190
- export const runFileViewerRenderSurfaceMount = async ({ buffer, file, version, sourceUrl, streamUrl, getContainer, surfaceState, readinessState, isCurrent, clearRenderedContent, render, waitForContainer, waitForPaint = waitForFileViewerNextPaint, disposeSession = disposeFileViewerRendererSession, onStartZoomObserver, onStartViewStateObserver, onRefreshDocumentIndex, onRefreshZoomProvider, onRefreshViewStateProvider, }) => {
303
+ export const runFileViewerRenderSurfaceMount = async ({ buffer, file, version, sourceUrl, streamUrl, getContainer, surfaceState, readinessState, isCurrent, clearRenderedContent, render, waitForContainer, waitForPaint = waitForFileViewerNextPaint, disposeSession = disposeFileViewerRendererSession, onStartZoomObserver, onStartFitObserver, onStartViewStateObserver, onApplyInitialFit, onRefreshDocumentIndex, onRefreshZoomProvider, onRefreshViewStateProvider, }) => {
191
304
  if (!getContainer()) {
192
305
  await (waitForContainer === null || waitForContainer === void 0 ? void 0 : waitForContainer());
193
306
  }
@@ -198,6 +311,7 @@ export const runFileViewerRenderSurfaceMount = async ({ buffer, file, version, s
198
311
  clearRenderedContent('replace');
199
312
  const target = createFileViewerRenderTarget(container);
200
313
  onStartZoomObserver === null || onStartZoomObserver === void 0 ? void 0 : onStartZoomObserver();
314
+ onStartFitObserver === null || onStartFitObserver === void 0 ? void 0 : onStartFitObserver();
201
315
  onStartViewStateObserver === null || onStartViewStateObserver === void 0 ? void 0 : onStartViewStateObserver();
202
316
  await (waitForContainer === null || waitForContainer === void 0 ? void 0 : waitForContainer());
203
317
  await waitForPaint();
@@ -236,6 +350,11 @@ export const runFileViewerRenderSurfaceMount = async ({ buffer, file, version, s
236
350
  void (onRefreshDocumentIndex === null || onRefreshDocumentIndex === void 0 ? void 0 : onRefreshDocumentIndex());
237
351
  onRefreshZoomProvider === null || onRefreshZoomProvider === void 0 ? void 0 : onRefreshZoomProvider();
238
352
  onRefreshViewStateProvider === null || onRefreshViewStateProvider === void 0 ? void 0 : onRefreshViewStateProvider();
353
+ if (onApplyInitialFit) {
354
+ await onApplyInitialFit();
355
+ onRefreshZoomProvider === null || onRefreshZoomProvider === void 0 ? void 0 : onRefreshZoomProvider();
356
+ onRefreshViewStateProvider === null || onRefreshViewStateProvider === void 0 ? void 0 : onRefreshViewStateProvider();
357
+ }
239
358
  return session;
240
359
  }
241
360
  catch (error) {
@@ -243,7 +362,7 @@ export const runFileViewerRenderSurfaceMount = async ({ buffer, file, version, s
243
362
  throw error;
244
363
  }
245
364
  };
246
- export const createFileViewerRenderSurfaceActionHandlers = ({ getContainer, surfaceState, readinessState, isCurrent, render, waitForContainer, waitForPaint, disposeOptions, onUnloadStart, onUnloadComplete, onClearActiveDocumentContext, onClearDocumentState, onStartZoomObserver, onStopZoomObserver, onClearZoomProvider, onStartViewStateObserver, onStopViewStateObserver, onClearViewStateProvider, onRefreshDocumentIndex, onRefreshZoomProvider, onRefreshViewStateProvider, }) => {
365
+ export const createFileViewerRenderSurfaceActionHandlers = ({ getContainer, surfaceState, readinessState, isCurrent, render, waitForContainer, waitForPaint, disposeOptions, onUnloadStart, onUnloadComplete, onClearActiveDocumentContext, onClearDocumentState, onStartZoomObserver, onStopZoomObserver, onClearZoomProvider, onStartFitObserver, onStopFitObserver, onStartViewStateObserver, onStopViewStateObserver, onClearViewStateProvider, onApplyInitialFit, onRefreshDocumentIndex, onRefreshZoomProvider, onRefreshViewStateProvider, }) => {
247
366
  const handleDisposeError = (error) => {
248
367
  if (disposeOptions === null || disposeOptions === void 0 ? void 0 : disposeOptions.onError) {
249
368
  disposeOptions.onError(error);
@@ -274,6 +393,7 @@ export const createFileViewerRenderSurfaceActionHandlers = ({ getContainer, surf
274
393
  onClearDocumentState,
275
394
  onStopZoomObserver,
276
395
  onClearZoomProvider,
396
+ onStopFitObserver,
277
397
  onStopViewStateObserver,
278
398
  onClearViewStateProvider,
279
399
  });
@@ -295,7 +415,9 @@ export const createFileViewerRenderSurfaceActionHandlers = ({ getContainer, surf
295
415
  waitForPaint,
296
416
  disposeSession: destroyRenderSession,
297
417
  onStartZoomObserver,
418
+ onStartFitObserver,
298
419
  onStartViewStateObserver,
420
+ onApplyInitialFit,
299
421
  onRefreshDocumentIndex,
300
422
  onRefreshZoomProvider,
301
423
  onRefreshViewStateProvider,
@@ -308,11 +430,12 @@ export const createFileViewerRenderSurfaceActionHandlers = ({ getContainer, surf
308
430
  mountRenderedContent,
309
431
  };
310
432
  };
311
- export const buildFileRenderContextFromLoadContext = ({ source, options, registerExportAdapter, renderContext, }) => ({
433
+ export const buildFileRenderContextFromLoadContext = ({ source, surface, options, registerExportAdapter, renderContext, }) => ({
312
434
  filename: source.filename,
313
435
  url: source.url,
314
436
  streamUrl: source.url,
315
437
  options,
438
+ surface,
316
439
  registerExportAdapter,
317
440
  ...renderContext,
318
441
  });
@@ -430,8 +430,10 @@ export interface RunFileViewerPreviewSourceChangeInput {
430
430
  export interface RunFileViewerPreviewComponentUnmountInput {
431
431
  reason?: FileViewerLifecycleContext['reason'];
432
432
  onCancelPreview?: (reason: FileViewerLifecycleContext['reason']) => void;
433
+ onClearRenderedContent?: (reason: FileViewerLifecycleContext['reason']) => void;
433
434
  onResetLoading?: () => void;
434
435
  onStopZoomObserver?: () => void;
436
+ onStopFitObserver?: () => void;
435
437
  onStopViewStateObserver?: () => void;
436
438
  }
437
439
  export interface FileViewerPreviewComponentUnmountState {
@@ -502,7 +504,7 @@ export declare const applyFileViewerPreviewRequestResetState: <Target extends Mu
502
504
  export declare const commitFileViewerPreviewRequestStartState: ({ reason, requestController, previewTarget, onClearRenderedContent, onClearError, }: CommitFileViewerPreviewRequestStartStateInput) => number;
503
505
  export declare const cancelFileViewerPreviewRequest: ({ reason, requestController, previewTarget, onClearRenderedContent, onClearError, }: CancelFileViewerPreviewRequestInput) => number;
504
506
  export declare const runFileViewerPreviewSourceChange: ({ onRefreshPreview, }?: RunFileViewerPreviewSourceChangeInput) => unknown;
505
- export declare const runFileViewerPreviewComponentUnmount: ({ reason, onCancelPreview, onResetLoading, onStopZoomObserver, onStopViewStateObserver, }?: RunFileViewerPreviewComponentUnmountInput) => FileViewerPreviewComponentUnmountState;
507
+ export declare const runFileViewerPreviewComponentUnmount: ({ reason, onCancelPreview, onClearRenderedContent, onResetLoading, onStopZoomObserver, onStopFitObserver, onStopViewStateObserver, }?: RunFileViewerPreviewComponentUnmountInput) => FileViewerPreviewComponentUnmountState;
506
508
  export declare const applyFileViewerEmptyPreviewState: <Target extends MutableFileViewerPreviewState>(target: Target, state?: FileViewerEmptyPreviewState) => Target;
507
509
  export declare const commitFileViewerEmptyPreviewResetState: ({ previewTarget, state, reason, onClearRenderedContent, onResetLoading, }: CommitFileViewerEmptyPreviewResetStateInput) => MutableFileViewerPreviewState;
508
510
  export declare const runFileViewerPreviewRequest: <LocalResult = unknown, RemoteResult = unknown>({ file, url, reason, requestController, previewTarget, onPreviewLocalFile, onPreviewRemoteFile, onClearRenderedContent, onClearError, onResetLoading, }: RunFileViewerPreviewRequestInput<LocalResult, RemoteResult>) => Promise<FileViewerPreviewRequestRunState<LocalResult, RemoteResult>>;
@@ -17,7 +17,7 @@ export const resolveFileViewerPreviewLoadErrorMessage = ({ kind, error, formatEr
17
17
  : kind === 'stream'
18
18
  ? translateFileViewerMessage(i18n, 'error.stream')
19
19
  : translateFileViewerMessage(i18n, 'error.load');
20
- return formatErrorMessage((_a = prefixes === null || prefixes === void 0 ? void 0 : prefixes[kind]) !== null && _a !== void 0 ? _a : fallbackPrefix, error);
20
+ return formatErrorMessage((_a = prefixes === null || prefixes === void 0 ? void 0 : prefixes[kind]) !== null && _a !== void 0 ? _a : fallbackPrefix, error, i18n);
21
21
  };
22
22
  export const resolveFileViewerMissingRemoteDataErrorMessage = ({ message, i18n, } = {}) => message || translateFileViewerMessage(i18n, 'error.remoteDownload');
23
23
  export const DEFAULT_FILE_VIEWER_PREVIEW_LOAD_ERROR_LOGGER = error => {
@@ -182,10 +182,14 @@ export const cancelFileViewerPreviewRequest = ({ reason = 'component-unmount', r
182
182
  export const runFileViewerPreviewSourceChange = ({ onRefreshPreview, } = {}) => {
183
183
  return onRefreshPreview === null || onRefreshPreview === void 0 ? void 0 : onRefreshPreview();
184
184
  };
185
- export const runFileViewerPreviewComponentUnmount = ({ reason = 'component-unmount', onCancelPreview, onResetLoading, onStopZoomObserver, onStopViewStateObserver, } = {}) => {
185
+ export const runFileViewerPreviewComponentUnmount = ({ reason = 'component-unmount', onCancelPreview, onClearRenderedContent, onResetLoading, onStopZoomObserver, onStopFitObserver, onStopViewStateObserver, } = {}) => {
186
186
  onCancelPreview === null || onCancelPreview === void 0 ? void 0 : onCancelPreview(reason);
187
+ if (!onCancelPreview) {
188
+ onClearRenderedContent === null || onClearRenderedContent === void 0 ? void 0 : onClearRenderedContent(reason);
189
+ }
187
190
  onResetLoading === null || onResetLoading === void 0 ? void 0 : onResetLoading();
188
191
  onStopZoomObserver === null || onStopZoomObserver === void 0 ? void 0 : onStopZoomObserver();
192
+ onStopFitObserver === null || onStopFitObserver === void 0 ? void 0 : onStopFitObserver();
189
193
  onStopViewStateObserver === null || onStopViewStateObserver === void 0 ? void 0 : onStopViewStateObserver();
190
194
  return {
191
195
  reason,
@@ -8,11 +8,12 @@ import { createEmptyFileViewerSearchState, } from '../features/document/model.js
8
8
  import { createFileViewerDocumentFeatureControllerActionHandlers, } from '../features/document/events.js';
9
9
  import { createFileViewerZoomController } from '../features/document/zoom.js';
10
10
  import { createFileViewerViewStateController } from '../features/document/viewState.js';
11
+ import { createFileViewerFitController, hasFileViewerExplicitInitialViewState, } from '../features/document/fit.js';
11
12
  import { DEFAULT_FILE_VIEWER_DOWNLOAD_FILENAME, DEFAULT_FILE_VIEWER_EXPORT_FILENAME, DEFAULT_FILE_VIEWER_PREVIEW_TITLE, createFileViewerOriginalSourceStateFromNormalizedSource, executeFileViewerDownloadOperation, executeFileViewerExportHtmlOperation, executeFileViewerPrintOperation, resolveFileViewerDisplayFilename, resolveFileViewerOperationFilename, } from './operations.js';
12
13
  import { applyFileViewerZoomAvailability, createUnsupportedAvailability, getRendererAvailability, } from '../registry/capabilities.js';
13
14
  import { buildFileViewerLifecycleContextFromNormalizedSource, buildFileViewerOperationContext, runFileViewerBeforeOperation, runFileViewerLifecycleHook, } from '../lifecycle/operations.js';
14
15
  import { collectFileViewerRendererPlugins, createRendererRegistry, getFileViewerAutoRendererPresetVersion, hasFileViewerRendererPresetName, installFileViewerRendererPlugins, listFileViewerAutoRendererPresets, resolveFileViewerRendererPresetInputs, } from '../registry/registry.js';
15
- import { createFileRenderHandlerLoader, applyFileViewerRenderSurfaceState, createFileViewerRenderSurfaceState, createFileViewerRenderTarget, removeFileViewerRenderTarget, } from '../rendering/handler.js';
16
+ import { createFileRenderHandlerLoader, applyFileViewerRenderSurfaceState, createFileViewerRenderSurfaceState, createFileViewerRenderSurface, removeFileViewerRenderTarget, } from '../rendering/handler.js';
16
17
  import { createFileViewerCoreRendererRegistry } from '../renderers/index.js';
17
18
  import { createFileViewerRequestScope } from '../source/loading.js';
18
19
  import { normalizeSource } from '../source/index.js';
@@ -94,6 +95,7 @@ export const createViewer = (container, createOptions = {}) => {
94
95
  let installedAutoRendererVersion = -1;
95
96
  let currentSource = null;
96
97
  let currentRenderTarget = null;
98
+ let currentDocumentRoot = null;
97
99
  const renderSurfaceState = createFileViewerRenderSurfaceState();
98
100
  const requestScope = createFileViewerRequestScope();
99
101
  const documentTarget = {
@@ -190,12 +192,16 @@ export const createViewer = (container, createOptions = {}) => {
190
192
  }
191
193
  };
192
194
  const createRenderTarget = (fillHeight = true) => {
193
- const target = createFileViewerRenderTarget(container);
195
+ const surface = createFileViewerRenderSurface(container, {
196
+ styleIsolation: options.styleIsolation,
197
+ });
198
+ const target = surface.host || surface.container;
194
199
  if (fillHeight) {
195
200
  target.style.height = '100%';
201
+ surface.container.style.height = '100%';
196
202
  }
197
203
  currentRenderTarget = target;
198
- return target;
204
+ return surface;
199
205
  };
200
206
  const disposeStaleSession = async (session, target) => {
201
207
  var _a;
@@ -350,20 +356,41 @@ export const createViewer = (container, createOptions = {}) => {
350
356
  emitZoomChange(state);
351
357
  emitOperationAvailabilityChange();
352
358
  };
359
+ const emitFitChange = (result) => {
360
+ var _a;
361
+ (_a = createOptions.onEvent) === null || _a === void 0 ? void 0 : _a.call(createOptions, {
362
+ type: 'fit-change',
363
+ payload: result,
364
+ });
365
+ };
353
366
  const zoomController = createFileViewerZoomController({
354
- root: () => container,
367
+ root: () => currentDocumentRoot || container,
355
368
  beforeZoom: runBeforeViewerOperation,
356
369
  onChange: state => emitZoomAndOperationAvailabilityChange(state),
357
370
  });
371
+ const fitController = createFileViewerFitController({
372
+ root: () => currentDocumentRoot || container,
373
+ getFit: () => options.fit,
374
+ onFit: result => {
375
+ zoomController.refreshProvider();
376
+ viewStateController.refreshProvider();
377
+ emitZoomAndOperationAvailabilityChange();
378
+ emitFitChange(result);
379
+ },
380
+ });
358
381
  const viewStateController = createFileViewerViewStateController({
359
- root: () => container,
382
+ root: () => currentDocumentRoot || container,
360
383
  onChange: change => {
361
384
  var _a;
385
+ if ((change.source === 'user' || change.source === 'api') &&
386
+ change.action !== 'fit') {
387
+ fitController.markUserInteraction();
388
+ }
362
389
  (_a = createOptions.onEvent) === null || _a === void 0 ? void 0 : _a.call(createOptions, { type: 'view-state-change', payload: change });
363
390
  },
364
391
  });
365
392
  const documentActions = createFileViewerDocumentFeatureControllerActionHandlers({
366
- root: () => container,
393
+ root: () => currentDocumentRoot || container,
367
394
  searchTarget: documentTarget,
368
395
  searchOptions: () => options.search,
369
396
  getAiOptions: () => options.ai,
@@ -378,6 +405,7 @@ export const createViewer = (container, createOptions = {}) => {
378
405
  });
379
406
  zoomController.observe();
380
407
  viewStateController.observe();
408
+ fitController.observe();
381
409
  const destroyCurrent = async (reason = 'replace') => {
382
410
  var _a;
383
411
  const session = renderSurfaceState.session;
@@ -394,6 +422,7 @@ export const createViewer = (container, createOptions = {}) => {
394
422
  await ((_a = session === null || session === void 0 ? void 0 : session.destroy) === null || _a === void 0 ? void 0 : _a.call(session));
395
423
  removeRenderTarget(target || undefined);
396
424
  currentSource = null;
425
+ currentDocumentRoot = null;
397
426
  applyFileViewerRenderSurfaceState(renderSurfaceState, {
398
427
  session: null,
399
428
  exportAdapter: null,
@@ -402,6 +431,7 @@ export const createViewer = (container, createOptions = {}) => {
402
431
  await documentActions.clearDocumentState();
403
432
  zoomController.clearProvider();
404
433
  viewStateController.clearProvider();
434
+ fitController.resetAutoFit();
405
435
  emitZoomAndOperationAvailabilityChange();
406
436
  if (source) {
407
437
  await emitLifecycle(options, createOptions.onEvent, 'unload-complete', source, version, startedAt, reason);
@@ -421,7 +451,10 @@ export const createViewer = (container, createOptions = {}) => {
421
451
  const renderer = registry.getByExtension(normalized.extension);
422
452
  const startedAt = Date.now();
423
453
  await emitLifecycle(options, createOptions.onEvent, 'load-start', normalized, version, startedAt);
424
- const target = createRenderTarget((renderer === null || renderer === void 0 ? void 0 : renderer.id) !== 'office-presentation');
454
+ const surface = createRenderTarget((renderer === null || renderer === void 0 ? void 0 : renderer.id) !== 'office-presentation');
455
+ const target = surface.container;
456
+ const targetHost = surface.host || target;
457
+ currentDocumentRoot = target;
425
458
  if (!(renderer === null || renderer === void 0 ? void 0 : renderer.load)) {
426
459
  renderMissingRendererState(target, normalized.extension, options);
427
460
  applyFileViewerRenderSurfaceState(renderSurfaceState, { session: null });
@@ -434,7 +467,7 @@ export const createViewer = (container, createOptions = {}) => {
434
467
  try {
435
468
  session = await renderer.load({
436
469
  source: normalized,
437
- surface: { container: target },
470
+ surface,
438
471
  options,
439
472
  signal: createOptions.signal,
440
473
  registerExportAdapter: adapter => {
@@ -446,14 +479,14 @@ export const createViewer = (container, createOptions = {}) => {
446
479
  }
447
480
  catch (error) {
448
481
  if (!requestScope.isCurrentRequest(version)) {
449
- removeRenderTarget(target);
482
+ removeRenderTarget(targetHost);
450
483
  return null;
451
484
  }
452
- removeRenderTarget(target);
485
+ removeRenderTarget(targetHost);
453
486
  throw error;
454
487
  }
455
488
  if (!requestScope.isCurrentRequest(version)) {
456
- await disposeStaleSession(session, target);
489
+ await disposeStaleSession(session, targetHost);
457
490
  return null;
458
491
  }
459
492
  applyFileViewerRenderSurfaceState(renderSurfaceState, { session });
@@ -461,6 +494,11 @@ export const createViewer = (container, createOptions = {}) => {
461
494
  zoomController.refreshProvider();
462
495
  viewStateController.refreshProvider();
463
496
  await documentActions.refreshDocumentIndex({ notify: false });
497
+ await fitController.applyInitialFit({
498
+ skip: hasFileViewerExplicitInitialViewState(options.initialViewState),
499
+ });
500
+ zoomController.refreshProvider();
501
+ viewStateController.refreshProvider();
464
502
  emitZoomAndOperationAvailabilityChange();
465
503
  await emitLifecycle(options, createOptions.onEvent, 'load-complete', normalized, version, startedAt);
466
504
  return session;
@@ -471,14 +509,20 @@ export const createViewer = (container, createOptions = {}) => {
471
509
  documentActions.destroyDocumentFeatures();
472
510
  zoomController.destroy();
473
511
  viewStateController.destroy();
512
+ fitController.destroy();
474
513
  removeWatermarkOverlay();
475
514
  },
476
515
  updateOptions(nextOptions) {
516
+ const previousFit = options.fit;
477
517
  options = {
478
518
  ...options,
479
519
  ...nextOptions,
480
520
  };
481
521
  syncWatermarkOverlay();
522
+ if ('fit' in nextOptions && nextOptions.fit !== previousFit) {
523
+ fitController.resetAutoFit();
524
+ fitController.scheduleFit('resize');
525
+ }
482
526
  },
483
527
  getCapabilities(extension) {
484
528
  return getCapabilitiesForExtension(extension);
@@ -540,20 +584,30 @@ export const createViewer = (container, createOptions = {}) => {
540
584
  });
541
585
  },
542
586
  async zoomIn() {
587
+ fitController.markUserInteraction();
543
588
  const state = await zoomController.zoomIn();
544
589
  emitZoomAndOperationAvailabilityChange(state);
545
590
  return state;
546
591
  },
547
592
  async zoomOut() {
593
+ fitController.markUserInteraction();
548
594
  const state = await zoomController.zoomOut();
549
595
  emitZoomAndOperationAvailabilityChange(state);
550
596
  return state;
551
597
  },
552
598
  async resetZoom() {
599
+ fitController.markUserInteraction();
553
600
  const state = await zoomController.resetZoom();
554
601
  emitZoomAndOperationAvailabilityChange(state);
555
602
  return state;
556
603
  },
604
+ async fitToView(fit) {
605
+ const result = await fitController.fit(fit, { source: 'api', reason: 'api' });
606
+ zoomController.refreshProvider();
607
+ viewStateController.refreshProvider();
608
+ emitZoomAndOperationAvailabilityChange();
609
+ return result;
610
+ },
557
611
  getZoomState() {
558
612
  return zoomController.getState();
559
613
  },
@@ -561,6 +615,7 @@ export const createViewer = (container, createOptions = {}) => {
561
615
  return viewStateController.getState();
562
616
  },
563
617
  applyViewState(state, applyOptions) {
618
+ fitController.markUserInteraction();
564
619
  return viewStateController.applyState(state, applyOptions);
565
620
  },
566
621
  search(query) {
@@ -50,7 +50,7 @@ export const resolveFileViewerOperationFilename = ({ filename, source, fallback
50
50
  };
51
51
  export const resolveFileViewerOperationActionErrorMessage = ({ context, formatErrorMessage, prefixes, i18n, }) => {
52
52
  var _a;
53
- return formatErrorMessage((_a = prefixes === null || prefixes === void 0 ? void 0 : prefixes[context.operation]) !== null && _a !== void 0 ? _a : translateFileViewerMessage(i18n, FILE_VIEWER_OPERATION_ACTION_ERROR_MESSAGE_KEYS[context.operation]), context.error);
53
+ return formatErrorMessage((_a = prefixes === null || prefixes === void 0 ? void 0 : prefixes[context.operation]) !== null && _a !== void 0 ? _a : translateFileViewerMessage(i18n, FILE_VIEWER_OPERATION_ACTION_ERROR_MESSAGE_KEYS[context.operation]), context.error, i18n);
54
54
  };
55
55
  export const hasFileViewerOriginalSource = (source) => {
56
56
  return !!source.buffer || !!source.file || !!source.url;
@@ -1,6 +1,6 @@
1
1
  import { type FileViewerI18nInput } from '../i18n/messages';
2
2
  import type { FileViewerRendererCategory, FileViewerStateDescriptor, FileViewerStateTheme } from '../contracts/types';
3
- export type FileViewerErrorMessageFormatter = (prefix: string, error: unknown) => string;
3
+ export type FileViewerErrorMessageFormatter = (prefix: string, error: unknown, i18n?: FileViewerI18nInput) => string;
4
4
  export declare const FILE_VIEWER_PREVIEW_MESSAGES: Readonly<{
5
5
  downloading: "正在下载文件资源...";
6
6
  streamingPdf: "正在建立 PDF 流式预览...";
@@ -28,6 +28,6 @@ export declare const createFileViewerPreviewLoadingState: (extension?: string, m
28
28
  export declare const createFileViewerReadyState: (extension?: string, theme?: FileViewerStateTheme, i18n?: FileViewerI18nInput) => FileViewerStateDescriptor;
29
29
  export declare const createFileViewerEmptyState: (extension?: string, theme?: FileViewerStateTheme, i18n?: FileViewerI18nInput) => FileViewerStateDescriptor;
30
30
  export declare const createFileViewerUnsupportedState: (extension?: string, theme?: FileViewerStateTheme, i18n?: FileViewerI18nInput) => FileViewerStateDescriptor;
31
- export declare const normalizeFileViewerErrorMessage: (error: unknown) => string;
31
+ export declare const normalizeFileViewerErrorMessage: (error: unknown, i18n?: FileViewerI18nInput) => string;
32
32
  export declare const formatFileViewerErrorMessage: FileViewerErrorMessageFormatter;
33
33
  export declare const createFileViewerErrorState: (extension?: string, error?: unknown, theme?: FileViewerStateTheme, i18n?: FileViewerI18nInput) => FileViewerStateDescriptor;
@@ -197,20 +197,53 @@ export const createFileViewerUnsupportedState = (extension = '', theme = DEFAULT
197
197
  recoverable: true,
198
198
  });
199
199
  };
200
- export const normalizeFileViewerErrorMessage = (error) => {
200
+ const FILE_VIEWER_KNOWN_ERROR_MESSAGE_KEYS = {
201
+ 'Blob is not available in the current execution environment.': 'error.blobUnavailable',
202
+ 'Unsupported file source input.': 'error.sourceUnsupported',
203
+ 'Failed to read file as ArrayBuffer.': 'error.fileReadArrayBuffer',
204
+ 'Failed to read file as data URL.': 'error.fileReadDataUrl',
205
+ 'Failed to read file as text.': 'error.fileReadText',
206
+ 'Unable to read image data URL.': 'error.imageDataUrlRead',
207
+ '文件不是有效的 DOCX/OOXML 压缩包,可能下载不完整或被服务端错误内容替换,请重新上传或检查文件源。': 'word.error.invalidDocx',
208
+ 'ODF 文件缺少 content.xml': 'word.error.missingOdfContent',
209
+ 'ODF 文件缺少 content.xml。': 'word.error.missingOdfContent',
210
+ 'ODF XML 解析失败': 'word.error.odfXmlParseFailed',
211
+ 'ODF XML 解析失败。': 'word.error.odfXmlParseFailed',
212
+ 'OFD 文件中没有可渲染的文档': 'ofd.error.empty',
213
+ 'OFD 文件解析失败': 'ofd.error.parseFailed',
214
+ 'IndexedDB 不可用': 'archive.error.indexedDbUnavailable',
215
+ 'Web Worker is not supported by this browser.': 'archive.error.workerUnsupported',
216
+ };
217
+ const resolveKnownFileViewerErrorMessage = (message, i18n) => {
218
+ const key = FILE_VIEWER_KNOWN_ERROR_MESSAGE_KEYS[message.trim()];
219
+ return key ? translateFileViewerMessage(i18n, key) : message;
220
+ };
221
+ export const normalizeFileViewerErrorMessage = (error, i18n) => {
201
222
  if (error instanceof Error) {
202
- return error.message;
223
+ return resolveKnownFileViewerErrorMessage(error.message, i18n);
224
+ }
225
+ if (typeof error === 'string') {
226
+ return resolveKnownFileViewerErrorMessage(error, i18n);
227
+ }
228
+ if (error === undefined || error === null) {
229
+ return translateFileViewerMessage(i18n, 'error.unknown');
203
230
  }
204
231
  return String(error);
205
232
  };
206
- export const formatFileViewerErrorMessage = (prefix, error) => {
207
- return `${prefix}:${normalizeFileViewerErrorMessage(error)}`;
233
+ export const formatFileViewerErrorMessage = (prefix, error, i18n) => {
234
+ const detail = normalizeFileViewerErrorMessage(error, i18n);
235
+ if (!detail) {
236
+ return prefix;
237
+ }
238
+ return resolveFileViewerLocale(i18n) === 'zh-CN'
239
+ ? `${prefix}:${detail}`
240
+ : `${prefix}: ${detail}`;
208
241
  };
209
- export const createFileViewerErrorState = (extension = '', error = '未知错误', theme = DEFAULT_FILE_VIEWER_STATE_THEME, i18n) => createFileViewerStateDescriptor({
242
+ export const createFileViewerErrorState = (extension = '', error = undefined, theme = DEFAULT_FILE_VIEWER_STATE_THEME, i18n) => createFileViewerStateDescriptor({
210
243
  state: 'error',
211
244
  extension,
212
245
  title: translateFileViewerMessage(i18n, 'state.error.title'),
213
- message: normalizeFileViewerErrorMessage(error),
246
+ message: normalizeFileViewerErrorMessage(error, i18n),
214
247
  theme,
215
248
  recoverable: true,
216
249
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@file-viewer/core",
3
- "version": "2.1.17",
3
+ "version": "2.1.18",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Framework-neutral modular TypeScript core for Flyfish File Viewer renderer orchestration, lifecycle APIs, search, zoom, print, and export",