@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.
- package/README.en.md +26 -15
- package/README.md +47 -41
- package/README.zh-CN.md +175 -0
- package/dist/assets.d.ts +2 -2
- package/dist/assets.js +5 -1
- package/dist/browser/controller.d.ts +2 -1
- package/dist/browser/controller.js +156 -32
- package/dist/browser/source-feedback.d.ts +4 -0
- package/dist/browser/source-feedback.js +40 -0
- package/dist/browser/toolbar-focus.d.ts +2 -0
- package/dist/browser/toolbar-focus.js +46 -0
- package/dist/browser.d.ts +2 -2
- package/dist/browser.js +2 -2
- package/dist/config/options.d.ts +1 -1
- package/dist/contracts/types.d.ts +479 -1
- package/dist/contracts/xml.d.ts +11 -0
- package/dist/export.d.ts +10 -3
- package/dist/export.js +9 -2
- package/dist/exportDocument.d.ts +12 -1
- package/dist/exportDocument.js +31 -2
- package/dist/features/document/dom/anchors.d.ts +1 -1
- package/dist/features/document/dom/index.d.ts +5 -5
- package/dist/features/document/dom/index.js +3 -3
- package/dist/features/document/dom/providers.d.ts +1 -1
- package/dist/features/document/events.d.ts +2 -2
- package/dist/features/document/events.js +3 -3
- package/dist/features/document/fit.d.ts +1 -1
- package/dist/features/document/fit.js +1 -1
- package/dist/features/document/imageHandTool.d.ts +3 -0
- package/dist/features/document/imageHandTool.js +141 -0
- package/dist/features/document/model.d.ts +1 -1
- package/dist/features/document/search.d.ts +1 -1
- package/dist/features/document/search.js +2 -2
- package/dist/features/document/viewState.d.ts +1 -1
- package/dist/features/document/viewState.js +2 -2
- package/dist/features/document/zoom.d.ts +1 -1
- package/dist/features/document/zoom.js +3 -3
- package/dist/features/document/zoomGestures.d.ts +2 -2
- package/dist/features/document/zoomGestures.js +2 -0
- package/dist/features/printMask.d.ts +1 -1
- package/dist/features/printMaskDesigner.d.ts +6 -2
- package/dist/features/printMaskDesigner.js +11 -2
- package/dist/features/printMaskLoader.d.ts +7 -1
- package/dist/features/printMaskLoader.js +7 -1
- package/dist/features/watermark.d.ts +1 -1
- package/dist/headless.d.ts +29 -29
- package/dist/headless.js +21 -16
- package/dist/i18n/messages.d.ts +1 -1
- package/dist/i18n/messages.de.d.ts +5 -1
- package/dist/i18n/messages.de.js +4 -0
- package/dist/i18n/messages.ja.d.ts +6 -1
- package/dist/i18n/messages.ja.js +5 -0
- package/dist/i18n/messages.js +2 -2
- package/dist/index.d.ts +77 -75
- package/dist/index.js +39 -37
- package/dist/lifecycle/facade.d.ts +4 -4
- package/dist/lifecycle/facade.js +2 -2
- package/dist/lifecycle/operations.d.ts +6 -4
- package/dist/lifecycle/operations.js +21 -4
- package/dist/output/canvasImage.d.ts +2 -1
- package/dist/output/canvasImage.js +4 -1
- package/dist/output/export.d.ts +3 -1
- package/dist/output/export.js +12 -0
- package/dist/output/printLayout.d.ts +5 -0
- package/dist/output/printLayout.js +4 -0
- package/dist/output/snapshotDownload.d.ts +2 -1
- package/dist/output/snapshotDownload.js +3 -2
- package/dist/platform/assets.d.ts +9 -2
- package/dist/platform/assets.js +15 -1
- package/dist/platform/copiedAssets.d.ts +1 -0
- package/dist/platform/copiedAssets.js +2 -1
- package/dist/presentation/colorScheme.d.ts +3 -0
- package/dist/presentation/colorScheme.js +40 -0
- package/dist/presentation/state.d.ts +1 -1
- package/dist/presentation/state.js +3 -3
- package/dist/print-mask.d.ts +2 -2
- package/dist/print-mask.js +1 -1
- package/dist/registry/capabilities.d.ts +14 -1
- package/dist/registry/capabilities.js +15 -2
- package/dist/registry/formats.d.ts +1 -1
- package/dist/registry/formats.generated.d.ts +1 -0
- package/dist/registry/formats.generated.js +1 -0
- package/dist/registry/formats.js +2 -2
- package/dist/registry/registry.d.ts +15 -1
- package/dist/registry/registry.js +10 -2
- package/dist/renderers/image.d.ts +1 -1
- package/dist/renderers/image.js +13 -7
- package/dist/renderers/index.d.ts +1 -1
- package/dist/renderers/index.js +11 -5
- package/dist/rendering/dispatcher.d.ts +6 -1
- package/dist/rendering/dispatcher.js +3 -3
- package/dist/rendering/handler.d.ts +13 -3
- package/dist/rendering/handler.js +21 -9
- package/dist/security/svgResources.d.ts +10 -0
- package/dist/security/svgResources.js +12 -0
- package/dist/source/index.d.ts +3 -3
- package/dist/source/index.js +1 -1
- package/dist/source/loading.d.ts +12 -7
- package/dist/source/loading.js +22 -17
- package/dist/source/precheck.d.ts +13 -1
- package/dist/source/precheck.js +9 -2
- package/dist/source/textEncoding.d.ts +18 -0
- package/dist/source/textEncoding.js +15 -0
- package/dist/viewer/createViewer.d.ts +1 -1
- package/dist/viewer/createViewer.js +30 -17
- package/dist/viewer/loading.d.ts +6 -2
- package/dist/viewer/loading.js +9 -1
- package/dist/viewer/operations.d.ts +3 -3
- package/dist/viewer/operations.js +14 -6
- package/dist/viewer/state.d.ts +6 -2
- package/dist/viewer/state.js +8 -3
- package/package.json +14 -5
package/dist/renderers/image.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { registerFileViewerZoomProvider, unregisterFileViewerZoomProvider, } from
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
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
|
-
(((
|
|
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
|
|
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;
|
package/dist/renderers/index.js
CHANGED
|
@@ -1,17 +1,23 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
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(
|
|
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
|
|
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
|
|
2
|
-
import { createRendererRegistry } from
|
|
3
|
-
import { normalizeFileExtension } from
|
|
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
|
|
2
|
-
import { type FileViewerMutableAccessor, type MutableFileViewerRenderReadinessState } from
|
|
3
|
-
import type { FileRenderExportAdapter, FileRenderThumbnailAdapter, FileRenderContext, FileRenderHandler, FileViewerLifecycleContext, FileViewerOptions, FileViewerStyleIsolation, RenderSurface, RendererDefinition, RendererLoadContext, RendererLoader, RendererRegistry, RendererSession } from
|
|
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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
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);
|
package/dist/source/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { FileViewerFileRef, FileViewerSource, NormalizedFileViewerSource } from
|
|
2
|
-
export { DEFAULT_FILE_VIEWER_TEXT_FALLBACK_ENCODING, createFileViewerTextDecoder, decodeFileViewerTextBuffer, isSingleByteFileViewerTextEncoding, isValidFileViewerUtf8, resolveFileViewerTextEncoding, } from
|
|
3
|
-
export type { DecodedFileViewerText, FileViewerSingleByteTextEncoding, FileViewerTextEncoding, ResolvedFileViewerTextEncoding, ResolvedFileViewerTextSource, } from
|
|
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;
|
package/dist/source/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { DEFAULT_FILE_VIEWER_TEXT_FALLBACK_ENCODING, createFileViewerTextDecoder, decodeFileViewerTextBuffer, isSingleByteFileViewerTextEncoding, isValidFileViewerUtf8, resolveFileViewerTextEncoding, } from
|
|
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();
|
package/dist/source/loading.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { FileViewerFileRef, FileViewerLifecycleContext, FileViewerPdfOptions } from
|
|
2
|
-
import { type FileViewerLifecycleStateController } from
|
|
3
|
-
import { type FileViewerErrorMessageFormatter } from
|
|
4
|
-
import type { FileViewerI18nInput } from
|
|
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"];
|
package/dist/source/loading.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { buildFileViewerLifecycleContext, } from
|
|
2
|
-
import { resolveFileViewerPreviewMessages, } from
|
|
3
|
-
import { translateFileViewerMessage } from
|
|
4
|
-
import { DEFAULT_FILE_VIEWER_SOURCE_FILENAME, getExtension, normalizeFilename, readFileViewerBuffer, resolveFileViewerSourceFilename, wrapFileViewerFileRef, } from
|
|
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 =
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
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
|
|
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 {};
|
package/dist/source/precheck.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { DEFAULT_RENDERER_DEFINITIONS, DEFAULT_SUPPORTED_EXTENSIONS } from
|
|
2
|
-
import { getExtension, normalizeFileExtension, normalizeFilename, normalizeSource, } from
|
|
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
|
|
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;
|