@file-viewer/core 3.1.2 → 3.2.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 (103) hide show
  1. package/README.en.md +3 -121
  2. package/README.md +3 -126
  3. package/dist/assets.d.ts +2 -2
  4. package/dist/assets.js +1 -5
  5. package/dist/browser/controller.d.ts +1 -2
  6. package/dist/browser/controller.js +2 -34
  7. package/dist/browser.d.ts +2 -2
  8. package/dist/browser.js +2 -2
  9. package/dist/config/options.d.ts +1 -1
  10. package/dist/contracts/types.d.ts +26 -423
  11. package/dist/contracts/xml.d.ts +0 -11
  12. package/dist/export.d.ts +3 -10
  13. package/dist/export.js +2 -9
  14. package/dist/exportDocument.d.ts +1 -12
  15. package/dist/exportDocument.js +7 -31
  16. package/dist/features/document/dom/anchors.d.ts +1 -1
  17. package/dist/features/document/dom/index.d.ts +5 -5
  18. package/dist/features/document/dom/index.js +3 -3
  19. package/dist/features/document/dom/providers.d.ts +1 -1
  20. package/dist/features/document/events.d.ts +2 -2
  21. package/dist/features/document/events.js +3 -3
  22. package/dist/features/document/fit.d.ts +1 -1
  23. package/dist/features/document/fit.js +1 -1
  24. package/dist/features/document/model.d.ts +1 -1
  25. package/dist/features/document/search.d.ts +1 -1
  26. package/dist/features/document/search.js +2 -2
  27. package/dist/features/document/viewState.d.ts +1 -1
  28. package/dist/features/document/viewState.js +2 -2
  29. package/dist/features/document/zoom.d.ts +3 -2
  30. package/dist/features/document/zoom.js +19 -3
  31. package/dist/features/document/zoomGestures.d.ts +11 -0
  32. package/dist/features/document/zoomGestures.js +111 -0
  33. package/dist/features/printMask.d.ts +1 -1
  34. package/dist/features/printMaskDesigner.d.ts +2 -6
  35. package/dist/features/printMaskDesigner.js +2 -11
  36. package/dist/features/printMaskLoader.d.ts +1 -7
  37. package/dist/features/printMaskLoader.js +1 -7
  38. package/dist/features/watermark.d.ts +1 -1
  39. package/dist/headless.d.ts +29 -29
  40. package/dist/headless.js +16 -21
  41. package/dist/i18n/messages.d.ts +1 -1
  42. package/dist/i18n/messages.de.d.ts +1 -5
  43. package/dist/i18n/messages.de.js +0 -4
  44. package/dist/i18n/messages.ja.d.ts +1 -6
  45. package/dist/i18n/messages.ja.js +0 -5
  46. package/dist/i18n/messages.js +2 -2
  47. package/dist/index.d.ts +74 -74
  48. package/dist/index.js +37 -37
  49. package/dist/lifecycle/facade.d.ts +4 -4
  50. package/dist/lifecycle/facade.js +2 -2
  51. package/dist/lifecycle/operations.d.ts +4 -4
  52. package/dist/lifecycle/operations.js +4 -9
  53. package/dist/output/canvasImage.d.ts +1 -2
  54. package/dist/output/canvasImage.js +1 -4
  55. package/dist/output/export.d.ts +2 -4
  56. package/dist/output/export.js +133 -81
  57. package/dist/output/printLayout.d.ts +2 -5
  58. package/dist/output/printLayout.js +37 -9
  59. package/dist/output/snapshotDownload.d.ts +1 -2
  60. package/dist/output/snapshotDownload.js +2 -3
  61. package/dist/platform/assets.d.ts +9 -16
  62. package/dist/platform/assets.js +60 -17
  63. package/dist/platform/copiedAssets.d.ts +0 -1
  64. package/dist/platform/copiedAssets.js +1 -2
  65. package/dist/presentation/state.d.ts +1 -1
  66. package/dist/presentation/state.js +3 -3
  67. package/dist/print-mask.d.ts +2 -2
  68. package/dist/print-mask.js +1 -1
  69. package/dist/registry/capabilities.d.ts +3 -16
  70. package/dist/registry/capabilities.js +2 -15
  71. package/dist/registry/formats.d.ts +5 -5
  72. package/dist/registry/formats.generated.d.ts +7 -8
  73. package/dist/registry/formats.generated.js +7 -4
  74. package/dist/registry/formats.js +2 -2
  75. package/dist/registry/registry.d.ts +6 -15
  76. package/dist/registry/registry.js +24 -10
  77. package/dist/renderers/image.d.ts +1 -1
  78. package/dist/renderers/image.js +26 -5
  79. package/dist/renderers/index.d.ts +11 -11
  80. package/dist/renderers/index.js +5 -11
  81. package/dist/rendering/dispatcher.d.ts +1 -6
  82. package/dist/rendering/dispatcher.js +3 -3
  83. package/dist/rendering/handler.d.ts +10 -15
  84. package/dist/rendering/handler.js +20 -27
  85. package/dist/security/svgResources.d.ts +0 -10
  86. package/dist/security/svgResources.js +2 -11
  87. package/dist/source/index.d.ts +3 -3
  88. package/dist/source/index.js +1 -1
  89. package/dist/source/loading.d.ts +56 -15
  90. package/dist/source/loading.js +177 -87
  91. package/dist/source/precheck.d.ts +1 -13
  92. package/dist/source/precheck.js +2 -9
  93. package/dist/source/textEncoding.d.ts +6 -3
  94. package/dist/source/textEncoding.js +58 -8
  95. package/dist/viewer/createViewer.d.ts +1 -1
  96. package/dist/viewer/createViewer.js +103 -33
  97. package/dist/viewer/loading.d.ts +2 -6
  98. package/dist/viewer/loading.js +1 -9
  99. package/dist/viewer/operations.d.ts +3 -3
  100. package/dist/viewer/operations.js +6 -14
  101. package/dist/viewer/state.d.ts +2 -6
  102. package/dist/viewer/state.js +3 -8
  103. package/package.json +127 -19
@@ -1,4 +1,3 @@
1
- /** Optional, self-hosted XML Schema 1.0 / XSLT 1.0 profiles. */
2
1
  export interface FileViewerXmlProfile {
3
2
  id: string;
4
3
  match: {
@@ -11,7 +10,6 @@ export interface FileViewerXmlProfile {
11
10
  enabled: boolean;
12
11
  };
13
12
  };
14
- /** Relative to the manifest URL (or baseUrl for inline profiles). */
15
13
  xsd?: string;
16
14
  xslt: string;
17
15
  }
@@ -25,21 +23,14 @@ export interface FileViewerXmlDiagnostic {
25
23
  profileId?: string;
26
24
  }
27
25
  export interface FileViewerXmlOptions {
28
- /** Opt in with a same-origin HTTP(S) manifest. Redirects are rejected. */
29
26
  profilesUrl?: string;
30
- /** Alternative to profilesUrl. Supplying both is a configuration error. */
31
27
  profiles?: FileViewerXmlProfile[];
32
- /** Base for inline profiles' resource paths; defaults to the document URL. */
33
28
  baseUrl?: string;
34
29
  runtime?: {
35
- /** xmllint-wasm@5.3.0 browser worker; xmllint.wasm must be beside it. */
36
30
  xsdWorkerUrl?: string;
37
- /** xslt-polyfill@1.0.29 dist/xslt-wasm.js, containing embedded WASM. */
38
31
  xsltModuleUrl?: string;
39
32
  };
40
- /** Whole profile operation, including downloads; default 15s, ceiling 60s. */
41
33
  timeoutMs?: number;
42
- /** Limits may lower the hard ceilings but cannot disable them. */
43
34
  limits?: {
44
35
  maxXmlBytes?: number;
45
36
  maxResourceBytes?: number;
@@ -48,12 +39,10 @@ export interface FileViewerXmlOptions {
48
39
  maxTotalResourceBytes?: number;
49
40
  };
50
41
  initialView?: 'source' | 'rendered';
51
- /** Labels can be localized without replacing the source renderer. */
52
42
  labels?: {
53
43
  viewSource?: string;
54
44
  viewRendered?: string;
55
45
  diagnostics?: string;
56
46
  };
57
- /** Also available on the rendered instance; never contains generated HTML. */
58
47
  onDiagnostic?: (diagnostic: FileViewerXmlDiagnostic) => void;
59
48
  }
package/dist/export.d.ts CHANGED
@@ -1,10 +1,3 @@
1
- /**
2
- * Explicit HTML export and print-document entrypoint.
3
- *
4
- * This subpath owns the DOM sanitizer and is intentionally excluded from the
5
- * normal viewer/headless graph. Viewer operations load it only when the user
6
- * starts an HTML export or print operation.
7
- */
8
- export { buildFileViewerRenderedDomDocument, buildFileViewerRenderedHtmlDocument, buildExportDomDocument, buildExportHtmlDocument, collectDocumentStyles, sanitizeFileViewerExportDocumentDom, sanitizeFileViewerExportDocumentHtml, } from './exportDocument';
9
- export type { BuildExportHtmlDocumentOptions, BuildFileViewerRenderedHtmlDocumentOptions, } from './exportDocument';
10
- export { inlineFileViewerBlobUrlsInHtml, prepareFileViewerRenderedContentForSnapshot, replaceFileViewerCanvasWithImages, resolveFileViewerPrintStyle, triggerFileViewerBlobDownload, triggerFileViewerUrlDownload, waitForFileViewerImages, waitForFileViewerNextPaint, waitForFileViewerPrintWindowReady, } from './output/export';
1
+ export { buildFileViewerRenderedDomDocument, buildFileViewerRenderedHtmlDocument, buildExportDomDocument, buildExportHtmlDocument, collectDocumentStyles, sanitizeFileViewerExportDocumentDom, sanitizeFileViewerExportDocumentHtml, } from "./exportDocument.js";
2
+ export type { BuildExportHtmlDocumentOptions, BuildFileViewerRenderedHtmlDocumentOptions, } from "./exportDocument.js";
3
+ export { inlineFileViewerBlobUrlsInHtml, prepareFileViewerRenderedContentForSnapshot, replaceFileViewerCanvasWithImages, resolveFileViewerPrintStyle, triggerFileViewerBlobDownload, triggerFileViewerUrlDownload, waitForFileViewerImages, waitForFileViewerNextPaint, waitForFileViewerPrintWindowReady, } from "./output/export.js";
package/dist/export.js CHANGED
@@ -1,9 +1,2 @@
1
- /**
2
- * Explicit HTML export and print-document entrypoint.
3
- *
4
- * This subpath owns the DOM sanitizer and is intentionally excluded from the
5
- * normal viewer/headless graph. Viewer operations load it only when the user
6
- * starts an HTML export or print operation.
7
- */
8
- export { buildFileViewerRenderedDomDocument, buildFileViewerRenderedHtmlDocument, buildExportDomDocument, buildExportHtmlDocument, collectDocumentStyles, sanitizeFileViewerExportDocumentDom, sanitizeFileViewerExportDocumentHtml, } from './exportDocument.js';
9
- export { inlineFileViewerBlobUrlsInHtml, prepareFileViewerRenderedContentForSnapshot, replaceFileViewerCanvasWithImages, resolveFileViewerPrintStyle, triggerFileViewerBlobDownload, triggerFileViewerUrlDownload, waitForFileViewerImages, waitForFileViewerNextPaint, waitForFileViewerPrintWindowReady, } from './output/export.js';
1
+ export { buildFileViewerRenderedDomDocument, buildFileViewerRenderedHtmlDocument, buildExportDomDocument, buildExportHtmlDocument, collectDocumentStyles, sanitizeFileViewerExportDocumentDom, sanitizeFileViewerExportDocumentHtml, } from "./exportDocument.js";
2
+ export { inlineFileViewerBlobUrlsInHtml, prepareFileViewerRenderedContentForSnapshot, replaceFileViewerCanvasWithImages, resolveFileViewerPrintStyle, triggerFileViewerBlobDownload, triggerFileViewerUrlDownload, waitForFileViewerImages, waitForFileViewerNextPaint, waitForFileViewerPrintWindowReady, } from "./output/export.js";
@@ -1,15 +1,5 @@
1
- import type { FileRenderExportAdapter, FileRenderExportMode, FileViewerPrintMaskOptions } from './contracts/types';
2
- /**
3
- * Sanitize the complete standalone export document before it is downloaded or
4
- * mounted into a print window. This is deliberately enforced in core because
5
- * third-party render adapters and watermark overrides are library inputs.
6
- */
1
+ import type { FileRenderExportAdapter, FileRenderExportMode, FileViewerPrintMaskOptions } from "./contracts/types.js";
7
2
  export declare const sanitizeFileViewerExportDocumentHtml: (html: string, documentRef?: Document) => string;
8
- /**
9
- * Produce a sanitized, inert document tree for the print-window mount path.
10
- * Returning DOM here avoids reinterpreting the sanitized string through a
11
- * second HTML sink such as document.write or innerHTML.
12
- */
13
3
  export declare const sanitizeFileViewerExportDocumentDom: (html: string, documentRef?: Document) => HTMLHtmlElement;
14
4
  export interface BuildExportHtmlDocumentOptions {
15
5
  contentHtml: string;
@@ -18,7 +8,6 @@ export interface BuildExportHtmlDocumentOptions {
18
8
  title: string;
19
9
  watermarkInlineStyle?: string;
20
10
  mask?: FileViewerPrintMaskOptions | null;
21
- /** Document whose Window owns the sanitizer. Defaults to the active document. */
22
11
  documentRef?: Document;
23
12
  }
24
13
  export declare const collectDocumentStyles: (documentRef?: Document) => string;
@@ -1,6 +1,6 @@
1
1
  import createDOMPurify from 'dompurify';
2
- import { applyFileViewerPagePrintMasksToHtml, buildFileViewerPrintMaskOverlayHtml, FILE_VIEWER_PRINT_MASK_STYLE, normalizeFileViewerPrintMaskOptions, } from './features/printMask.js';
3
- import { inlineFileViewerBlobUrlsInHtml, prepareFileViewerRenderedContentForSnapshot, replaceFileViewerCanvasWithImages, resolveFileViewerPrintStyle, } from './output/export.js';
2
+ import { applyFileViewerPagePrintMasksToHtml, buildFileViewerPrintMaskOverlayHtml, FILE_VIEWER_PRINT_MASK_STYLE, normalizeFileViewerPrintMaskOptions, } from "./features/printMask.js";
3
+ import { inlineFileViewerBlobUrlsInHtml, prepareFileViewerRenderedContentForSnapshot, replaceFileViewerCanvasWithImages, resolveFileViewerPrintStyle, } from "./output/export.js";
4
4
  const escapeHtmlAttribute = (value) => value
5
5
  .replace(/&/g, '&')
6
6
  .replace(/"/g, '"')
@@ -47,11 +47,6 @@ const decodeCssEscape = (value, slashIndex) => {
47
47
  }
48
48
  return { value: first, nextIndex: cursor + 1 };
49
49
  };
50
- /**
51
- * Remove CSS comments and decode identifier escapes before looking for URL
52
- * functions. CSS comments and escapes may otherwise split security-sensitive
53
- * tokens such as `url` or `@import` while the browser still accepts them.
54
- */
55
50
  const canonicalizeCssForSecurity = (css) => {
56
51
  let output = '';
57
52
  let quote = '';
@@ -246,12 +241,6 @@ const inspectCssNetworkTokens = (css) => {
246
241
  }
247
242
  return { hasImport, hasUrl };
248
243
  };
249
- /**
250
- * Keep ordinary CSS and local embedded resources, but remove every construct
251
- * that can make an exported document initiate a new network request. Imported
252
- * stylesheets fail closed; unsafe url() values become `none` so the rest of a
253
- * declaration can still render.
254
- */
255
244
  const sanitizeExportCss = (css) => {
256
245
  const canonical = canonicalizeCssForSecurity(css);
257
246
  const canonicalTokens = inspectCssNetworkTokens(canonical);
@@ -353,13 +342,9 @@ const getExportPurifier = (documentRef) => {
353
342
  element.setAttribute('rel', 'noopener noreferrer');
354
343
  }
355
344
  if (localName === 'a' || localName === 'area') {
356
- // Keep ordinary links, but never preserve document-controlled beacons.
357
345
  element.removeAttribute('ping');
358
346
  }
359
347
  if (element.hasAttribute('srcset')) {
360
- // Candidate lists have their own comma/descriptor grammar. The export
361
- // keeps the materialized src and drops candidates that could select a
362
- // remote resource after the standalone document is opened.
363
348
  element.removeAttribute('srcset');
364
349
  }
365
350
  if (['img', 'audio', 'video', 'source', 'track', 'input'].includes(localName || '')) {
@@ -392,19 +377,12 @@ const EXPORT_STATIC_META = [
392
377
  '<meta charset="utf-8" />',
393
378
  '<meta name="viewport" content="width=device-width,initial-scale=1" />',
394
379
  ].join('\n ');
395
- /**
396
- * Sanitize the complete standalone export document before it is downloaded or
397
- * mounted into a print window. This is deliberately enforced in core because
398
- * third-party render adapters and watermark overrides are library inputs.
399
- */
400
380
  export const sanitizeFileViewerExportDocumentHtml = (html, documentRef) => {
401
381
  const currentDocument = resolveExportDocument(documentRef);
402
382
  const purifier = currentDocument
403
383
  ? getExportPurifier(currentDocument)
404
384
  : null;
405
385
  if (!purifier) {
406
- // Export is a browser feature. Fail closed rather than return executable
407
- // markup when called without a usable DOM implementation.
408
386
  return '<!doctype html>\n<html lang="en"><head><meta charset="utf-8" /></head><body></body></html>';
409
387
  }
410
388
  const sanitized = String(purifier.sanitize(html, EXPORT_SANITIZER_OPTIONS));
@@ -439,11 +417,6 @@ const ensureFileViewerExportDocumentMeta = (root) => {
439
417
  }
440
418
  return root;
441
419
  };
442
- /**
443
- * Produce a sanitized, inert document tree for the print-window mount path.
444
- * Returning DOM here avoids reinterpreting the sanitized string through a
445
- * second HTML sink such as document.write or innerHTML.
446
- */
447
420
  export const sanitizeFileViewerExportDocumentDom = (html, documentRef) => {
448
421
  const currentDocument = resolveExportDocument(documentRef);
449
422
  if (!currentDocument) {
@@ -728,6 +701,11 @@ const EXPORT_DOCUMENT_STYLE = `
728
701
  overflow: visible;
729
702
  background: #ffffff;
730
703
  }
704
+ .viewer-export-content .docx-print-document {
705
+ width: auto !important;
706
+ max-width: none !important;
707
+ margin: 0 !important;
708
+ }
731
709
  .viewer-export-content .pdf-export-document {
732
710
  display: block;
733
711
  padding: 0;
@@ -801,8 +779,6 @@ export const collectDocumentStyles = (documentRef) => {
801
779
  : '';
802
780
  }
803
781
  catch {
804
- // Cross-origin and unreadable stylesheets fail closed. Keeping their
805
- // link would make a standalone export initiate a second request.
806
782
  return '';
807
783
  }
808
784
  })
@@ -1,4 +1,4 @@
1
- import type { FileViewerDocumentAnchor } from '../../../contracts/types';
1
+ import type { FileViewerDocumentAnchor } from "../../../contracts/types.js";
2
2
  export declare const DEFAULT_FILE_VIEWER_ANCHOR_SELECTOR: string;
3
3
  export declare const DEFAULT_FILE_VIEWER_ANCHOR_EXCLUDE_SELECTOR: string;
4
4
  export declare const collectFileViewerDocumentAnchors: (root: HTMLElement | null) => FileViewerDocumentAnchor[];
@@ -1,5 +1,5 @@
1
- export { DEFAULT_FILE_VIEWER_ANCHOR_EXCLUDE_SELECTOR, DEFAULT_FILE_VIEWER_ANCHOR_SELECTOR, collectFileViewerDocumentAnchors, findFileViewerAnchorForElement, getCurrentFileViewerDocumentAnchor, scrollToFileViewerDocumentAnchor, } from './anchors';
2
- export { findFileViewerSearchProvider, findFileViewerViewStateProvider, findFileViewerZoomProvider, registerFileViewerSearchProvider, registerFileViewerViewStateProvider, registerFileViewerZoomProvider, unregisterFileViewerSearchProvider, unregisterFileViewerViewStateProvider, unregisterFileViewerZoomProvider, } from './providers';
3
- export type { FileViewerSearchProviderHost, FileViewerViewStateProviderHost, FileViewerZoomProviderHost, } from './providers';
4
- export { DEFAULT_FILE_VIEWER_SCROLL_CONTAINER_CANDIDATE_SELECTOR, DEFAULT_FILE_VIEWER_SCROLL_CONTAINER_SELECTOR, DEFAULT_FILE_VIEWER_SCROLLABLE_OVERFLOW_VALUES, getFileViewerScrollableRange, isFileViewerScrollableElement, resolveFileViewerScrollContainer, } from './scroll';
5
- export type { ResolveFileViewerScrollContainerOptions, } from './scroll';
1
+ export { DEFAULT_FILE_VIEWER_ANCHOR_EXCLUDE_SELECTOR, DEFAULT_FILE_VIEWER_ANCHOR_SELECTOR, collectFileViewerDocumentAnchors, findFileViewerAnchorForElement, getCurrentFileViewerDocumentAnchor, scrollToFileViewerDocumentAnchor, } from "./anchors.js";
2
+ export { findFileViewerSearchProvider, findFileViewerViewStateProvider, findFileViewerZoomProvider, registerFileViewerSearchProvider, registerFileViewerViewStateProvider, registerFileViewerZoomProvider, unregisterFileViewerSearchProvider, unregisterFileViewerViewStateProvider, unregisterFileViewerZoomProvider, } from "./providers.js";
3
+ export type { FileViewerSearchProviderHost, FileViewerViewStateProviderHost, FileViewerZoomProviderHost, } from "./providers.js";
4
+ export { DEFAULT_FILE_VIEWER_SCROLL_CONTAINER_CANDIDATE_SELECTOR, DEFAULT_FILE_VIEWER_SCROLL_CONTAINER_SELECTOR, DEFAULT_FILE_VIEWER_SCROLLABLE_OVERFLOW_VALUES, getFileViewerScrollableRange, isFileViewerScrollableElement, resolveFileViewerScrollContainer, } from "./scroll.js";
5
+ export type { ResolveFileViewerScrollContainerOptions, } from "./scroll.js";
@@ -1,3 +1,3 @@
1
- export { DEFAULT_FILE_VIEWER_ANCHOR_EXCLUDE_SELECTOR, DEFAULT_FILE_VIEWER_ANCHOR_SELECTOR, collectFileViewerDocumentAnchors, findFileViewerAnchorForElement, getCurrentFileViewerDocumentAnchor, scrollToFileViewerDocumentAnchor, } from './anchors.js';
2
- export { findFileViewerSearchProvider, findFileViewerViewStateProvider, findFileViewerZoomProvider, registerFileViewerSearchProvider, registerFileViewerViewStateProvider, registerFileViewerZoomProvider, unregisterFileViewerSearchProvider, unregisterFileViewerViewStateProvider, unregisterFileViewerZoomProvider, } from './providers.js';
3
- export { DEFAULT_FILE_VIEWER_SCROLL_CONTAINER_CANDIDATE_SELECTOR, DEFAULT_FILE_VIEWER_SCROLL_CONTAINER_SELECTOR, DEFAULT_FILE_VIEWER_SCROLLABLE_OVERFLOW_VALUES, getFileViewerScrollableRange, isFileViewerScrollableElement, resolveFileViewerScrollContainer, } from './scroll.js';
1
+ export { DEFAULT_FILE_VIEWER_ANCHOR_EXCLUDE_SELECTOR, DEFAULT_FILE_VIEWER_ANCHOR_SELECTOR, collectFileViewerDocumentAnchors, findFileViewerAnchorForElement, getCurrentFileViewerDocumentAnchor, scrollToFileViewerDocumentAnchor, } from "./anchors.js";
2
+ export { findFileViewerSearchProvider, findFileViewerViewStateProvider, findFileViewerZoomProvider, registerFileViewerSearchProvider, registerFileViewerViewStateProvider, registerFileViewerZoomProvider, unregisterFileViewerSearchProvider, unregisterFileViewerViewStateProvider, unregisterFileViewerZoomProvider, } from "./providers.js";
3
+ export { DEFAULT_FILE_VIEWER_SCROLL_CONTAINER_CANDIDATE_SELECTOR, DEFAULT_FILE_VIEWER_SCROLL_CONTAINER_SELECTOR, DEFAULT_FILE_VIEWER_SCROLLABLE_OVERFLOW_VALUES, getFileViewerScrollableRange, isFileViewerScrollableElement, resolveFileViewerScrollContainer, } from "./scroll.js";
@@ -1,4 +1,4 @@
1
- import type { FileViewerSearchProvider, FileViewerViewStateProvider, FileViewerZoomProvider } from '../../../contracts/types';
1
+ import type { FileViewerSearchProvider, FileViewerViewStateProvider, FileViewerZoomProvider } from "../../../contracts/types.js";
2
2
  export interface FileViewerSearchProviderHost extends HTMLElement {
3
3
  __flyfishViewerSearchProvider?: FileViewerSearchProvider;
4
4
  }
@@ -1,5 +1,5 @@
1
- import { type FileViewerDomSearchControllerStateTarget } from './search';
2
- import type { FileViewerAiOptions, FileViewerDocumentAnchor, FileViewerDocumentChunk, FileViewerSearchOptions, FileViewerSearchState } from '../../contracts/types';
1
+ import { type FileViewerDomSearchControllerStateTarget } from "./search.js";
2
+ import type { FileViewerAiOptions, FileViewerDocumentAnchor, FileViewerDocumentChunk, FileViewerSearchOptions, FileViewerSearchState } from "../../contracts/types.js";
3
3
  export interface ResolveFileViewerLocationChangeAnchorInput {
4
4
  root: HTMLElement | null | undefined;
5
5
  anchors: FileViewerDocumentAnchor[];
@@ -1,6 +1,6 @@
1
- import { buildFileViewerDocumentTextChunks } from './model.js';
2
- import { getCurrentFileViewerDocumentAnchor, resolveFileViewerScrollContainer, scrollToFileViewerDocumentAnchor, } from './dom/index.js';
3
- import { cloneFileViewerSearchState, createFileViewerDomSearchController, createFileViewerDomSearchControllerActionHandlers, } from './search.js';
1
+ import { buildFileViewerDocumentTextChunks } from "./model.js";
2
+ import { getCurrentFileViewerDocumentAnchor, resolveFileViewerScrollContainer, scrollToFileViewerDocumentAnchor, } from "./dom/index.js";
3
+ import { cloneFileViewerSearchState, createFileViewerDomSearchController, createFileViewerDomSearchControllerActionHandlers, } from "./search.js";
4
4
  export const createFileViewerSearchChangeState = (state) => {
5
5
  return cloneFileViewerSearchState(state);
6
6
  };
@@ -1,4 +1,4 @@
1
- import type { FileViewerFitMode, FileViewerFitOptions, FileViewerFitRequest, FileViewerFitResize, FileViewerFitResult, FileViewerViewState, FileViewerViewStateChangeSource } from '../../contracts/types';
1
+ import type { FileViewerFitMode, FileViewerFitOptions, FileViewerFitRequest, FileViewerFitResize, FileViewerFitResult, FileViewerViewState, FileViewerViewStateChangeSource } from "../../contracts/types.js";
2
2
  export declare const FILE_VIEWER_FIT_MODES: readonly ["auto", "contain", "cover", "width", "height", "actual", "scale-down"];
3
3
  export declare const FILE_VIEWER_FIT_RESIZE_MODES: readonly ["until-interaction", "always", "initial"];
4
4
  export interface NormalizedFileViewerFitOptions extends Required<Pick<FileViewerFitOptions, 'mode' | 'resize' | 'padding'>> {
@@ -1,4 +1,4 @@
1
- import { findFileViewerViewStateProvider, findFileViewerZoomProvider, } from './dom/index.js';
1
+ import { findFileViewerViewStateProvider, findFileViewerZoomProvider, } from "./dom/index.js";
2
2
  export const FILE_VIEWER_FIT_MODES = [
3
3
  'auto',
4
4
  'contain',
@@ -1,4 +1,4 @@
1
- import type { FileViewerAiOptions, FileViewerDocumentAnchor, FileViewerDocumentChunk, FileViewerSearchOptions, FileViewerSearchState, FileViewerZoomState } from '../../contracts/types';
1
+ import type { FileViewerAiOptions, FileViewerDocumentAnchor, FileViewerDocumentChunk, FileViewerSearchOptions, FileViewerSearchState, FileViewerZoomState } from "../../contracts/types.js";
2
2
  export declare const DEFAULT_FILE_VIEWER_ZOOM_SCALE = 1;
3
3
  export declare const DEFAULT_FILE_VIEWER_TEXT_CHUNK_SIZE = 1200;
4
4
  export declare const DEFAULT_FILE_VIEWER_TEXT_CHUNK_OVERLAP = 160;
@@ -1,4 +1,4 @@
1
- import type { FileViewerDocumentAnchor, FileViewerSearchMatch, FileViewerSearchOptions, FileViewerSearchState } from '../../contracts/types';
1
+ import type { FileViewerDocumentAnchor, FileViewerSearchMatch, FileViewerSearchOptions, FileViewerSearchState } from "../../contracts/types.js";
2
2
  export interface CreateFileViewerDomSearchControllerOptions {
3
3
  root: () => HTMLElement | null | undefined;
4
4
  options?: () => boolean | FileViewerSearchOptions | undefined;
@@ -1,5 +1,5 @@
1
- import { collectFileViewerDocumentAnchors, findFileViewerAnchorForElement, findFileViewerSearchProvider, } from './dom/index.js';
2
- import { createEmptyFileViewerSearchState, normalizeFileViewerSearchOptions, } from './model.js';
1
+ import { collectFileViewerDocumentAnchors, findFileViewerAnchorForElement, findFileViewerSearchProvider, } from "./dom/index.js";
2
+ import { createEmptyFileViewerSearchState, normalizeFileViewerSearchOptions, } from "./model.js";
3
3
  export const DEFAULT_FILE_VIEWER_SEARCH_MATCH_CLASS = 'flyfish-search-match';
4
4
  export const DEFAULT_FILE_VIEWER_SEARCH_ACTIVE_CLASS = 'flyfish-search-match--active';
5
5
  export const DEFAULT_FILE_VIEWER_SEARCH_MAX_MATCHES = 1000;
@@ -1,4 +1,4 @@
1
- import type { FileViewerApplyViewStateOptions, FileViewerViewState, FileViewerViewStateChange, FileViewerViewStateProvider } from '../../contracts/types';
1
+ import type { FileViewerApplyViewStateOptions, FileViewerViewState, FileViewerViewStateChange, FileViewerViewStateProvider } from "../../contracts/types.js";
2
2
  export interface RegisterFileViewerGenericViewStateProviderOptions {
3
3
  host: HTMLElement;
4
4
  renderer?: string;
@@ -1,5 +1,5 @@
1
- import { findFileViewerViewStateProvider, findFileViewerZoomProvider, registerFileViewerViewStateProvider, resolveFileViewerScrollContainer, unregisterFileViewerViewStateProvider, } from './dom/index.js';
2
- import { resolveFileViewerFitScale } from './fit.js';
1
+ import { findFileViewerViewStateProvider, findFileViewerZoomProvider, registerFileViewerViewStateProvider, resolveFileViewerScrollContainer, unregisterFileViewerViewStateProvider, } from "./dom/index.js";
2
+ import { resolveFileViewerFitScale } from "./fit.js";
3
3
  export const cloneFileViewerViewState = (state) => {
4
4
  if (!state) {
5
5
  return null;
@@ -1,8 +1,9 @@
1
- import type { FileViewerOperationType, FileViewerZoomProvider, FileViewerZoomState } from '../../contracts/types';
1
+ import type { FileViewerOptions, FileViewerOperationType, FileViewerZoomProvider, FileViewerZoomState } from "../../contracts/types.js";
2
2
  export type FileViewerZoomOperation = Extract<FileViewerOperationType, 'zoom-in' | 'zoom-out' | 'zoom-reset'>;
3
3
  export interface CreateFileViewerZoomControllerOptions {
4
4
  root: () => HTMLElement | null | undefined;
5
5
  enabled?: () => boolean;
6
+ gestures?: () => FileViewerOptions['zoomGestures'];
6
7
  beforeZoom?: (operation: FileViewerZoomOperation) => Promise<boolean> | boolean;
7
8
  onChange?: (state: FileViewerZoomState) => void;
8
9
  }
@@ -46,4 +47,4 @@ export declare const createFileViewerZoomChangeEmitter: () => {
46
47
  subscribe(listener: () => void): () => void;
47
48
  clear(): void;
48
49
  };
49
- export declare const createFileViewerZoomController: ({ root, enabled, beforeZoom, onChange, }: CreateFileViewerZoomControllerOptions) => FileViewerZoomController;
50
+ export declare const createFileViewerZoomController: ({ root, enabled, gestures, beforeZoom, onChange, }: CreateFileViewerZoomControllerOptions) => FileViewerZoomController;
@@ -1,5 +1,6 @@
1
- import { createFileViewerZoomState } from './model.js';
2
- import { findFileViewerZoomProvider } from './dom/index.js';
1
+ import { createFileViewerZoomState } from "./model.js";
2
+ import { findFileViewerZoomProvider } from "./dom/index.js";
3
+ import { installFileViewerZoomGestures } from "./zoomGestures.js";
3
4
  export const cloneFileViewerZoomState = (state) => ({
4
5
  scale: state.scale,
5
6
  label: state.label,
@@ -102,10 +103,11 @@ const getMutationObserverConstructor = (root) => {
102
103
  return ((_b = (_a = root === null || root === void 0 ? void 0 : root.ownerDocument) === null || _a === void 0 ? void 0 : _a.defaultView) === null || _b === void 0 ? void 0 : _b.MutationObserver) ||
103
104
  (typeof MutationObserver !== 'undefined' ? MutationObserver : undefined);
104
105
  };
105
- export const createFileViewerZoomController = ({ root, enabled, beforeZoom, onChange, }) => {
106
+ export const createFileViewerZoomController = ({ root, enabled, gestures, beforeZoom, onChange, }) => {
106
107
  let provider = null;
107
108
  let unsubscribe = null;
108
109
  let observer = null;
110
+ let disposeGestures = null;
109
111
  let runningAction = false;
110
112
  const state = createFileViewerZoomState();
111
113
  const notifyChange = () => {
@@ -140,6 +142,8 @@ export const createFileViewerZoomController = ({ root, enabled, beforeZoom, onCh
140
142
  const disconnectObserver = () => {
141
143
  observer === null || observer === void 0 ? void 0 : observer.disconnect();
142
144
  observer = null;
145
+ disposeGestures === null || disposeGestures === void 0 ? void 0 : disposeGestures();
146
+ disposeGestures = null;
143
147
  };
144
148
  const runZoomAction = async (operation, action) => {
145
149
  const nextProvider = syncProvider();
@@ -171,6 +175,18 @@ export const createFileViewerZoomController = ({ root, enabled, beforeZoom, onCh
171
175
  observe() {
172
176
  disconnectObserver();
173
177
  const currentRoot = root();
178
+ if (currentRoot) {
179
+ disposeGestures = installFileViewerZoomGestures(currentRoot, {
180
+ provider: syncProvider,
181
+ enabled: () => (enabled === null || enabled === void 0 ? void 0 : enabled()) !== false,
182
+ settings: () => gestures === null || gestures === void 0 ? void 0 : gestures(),
183
+ beforeZoom: operation => { var _a; return (_a = beforeZoom === null || beforeZoom === void 0 ? void 0 : beforeZoom(operation)) !== null && _a !== void 0 ? _a : true; },
184
+ onZoom: () => {
185
+ syncProvider();
186
+ notifyChange();
187
+ },
188
+ });
189
+ }
174
190
  const MutationObserverCtor = getMutationObserverConstructor(currentRoot);
175
191
  if (!currentRoot || !MutationObserverCtor) {
176
192
  syncProvider();
@@ -0,0 +1,11 @@
1
+ import type { FileViewerOptions, FileViewerZoomProvider } from "../../contracts/types.js";
2
+ import type { FileViewerZoomOperation } from "./zoom.js";
3
+ interface ZoomGestureOptions {
4
+ provider: () => FileViewerZoomProvider | null;
5
+ enabled: () => boolean;
6
+ settings: () => FileViewerOptions['zoomGestures'];
7
+ beforeZoom: (operation: FileViewerZoomOperation) => boolean | Promise<boolean>;
8
+ onZoom: () => void;
9
+ }
10
+ export declare function installFileViewerZoomGestures(container: HTMLElement, options: ZoomGestureOptions): () => void;
11
+ export {};
@@ -0,0 +1,111 @@
1
+ export function installFileViewerZoomGestures(container, options) {
2
+ const view = container.ownerDocument.defaultView;
3
+ if (!view)
4
+ return () => { };
5
+ let disposed = false;
6
+ let frame = 0;
7
+ let busy = false;
8
+ let pending = null;
9
+ let pendingProvider = null;
10
+ let pinch = null;
11
+ const allowed = (kind) => {
12
+ const settings = options.settings();
13
+ return settings !== false &&
14
+ (typeof settings !== 'object' || settings[kind] !== false) && options.enabled();
15
+ };
16
+ const interactive = (event) => event.composedPath().some(target => target instanceof view.Element && target.matches('button,a[href],input,textarea,select,[contenteditable]:not([contenteditable="false"])'));
17
+ const clamp = (scale, provider) => {
18
+ var _a, _b;
19
+ const state = provider.getState();
20
+ return Math.min((_a = state.maxScale) !== null && _a !== void 0 ? _a : 4, Math.max((_b = state.minScale) !== null && _b !== void 0 ? _b : 0.1, scale));
21
+ };
22
+ const schedule = () => {
23
+ if (!disposed && !busy && !frame && pending !== null) {
24
+ frame = view.requestAnimationFrame(() => { frame = 0; void flush(); });
25
+ }
26
+ };
27
+ const flush = async () => {
28
+ const scale = pending;
29
+ const provider = pendingProvider;
30
+ pending = null;
31
+ pendingProvider = null;
32
+ if (disposed || !options.enabled() || scale === null || !(provider === null || provider === void 0 ? void 0 : provider.setZoom) ||
33
+ provider !== options.provider())
34
+ return;
35
+ const operation = scale >= provider.getState().scale ? 'zoom-in' : 'zoom-out';
36
+ busy = true;
37
+ try {
38
+ if (!await options.beforeZoom(operation) || disposed || !options.enabled() ||
39
+ provider !== options.provider())
40
+ return;
41
+ await provider.setZoom(clamp(scale, provider));
42
+ if (!disposed && provider === options.provider())
43
+ options.onZoom();
44
+ }
45
+ catch {
46
+ }
47
+ finally {
48
+ busy = false;
49
+ schedule();
50
+ }
51
+ };
52
+ const queue = (scale, provider) => {
53
+ pending = clamp(scale, provider);
54
+ pendingProvider = provider;
55
+ schedule();
56
+ };
57
+ const wheel = (event) => {
58
+ if (event.defaultPrevented || !allowed('wheel') || !(event.ctrlKey || event.metaKey) ||
59
+ interactive(event) || !Number.isFinite(event.deltaY) || event.deltaY === 0)
60
+ return;
61
+ const provider = options.provider();
62
+ if (!(provider === null || provider === void 0 ? void 0 : provider.setZoom))
63
+ return;
64
+ const unit = event.deltaMode === 1 ? 16 : event.deltaMode === 2 ? Math.max(1, container.clientHeight) : 1;
65
+ const start = pendingProvider === provider && pending !== null ? pending : provider.getState().scale;
66
+ queue(start * Math.exp(-Math.max(-1000, Math.min(1000, event.deltaY * unit)) * 0.002), provider);
67
+ event.preventDefault();
68
+ };
69
+ const distance = (touches) => Math.hypot(touches[0].clientX - touches[1].clientX, touches[0].clientY - touches[1].clientY);
70
+ const start = (event) => {
71
+ if (event.defaultPrevented || event.touches.length !== 2 || !allowed('pinch') || interactive(event))
72
+ return;
73
+ const provider = options.provider();
74
+ const d = distance(event.touches);
75
+ if (!(provider === null || provider === void 0 ? void 0 : provider.setZoom) || !Number.isFinite(d) || d <= 0)
76
+ return;
77
+ pinch = { distance: d, scale: provider.getState().scale, provider };
78
+ event.preventDefault();
79
+ };
80
+ const move = (event) => {
81
+ if (!pinch || event.touches.length !== 2 || !allowed('pinch') || pinch.provider !== options.provider()) {
82
+ pinch = null;
83
+ return;
84
+ }
85
+ const d = distance(event.touches);
86
+ if (!Number.isFinite(d) || d <= 0)
87
+ return;
88
+ queue(pinch.scale * d / pinch.distance, pinch.provider);
89
+ event.preventDefault();
90
+ };
91
+ const end = (event) => { if (event.touches.length !== 2)
92
+ pinch = null; };
93
+ container.addEventListener('wheel', wheel, { passive: false });
94
+ container.addEventListener('touchstart', start, { passive: false });
95
+ container.addEventListener('touchmove', move, { passive: false });
96
+ container.addEventListener('touchend', end);
97
+ container.addEventListener('touchcancel', end);
98
+ return () => {
99
+ disposed = true;
100
+ pending = null;
101
+ pendingProvider = null;
102
+ pinch = null;
103
+ if (frame)
104
+ view.cancelAnimationFrame(frame);
105
+ container.removeEventListener('wheel', wheel);
106
+ container.removeEventListener('touchstart', start);
107
+ container.removeEventListener('touchmove', move);
108
+ container.removeEventListener('touchend', end);
109
+ container.removeEventListener('touchcancel', end);
110
+ };
111
+ }
@@ -1,4 +1,4 @@
1
- import type { FileViewerPrintMaskOptions, FileViewerPrintMaskRegion, FileViewerPrintStamp } from '../contracts/types';
1
+ import type { FileViewerPrintMaskOptions, FileViewerPrintMaskRegion, FileViewerPrintStamp } from "../contracts/types.js";
2
2
  export declare const normalizeFileViewerPrintMaskRegion: (region: Partial<FileViewerPrintMaskRegion> | null | undefined) => FileViewerPrintMaskRegion | null;
3
3
  export declare const normalizeFileViewerPrintStamp: (stamp: Partial<FileViewerPrintStamp> | null | undefined) => FileViewerPrintStamp | null;
4
4
  export declare const normalizeFileViewerPrintMaskOptions: (mask?: FileViewerPrintMaskOptions | null) => FileViewerPrintMaskOptions | null;
@@ -1,5 +1,5 @@
1
- import type { FileViewerPrintMaskOptions, FileViewerPrintMaskRegion, FileViewerPrintStamp } from '../contracts/types';
2
- import { type FileViewerI18nInput } from '../i18n/messages';
1
+ import type { FileViewerPrintMaskOptions, FileViewerPrintMaskRegion, FileViewerPrintStamp } from "../contracts/types.js";
2
+ import { type FileViewerI18nInput } from "../i18n/messages.js";
3
3
  export interface OpenFileViewerPrintMaskDesignerOptions {
4
4
  root: HTMLElement;
5
5
  pages?: readonly HTMLElement[];
@@ -11,8 +11,4 @@ export interface OpenFileViewerPrintMaskDesignerOptions {
11
11
  export interface FileViewerPrintMaskDesignerResult {
12
12
  mask: FileViewerPrintMaskOptions;
13
13
  }
14
- /**
15
- * Opens a page-aware print-mask designer. Browsing remains the default mode;
16
- * drawing is armed for the currently visible page only and disarms after one block.
17
- */
18
14
  export declare const openFileViewerPrintMaskDesigner: (options: OpenFileViewerPrintMaskDesignerOptions) => Promise<FileViewerPrintMaskDesignerResult | null>;
@@ -1,5 +1,5 @@
1
- import { createFileViewerTranslator } from '../i18n/messages.js';
2
- import { normalizeFileViewerPrintMaskRegion, normalizeFileViewerPrintStamp, } from './printMask.js';
1
+ import { createFileViewerTranslator } from "../i18n/messages.js";
2
+ import { normalizeFileViewerPrintMaskRegion, normalizeFileViewerPrintStamp, } from "./printMask.js";
3
3
  const DESIGNER_STYLE = `
4
4
  .fv-print-mask-layer{position:absolute;inset:0;z-index:2147483000;pointer-events:none;}
5
5
  .fv-print-mask-canvas{position:absolute;inset:0;z-index:2147483000;pointer-events:none;cursor:default;}
@@ -74,10 +74,6 @@ const readStampFileAsDataUrl = (file, windowRef) => new Promise((resolve, reject
74
74
  reader.addEventListener('error', () => reject(reader.error || new Error('Failed to read print stamp image')));
75
75
  reader.readAsDataURL(file);
76
76
  });
77
- /**
78
- * Opens a page-aware print-mask designer. Browsing remains the default mode;
79
- * drawing is armed for the currently visible page only and disarms after one block.
80
- */
81
77
  export const openFileViewerPrintMaskDesigner = (options) => {
82
78
  const root = options.root;
83
79
  const documentRef = root.ownerDocument;
@@ -105,8 +101,6 @@ export const openFileViewerPrintMaskDesigner = (options) => {
105
101
  }
106
102
  return previousPosition;
107
103
  };
108
- // The toolbar layer is positioned against the viewer root even when page
109
- // canvases live on child page elements.
110
104
  const previousRootPosition = ensureOverlayContainingBlock(root);
111
105
  return new Promise(resolve => {
112
106
  let settled = false;
@@ -316,7 +310,6 @@ export const openFileViewerPrintMaskDesigner = (options) => {
316
310
  (_a = stampElement.releasePointerCapture) === null || _a === void 0 ? void 0 : _a.call(stampElement, interaction.pointerId);
317
311
  }
318
312
  catch {
319
- // Pointer capture may already be released by the browser.
320
313
  }
321
314
  interaction = null;
322
315
  };
@@ -371,7 +364,6 @@ export const openFileViewerPrintMaskDesigner = (options) => {
371
364
  surface.canvas.releasePointerCapture(drawing.pointerId);
372
365
  }
373
366
  catch {
374
- // Pointer capture may already be released by the browser.
375
367
  }
376
368
  drawing = null;
377
369
  disarm();
@@ -435,7 +427,6 @@ export const openFileViewerPrintMaskDesigner = (options) => {
435
427
  }
436
428
  }
437
429
  catch {
438
- // Unsupported or unreadable images are ignored; the document stays untouched.
439
430
  }
440
431
  });
441
432
  clearButton.addEventListener('click', () => {
@@ -1,8 +1,2 @@
1
- import type { FileViewerPrintMaskDesignerResult, OpenFileViewerPrintMaskDesignerOptions } from './printMaskDesigner';
2
- /**
3
- * Lazily opens the print-mask designer.
4
- * Kept as a dynamic import so the designer stays out of the main core graph,
5
- * while remaining reachable through the primary `@file-viewer/core` entry
6
- * (no consumer alias / subpath setup required).
7
- */
1
+ import type { FileViewerPrintMaskDesignerResult, OpenFileViewerPrintMaskDesignerOptions } from "./printMaskDesigner.js";
8
2
  export declare const openFileViewerPrintMaskDesignerAsync: (options: OpenFileViewerPrintMaskDesignerOptions) => Promise<FileViewerPrintMaskDesignerResult | null>;
@@ -1,10 +1,4 @@
1
- /**
2
- * Lazily opens the print-mask designer.
3
- * Kept as a dynamic import so the designer stays out of the main core graph,
4
- * while remaining reachable through the primary `@file-viewer/core` entry
5
- * (no consumer alias / subpath setup required).
6
- */
7
1
  export const openFileViewerPrintMaskDesignerAsync = async (options) => {
8
- const { openFileViewerPrintMaskDesigner } = await import('../print-mask.js');
2
+ const { openFileViewerPrintMaskDesigner } = await import("../print-mask.js");
9
3
  return openFileViewerPrintMaskDesigner(options);
10
4
  };
@@ -1,4 +1,4 @@
1
- import type { FileViewerWatermarkOptions } from '../contracts/types';
1
+ import type { FileViewerWatermarkOptions } from "../contracts/types.js";
2
2
  export type FileViewerWatermarkStyle = Record<string, string> & {
3
3
  backgroundImage: string;
4
4
  };