@file-viewer/core 2.0.6 → 2.0.8
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/dist/contracts/types.d.ts +1 -0
- package/dist/platform/assets.d.ts +10 -6
- package/dist/platform/assets.js +25 -25
- package/dist/renderers/cad.js +1 -0
- package/dist/renderers/data.js +1 -1
- package/dist/renderers/drawing.js +274 -3
- package/dist/renderers/epub.js +1 -0
- package/dist/renderers/model.js +1 -0
- package/dist/renderers/ofd.js +1 -0
- package/dist/renderers/pdf.js +6 -1
- package/dist/renderers/typst.js +172 -24
- package/dist/renderers/umd.js +1 -1
- package/package.json +2 -2
|
@@ -107,6 +107,7 @@ export type FileViewerRenderedInstance = {
|
|
|
107
107
|
export interface FileViewerTypstOptions {
|
|
108
108
|
compilerWasmUrl?: string;
|
|
109
109
|
rendererWasmUrl?: string;
|
|
110
|
+
renderTimeoutMs?: number;
|
|
110
111
|
}
|
|
111
112
|
export interface FileViewerDataOptions {
|
|
112
113
|
sqlWasmUrl?: string;
|
|
@@ -6,10 +6,14 @@ export declare const DEFAULT_FILE_VIEWER_SPREADSHEET_WORKER_PATH = "vendor/xlsx/
|
|
|
6
6
|
export declare const DEFAULT_FILE_VIEWER_CAD_WASM_PATH = "wasm/cad/";
|
|
7
7
|
export declare const DEFAULT_FILE_VIEWER_CAD_WORKER_PATH = "wasm/cad/dwg-worker.js";
|
|
8
8
|
export declare const DEFAULT_FILE_VIEWER_CAD_DWF_WASM_PATH = "wasm/cad/dwfv-render.wasm";
|
|
9
|
-
export declare const DEFAULT_FILE_VIEWER_TYPST_COMPILER_WASM_URL = "
|
|
10
|
-
export declare const DEFAULT_FILE_VIEWER_TYPST_RENDERER_WASM_URL = "
|
|
9
|
+
export declare const DEFAULT_FILE_VIEWER_TYPST_COMPILER_WASM_URL = "wasm/typst/typst_ts_web_compiler_bg.wasm";
|
|
10
|
+
export declare const DEFAULT_FILE_VIEWER_TYPST_RENDERER_WASM_URL = "wasm/typst/typst_ts_renderer_bg.wasm";
|
|
11
|
+
export declare const FALLBACK_FILE_VIEWER_TYPST_COMPILER_WASM_URL = "https://cdn.jsdelivr.net/npm/@myriaddreamin/typst-ts-web-compiler@0.7.0/pkg/typst_ts_web_compiler_bg.wasm";
|
|
12
|
+
export declare const FALLBACK_FILE_VIEWER_TYPST_RENDERER_WASM_URL = "https://cdn.jsdelivr.net/npm/@myriaddreamin/typst-ts-renderer@0.7.0/pkg/typst_ts_renderer_bg.wasm";
|
|
13
|
+
export declare const DEFAULT_FILE_VIEWER_TYPST_COMPILER_WASM_PACKAGE_PATH = "@myriaddreamin/typst-ts-web-compiler/pkg/typst_ts_web_compiler_bg.wasm";
|
|
11
14
|
export declare const DEFAULT_FILE_VIEWER_TYPST_RENDERER_WASM_PACKAGE_PATH = "@myriaddreamin/typst-ts-renderer/pkg/typst_ts_renderer_bg.wasm";
|
|
12
|
-
export declare const DEFAULT_FILE_VIEWER_DATA_SQL_WASM_URL = "
|
|
15
|
+
export declare const DEFAULT_FILE_VIEWER_DATA_SQL_WASM_URL = "wasm/data/sql-wasm.wasm";
|
|
16
|
+
export declare const DEFAULT_FILE_VIEWER_DATA_SQL_WASM_PACKAGE_PATH = "sql.js/dist/sql-wasm.wasm";
|
|
13
17
|
export interface ResolveFileViewerAssetUrlOptions {
|
|
14
18
|
baseUrl?: string;
|
|
15
19
|
documentBaseUrl?: string;
|
|
@@ -54,9 +58,9 @@ export declare const resolveFileViewerArchiveWasmUrl: (options?: Pick<FileViewer
|
|
|
54
58
|
export declare const resolveFileViewerCadAssetUrls: (options?: Pick<FileViewerCadOptions, "wasmPath" | "workerUrl" | "dwfWasmUrl"> | null, documentBaseUrl?: string) => ResolvedFileViewerCadAssetUrls;
|
|
55
59
|
export declare const resolveFileViewerDocxWorkerUrl: (options?: Pick<FileViewerDocxOptions, "workerUrl"> | null, documentBaseUrl?: string) => string;
|
|
56
60
|
export declare const resolveFileViewerSpreadsheetWorkerUrl: (options?: Pick<FileViewerSpreadsheetOptions, "workerUrl"> | null, documentBaseUrl?: string) => string;
|
|
57
|
-
export declare const resolveFileViewerTypstCompilerWasmUrl: (options?: Pick<FileViewerTypstOptions, "compilerWasmUrl"> | null, overrides?: Array<string | undefined
|
|
58
|
-
export declare const resolveFileViewerTypstRendererWasmUrl: (options?: Pick<FileViewerTypstOptions, "rendererWasmUrl"> | null, overrides?: Array<string | undefined
|
|
59
|
-
export declare const resolveFileViewerDataSqlWasmUrl: (options?: Pick<FileViewerDataOptions, "sqlWasmUrl"> | null, overrides?: Array<string | undefined
|
|
61
|
+
export declare const resolveFileViewerTypstCompilerWasmUrl: (options?: Pick<FileViewerTypstOptions, "compilerWasmUrl"> | null, overrides?: Array<string | undefined>, documentBaseUrl?: string) => string;
|
|
62
|
+
export declare const resolveFileViewerTypstRendererWasmUrl: (options?: Pick<FileViewerTypstOptions, "rendererWasmUrl"> | null, overrides?: Array<string | undefined>, documentBaseUrl?: string) => string;
|
|
63
|
+
export declare const resolveFileViewerDataSqlWasmUrl: (options?: Pick<FileViewerDataOptions, "sqlWasmUrl"> | null, overrides?: Array<string | undefined>, documentBaseUrl?: string) => string;
|
|
60
64
|
export declare const listFileViewerRendererAssetManifests: () => FileViewerRendererAssetManifest[];
|
|
61
65
|
export declare const getFileViewerRendererAssetManifest: (rendererId: string) => FileViewerRendererAssetManifest | null;
|
|
62
66
|
export declare const resolveFileViewerRendererAssets: (rendererId: string, resolveOptions?: ResolveFileViewerRendererAssetsOptions) => ResolvedFileViewerRendererAsset[];
|
package/dist/platform/assets.js
CHANGED
|
@@ -5,10 +5,14 @@ export const DEFAULT_FILE_VIEWER_SPREADSHEET_WORKER_PATH = 'vendor/xlsx/sheet.wo
|
|
|
5
5
|
export const DEFAULT_FILE_VIEWER_CAD_WASM_PATH = 'wasm/cad/';
|
|
6
6
|
export const DEFAULT_FILE_VIEWER_CAD_WORKER_PATH = 'wasm/cad/dwg-worker.js';
|
|
7
7
|
export const DEFAULT_FILE_VIEWER_CAD_DWF_WASM_PATH = 'wasm/cad/dwfv-render.wasm';
|
|
8
|
-
export const DEFAULT_FILE_VIEWER_TYPST_COMPILER_WASM_URL = '
|
|
9
|
-
export const DEFAULT_FILE_VIEWER_TYPST_RENDERER_WASM_URL = '
|
|
8
|
+
export const DEFAULT_FILE_VIEWER_TYPST_COMPILER_WASM_URL = 'wasm/typst/typst_ts_web_compiler_bg.wasm';
|
|
9
|
+
export const DEFAULT_FILE_VIEWER_TYPST_RENDERER_WASM_URL = 'wasm/typst/typst_ts_renderer_bg.wasm';
|
|
10
|
+
export const FALLBACK_FILE_VIEWER_TYPST_COMPILER_WASM_URL = 'https://cdn.jsdelivr.net/npm/@myriaddreamin/typst-ts-web-compiler@0.7.0/pkg/typst_ts_web_compiler_bg.wasm';
|
|
11
|
+
export const FALLBACK_FILE_VIEWER_TYPST_RENDERER_WASM_URL = 'https://cdn.jsdelivr.net/npm/@myriaddreamin/typst-ts-renderer@0.7.0/pkg/typst_ts_renderer_bg.wasm';
|
|
12
|
+
export const DEFAULT_FILE_VIEWER_TYPST_COMPILER_WASM_PACKAGE_PATH = '@myriaddreamin/typst-ts-web-compiler/pkg/typst_ts_web_compiler_bg.wasm';
|
|
10
13
|
export const DEFAULT_FILE_VIEWER_TYPST_RENDERER_WASM_PACKAGE_PATH = '@myriaddreamin/typst-ts-renderer/pkg/typst_ts_renderer_bg.wasm';
|
|
11
|
-
export const DEFAULT_FILE_VIEWER_DATA_SQL_WASM_URL = '
|
|
14
|
+
export const DEFAULT_FILE_VIEWER_DATA_SQL_WASM_URL = 'wasm/data/sql-wasm.wasm';
|
|
15
|
+
export const DEFAULT_FILE_VIEWER_DATA_SQL_WASM_PACKAGE_PATH = 'sql.js/dist/sql-wasm.wasm';
|
|
12
16
|
export const DEFAULT_FILE_VIEWER_RENDERER_ASSET_MANIFESTS = [
|
|
13
17
|
{
|
|
14
18
|
rendererId: 'archive',
|
|
@@ -107,22 +111,23 @@ export const DEFAULT_FILE_VIEWER_RENDERER_ASSET_MANIFESTS = [
|
|
|
107
111
|
id: 'typst-compiler-wasm',
|
|
108
112
|
rendererId: 'typst',
|
|
109
113
|
kind: 'wasm',
|
|
110
|
-
target: '
|
|
114
|
+
target: 'public',
|
|
111
115
|
required: true,
|
|
112
|
-
|
|
116
|
+
defaultPath: DEFAULT_FILE_VIEWER_TYPST_COMPILER_WASM_URL,
|
|
117
|
+
packagePath: DEFAULT_FILE_VIEWER_TYPST_COMPILER_WASM_PACKAGE_PATH,
|
|
113
118
|
optionPath: 'typst.compilerWasmUrl',
|
|
114
|
-
description: 'Typst compiler WebAssembly module
|
|
119
|
+
description: 'Typst compiler WebAssembly module copied to the public assets directory.',
|
|
115
120
|
},
|
|
116
121
|
{
|
|
117
122
|
id: 'typst-renderer-wasm',
|
|
118
123
|
rendererId: 'typst',
|
|
119
|
-
kind: '
|
|
120
|
-
target: '
|
|
124
|
+
kind: 'wasm',
|
|
125
|
+
target: 'public',
|
|
121
126
|
required: true,
|
|
122
|
-
|
|
127
|
+
defaultPath: DEFAULT_FILE_VIEWER_TYPST_RENDERER_WASM_URL,
|
|
123
128
|
packagePath: DEFAULT_FILE_VIEWER_TYPST_RENDERER_WASM_PACKAGE_PATH,
|
|
124
129
|
optionPath: 'typst.rendererWasmUrl',
|
|
125
|
-
description: 'Typst SVG renderer WebAssembly module
|
|
130
|
+
description: 'Typst SVG renderer WebAssembly module copied to the public assets directory.',
|
|
126
131
|
},
|
|
127
132
|
],
|
|
128
133
|
},
|
|
@@ -133,11 +138,12 @@ export const DEFAULT_FILE_VIEWER_RENDERER_ASSET_MANIFESTS = [
|
|
|
133
138
|
id: 'data-sql-wasm',
|
|
134
139
|
rendererId: 'data-asset',
|
|
135
140
|
kind: 'wasm',
|
|
136
|
-
target: '
|
|
141
|
+
target: 'public',
|
|
137
142
|
required: false,
|
|
138
|
-
|
|
143
|
+
defaultPath: DEFAULT_FILE_VIEWER_DATA_SQL_WASM_URL,
|
|
144
|
+
packagePath: DEFAULT_FILE_VIEWER_DATA_SQL_WASM_PACKAGE_PATH,
|
|
139
145
|
optionPath: 'data.sqlWasmUrl',
|
|
140
|
-
description: 'sql.js WebAssembly module
|
|
146
|
+
description: 'sql.js WebAssembly module copied to the public assets directory for SQLite previews.',
|
|
141
147
|
},
|
|
142
148
|
],
|
|
143
149
|
},
|
|
@@ -188,20 +194,14 @@ export const resolveFileViewerSpreadsheetWorkerUrl = (options, documentBaseUrl)
|
|
|
188
194
|
documentBaseUrl,
|
|
189
195
|
});
|
|
190
196
|
};
|
|
191
|
-
export const resolveFileViewerTypstCompilerWasmUrl = (options, overrides = []) => {
|
|
192
|
-
return (options === null || options === void 0 ? void 0 : options.compilerWasmUrl) ||
|
|
193
|
-
overrides.find(Boolean) ||
|
|
194
|
-
DEFAULT_FILE_VIEWER_TYPST_COMPILER_WASM_URL;
|
|
197
|
+
export const resolveFileViewerTypstCompilerWasmUrl = (options, overrides = [], documentBaseUrl) => {
|
|
198
|
+
return resolveFileViewerAssetUrl((options === null || options === void 0 ? void 0 : options.compilerWasmUrl) || overrides.find(Boolean), DEFAULT_FILE_VIEWER_TYPST_COMPILER_WASM_URL, { documentBaseUrl });
|
|
195
199
|
};
|
|
196
|
-
export const resolveFileViewerTypstRendererWasmUrl = (options, overrides = []) => {
|
|
197
|
-
return (options === null || options === void 0 ? void 0 : options.rendererWasmUrl) ||
|
|
198
|
-
overrides.find(Boolean) ||
|
|
199
|
-
DEFAULT_FILE_VIEWER_TYPST_RENDERER_WASM_URL;
|
|
200
|
+
export const resolveFileViewerTypstRendererWasmUrl = (options, overrides = [], documentBaseUrl) => {
|
|
201
|
+
return resolveFileViewerAssetUrl((options === null || options === void 0 ? void 0 : options.rendererWasmUrl) || overrides.find(Boolean), DEFAULT_FILE_VIEWER_TYPST_RENDERER_WASM_URL, { documentBaseUrl });
|
|
200
202
|
};
|
|
201
|
-
export const resolveFileViewerDataSqlWasmUrl = (options, overrides = []) => {
|
|
202
|
-
return (options === null || options === void 0 ? void 0 : options.sqlWasmUrl) ||
|
|
203
|
-
overrides.find(Boolean) ||
|
|
204
|
-
DEFAULT_FILE_VIEWER_DATA_SQL_WASM_URL;
|
|
203
|
+
export const resolveFileViewerDataSqlWasmUrl = (options, overrides = [], documentBaseUrl) => {
|
|
204
|
+
return resolveFileViewerAssetUrl((options === null || options === void 0 ? void 0 : options.sqlWasmUrl) || overrides.find(Boolean), DEFAULT_FILE_VIEWER_DATA_SQL_WASM_URL, { documentBaseUrl });
|
|
205
205
|
};
|
|
206
206
|
export const listFileViewerRendererAssetManifests = () => {
|
|
207
207
|
return [...DEFAULT_FILE_VIEWER_RENDERER_ASSET_MANIFESTS];
|
package/dist/renderers/cad.js
CHANGED
|
@@ -68,6 +68,7 @@ const cadStyle = `
|
|
|
68
68
|
.cad-native-stage .dwfv-root,.cad-native-stage .dwfv-workspace,.cad-native-stage .dwfv-stage{width:100%;min-width:0;min-height:0}
|
|
69
69
|
.cad-native-stage .dwfv-root{height:100%}
|
|
70
70
|
.cad-state{position:absolute;inset:50% auto auto 50%;max-width:min(520px,calc(100% - 48px));transform:translate(-50%,-50%);border-radius:12px;padding:14px 18px;background:rgba(255,255,255,.92);box-shadow:0 14px 38px rgba(15,23,42,.12);color:#53637a;font-size:13px;font-weight:800;text-align:center}
|
|
71
|
+
.cad-state[hidden]{display:none!important}
|
|
71
72
|
.cad-state.error{color:#b42318}
|
|
72
73
|
.cad-inspector strong{display:block;margin-bottom:12px;color:#1f2a3d;font-size:13px}
|
|
73
74
|
.cad-inspector dl{display:grid;gap:8px;margin:0}
|
package/dist/renderers/data.js
CHANGED
|
@@ -144,7 +144,7 @@ const renderSqlite = async (documentRef, buffer, context) => {
|
|
|
144
144
|
const { default: initSqlJs } = await import('sql.js');
|
|
145
145
|
const sqlWasmUrl = resolveFileViewerDataSqlWasmUrl((_a = context === null || context === void 0 ? void 0 : context.options) === null || _a === void 0 ? void 0 : _a.data, [
|
|
146
146
|
getWindowSqlWasmOverride(documentRef),
|
|
147
|
-
]);
|
|
147
|
+
], documentRef.baseURI);
|
|
148
148
|
const SQL = await initSqlJs({ locateFile: () => sqlWasmUrl });
|
|
149
149
|
const db = new SQL.Database(new Uint8Array(buffer));
|
|
150
150
|
try {
|
|
@@ -5,6 +5,7 @@ import { readFileViewerText } from '../source/index.js';
|
|
|
5
5
|
const DIAGRAMS_VIEWER_URL = 'https://viewer.diagrams.net/js/viewer-static.min.js';
|
|
6
6
|
const SVG_NS = 'http://www.w3.org/2000/svg';
|
|
7
7
|
const EXCALIDRAW_OFFICIAL_TIMEOUT = 6000;
|
|
8
|
+
const DRAWIO_OFFICIAL_TIMEOUT = 6000;
|
|
8
9
|
const diagramsViewerPromises = new WeakMap();
|
|
9
10
|
const drawingStyle = `
|
|
10
11
|
.drawing-viewer{display:flex;height:100%;min-height:360px;flex-direction:column;background:#edf2f7;color:#172033}
|
|
@@ -22,6 +23,7 @@ const drawingStyle = `
|
|
|
22
23
|
.drawing-canvas .drawing-svg,.drawing-canvas svg{display:block;max-width:100%;height:auto;margin:0 auto;border-radius:10px;background:#fff;box-shadow:0 18px 42px rgba(15,23,42,.12)}
|
|
23
24
|
.drawing-canvas .drawing-mxgraph{min-height:420px;overflow:hidden;border-radius:10px;background:#fff;box-shadow:0 18px 42px rgba(15,23,42,.12)}
|
|
24
25
|
.drawing-state{position:absolute;inset:0;z-index:1;display:flex;align-items:center;justify-content:center;padding:24px;color:#64748b;font-size:14px;font-weight:700;text-align:center}
|
|
26
|
+
.drawing-state[hidden]{display:none!important}
|
|
25
27
|
.drawing-state.error{color:#b42318}
|
|
26
28
|
@media (max-width:720px){.drawing-toolbar{align-items:flex-start;flex-direction:column}.drawing-actions{width:100%;justify-content:space-between}.drawing-scroll{padding:12px}}
|
|
27
29
|
`;
|
|
@@ -120,7 +122,8 @@ const suppressExcalidrawWorkerWarning = () => {
|
|
|
120
122
|
const originalError = console.error;
|
|
121
123
|
const patchedError = (...args) => {
|
|
122
124
|
const message = args.map(arg => String(arg)).join(' ');
|
|
123
|
-
if (message.includes('Failed to use workers for subsetting')
|
|
125
|
+
if (message.includes('Failed to use workers for subsetting') ||
|
|
126
|
+
message.includes('Failed to fetch font family')) {
|
|
124
127
|
return;
|
|
125
128
|
}
|
|
126
129
|
originalError(...args);
|
|
@@ -361,7 +364,7 @@ const renderOfficialExcalidraw = async (payload, elements, target) => {
|
|
|
361
364
|
}
|
|
362
365
|
finally {
|
|
363
366
|
clearTimeout(restoreTimer);
|
|
364
|
-
restoreConsole
|
|
367
|
+
setTimeout(restoreConsole, 3000);
|
|
365
368
|
}
|
|
366
369
|
};
|
|
367
370
|
const renderExcalidraw = async (documentRef, text, target) => {
|
|
@@ -380,7 +383,262 @@ const renderExcalidraw = async (documentRef, text, target) => {
|
|
|
380
383
|
await renderRoughFallback(documentRef, payload, elements, target);
|
|
381
384
|
}
|
|
382
385
|
};
|
|
383
|
-
const
|
|
386
|
+
const getDirectChild = (parent, tagName) => {
|
|
387
|
+
return Array.from(parent.children).find(child => child.localName === tagName) || null;
|
|
388
|
+
};
|
|
389
|
+
const parseDrawioStyle = (style) => {
|
|
390
|
+
const entries = new Map();
|
|
391
|
+
for (const item of (style || '').split(';')) {
|
|
392
|
+
if (!item) {
|
|
393
|
+
continue;
|
|
394
|
+
}
|
|
395
|
+
const [key, ...rest] = item.split('=');
|
|
396
|
+
entries.set(key, rest.join('=') || '1');
|
|
397
|
+
}
|
|
398
|
+
return entries;
|
|
399
|
+
};
|
|
400
|
+
const getDrawioStyleValue = (style, key, fallback) => {
|
|
401
|
+
const value = style.get(key);
|
|
402
|
+
return value && value !== 'none' ? value : fallback;
|
|
403
|
+
};
|
|
404
|
+
const parseDrawioGeometry = (cell) => {
|
|
405
|
+
const geometry = getDirectChild(cell, 'mxGeometry');
|
|
406
|
+
if (!geometry) {
|
|
407
|
+
return null;
|
|
408
|
+
}
|
|
409
|
+
return {
|
|
410
|
+
x: toNumber(geometry.getAttribute('x')),
|
|
411
|
+
y: toNumber(geometry.getAttribute('y')),
|
|
412
|
+
width: Math.max(1, toNumber(geometry.getAttribute('width'), 80)),
|
|
413
|
+
height: Math.max(1, toNumber(geometry.getAttribute('height'), 40)),
|
|
414
|
+
points: Array.from(geometry.querySelectorAll('mxPoint')).map(point => ({
|
|
415
|
+
x: toNumber(point.getAttribute('x')),
|
|
416
|
+
y: toNumber(point.getAttribute('y')),
|
|
417
|
+
})),
|
|
418
|
+
};
|
|
419
|
+
};
|
|
420
|
+
const normalizeDrawioText = (documentRef, value) => {
|
|
421
|
+
if (!value) {
|
|
422
|
+
return '';
|
|
423
|
+
}
|
|
424
|
+
const helper = documentRef.createElement('textarea');
|
|
425
|
+
helper.innerHTML = value;
|
|
426
|
+
return helper.value
|
|
427
|
+
.replace(/<br\s*\/?>/gi, '\n')
|
|
428
|
+
.replace(/<[^>]+>/g, '')
|
|
429
|
+
.replace(/\u00a0/g, ' ')
|
|
430
|
+
.replace(/[ \t]+/g, ' ')
|
|
431
|
+
.trim();
|
|
432
|
+
};
|
|
433
|
+
const appendDrawioWrappedText = (documentRef, svg, text, x, y, width, height, fontSize, fill) => {
|
|
434
|
+
if (!text) {
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
const textNode = createSvgElement(documentRef, 'text');
|
|
438
|
+
const maxChars = Math.max(4, Math.floor(width / Math.max(7, fontSize * 0.55)));
|
|
439
|
+
const words = text.includes(' ') ? text.split(/\s+/) : text.match(new RegExp(`.{1,${maxChars}}`, 'g')) || [text];
|
|
440
|
+
const lines = [];
|
|
441
|
+
let current = '';
|
|
442
|
+
for (const word of words) {
|
|
443
|
+
const next = current ? `${current} ${word}` : word;
|
|
444
|
+
if (next.length > maxChars && current) {
|
|
445
|
+
lines.push(current);
|
|
446
|
+
current = word;
|
|
447
|
+
}
|
|
448
|
+
else {
|
|
449
|
+
current = next;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
if (current) {
|
|
453
|
+
lines.push(current);
|
|
454
|
+
}
|
|
455
|
+
const lineHeight = fontSize * 1.24;
|
|
456
|
+
const totalHeight = lineHeight * lines.length;
|
|
457
|
+
textNode.setAttribute('x', String(x + width / 2));
|
|
458
|
+
textNode.setAttribute('y', String(y + height / 2 - totalHeight / 2 + fontSize));
|
|
459
|
+
textNode.setAttribute('fill', fill);
|
|
460
|
+
textNode.setAttribute('font-size', String(fontSize));
|
|
461
|
+
textNode.setAttribute('font-family', 'Inter, Segoe UI, Arial, sans-serif');
|
|
462
|
+
textNode.setAttribute('font-weight', '600');
|
|
463
|
+
textNode.setAttribute('text-anchor', 'middle');
|
|
464
|
+
textNode.setAttribute('pointer-events', 'none');
|
|
465
|
+
lines.slice(0, 5).forEach((line, index) => {
|
|
466
|
+
const lineNode = createSvgElement(documentRef, 'tspan');
|
|
467
|
+
lineNode.setAttribute('x', String(x + width / 2));
|
|
468
|
+
lineNode.setAttribute('dy', index === 0 ? '0' : String(lineHeight));
|
|
469
|
+
lineNode.textContent = line;
|
|
470
|
+
textNode.appendChild(lineNode);
|
|
471
|
+
});
|
|
472
|
+
svg.appendChild(textNode);
|
|
473
|
+
};
|
|
474
|
+
const renderDrawioFallback = (documentRef, text, target) => {
|
|
475
|
+
var _a;
|
|
476
|
+
const parser = new DOMParser();
|
|
477
|
+
const parsed = parser.parseFromString(text, 'text/xml');
|
|
478
|
+
const parseError = parsed.querySelector('parsererror');
|
|
479
|
+
if (parseError) {
|
|
480
|
+
throw new Error(`Draw.io XML 解析失败:${parseError.textContent || 'invalid xml'}`);
|
|
481
|
+
}
|
|
482
|
+
const firstDiagram = parsed.querySelector('diagram');
|
|
483
|
+
const graphModel = (firstDiagram === null || firstDiagram === void 0 ? void 0 : firstDiagram.querySelector('mxGraphModel')) || parsed.querySelector('mxGraphModel');
|
|
484
|
+
if (!graphModel) {
|
|
485
|
+
throw new Error('当前 Draw.io 文件没有可直接渲染的 mxGraphModel。');
|
|
486
|
+
}
|
|
487
|
+
const cells = Array.from(graphModel.querySelectorAll('mxCell'));
|
|
488
|
+
const vertices = cells
|
|
489
|
+
.filter(cell => cell.getAttribute('vertex') === '1' && cell.getAttribute('connectable') !== '0')
|
|
490
|
+
.map(cell => ({
|
|
491
|
+
cell,
|
|
492
|
+
id: cell.getAttribute('id') || '',
|
|
493
|
+
geometry: parseDrawioGeometry(cell),
|
|
494
|
+
style: parseDrawioStyle(cell.getAttribute('style')),
|
|
495
|
+
text: normalizeDrawioText(documentRef, cell.getAttribute('value')),
|
|
496
|
+
}))
|
|
497
|
+
.filter(item => item.id && item.geometry);
|
|
498
|
+
if (!vertices.length) {
|
|
499
|
+
throw new Error('当前 Draw.io 文件没有可预览图元。');
|
|
500
|
+
}
|
|
501
|
+
const vertexById = new Map(vertices.map(vertex => [vertex.id, vertex]));
|
|
502
|
+
const allX = vertices.flatMap(vertex => [vertex.geometry.x, vertex.geometry.x + vertex.geometry.width]);
|
|
503
|
+
const allY = vertices.flatMap(vertex => [vertex.geometry.y, vertex.geometry.y + vertex.geometry.height]);
|
|
504
|
+
const edgePoints = [];
|
|
505
|
+
const edges = cells.filter(cell => cell.getAttribute('edge') === '1');
|
|
506
|
+
for (const edge of edges) {
|
|
507
|
+
const source = vertexById.get(edge.getAttribute('source') || '');
|
|
508
|
+
const targetVertex = vertexById.get(edge.getAttribute('target') || '');
|
|
509
|
+
const geometry = parseDrawioGeometry(edge);
|
|
510
|
+
if (source === null || source === void 0 ? void 0 : source.geometry) {
|
|
511
|
+
edgePoints.push({
|
|
512
|
+
x: source.geometry.x + source.geometry.width / 2,
|
|
513
|
+
y: source.geometry.y + source.geometry.height / 2,
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
geometry === null || geometry === void 0 ? void 0 : geometry.points.forEach(point => edgePoints.push(point));
|
|
517
|
+
if (targetVertex === null || targetVertex === void 0 ? void 0 : targetVertex.geometry) {
|
|
518
|
+
edgePoints.push({
|
|
519
|
+
x: targetVertex.geometry.x + targetVertex.geometry.width / 2,
|
|
520
|
+
y: targetVertex.geometry.y + targetVertex.geometry.height / 2,
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
allX.push(...edgePoints.map(point => point.x));
|
|
525
|
+
allY.push(...edgePoints.map(point => point.y));
|
|
526
|
+
const padding = 96;
|
|
527
|
+
const minX = Math.min(...allX) - padding;
|
|
528
|
+
const minY = Math.min(...allY) - padding;
|
|
529
|
+
const width = Math.max(480, Math.max(...allX) - Math.min(...allX) + padding * 2);
|
|
530
|
+
const height = Math.max(320, Math.max(...allY) - Math.min(...allY) + padding * 2);
|
|
531
|
+
const svg = createSvgElement(documentRef, 'svg');
|
|
532
|
+
svg.setAttribute('viewBox', `${minX} ${minY} ${width} ${height}`);
|
|
533
|
+
svg.setAttribute('width', String(Math.ceil(width)));
|
|
534
|
+
svg.setAttribute('height', String(Math.ceil(height)));
|
|
535
|
+
svg.setAttribute('role', 'img');
|
|
536
|
+
svg.setAttribute('aria-label', (firstDiagram === null || firstDiagram === void 0 ? void 0 : firstDiagram.getAttribute('name')) || 'Draw.io local SVG preview');
|
|
537
|
+
const defs = createSvgElement(documentRef, 'defs');
|
|
538
|
+
const marker = createSvgElement(documentRef, 'marker');
|
|
539
|
+
marker.setAttribute('id', 'drawio-arrow');
|
|
540
|
+
marker.setAttribute('viewBox', '0 0 10 10');
|
|
541
|
+
marker.setAttribute('refX', '9');
|
|
542
|
+
marker.setAttribute('refY', '5');
|
|
543
|
+
marker.setAttribute('markerWidth', '7');
|
|
544
|
+
marker.setAttribute('markerHeight', '7');
|
|
545
|
+
marker.setAttribute('orient', 'auto-start-reverse');
|
|
546
|
+
const arrow = createSvgElement(documentRef, 'path');
|
|
547
|
+
arrow.setAttribute('d', 'M 0 0 L 10 5 L 0 10 z');
|
|
548
|
+
arrow.setAttribute('fill', '#64748b');
|
|
549
|
+
marker.appendChild(arrow);
|
|
550
|
+
defs.appendChild(marker);
|
|
551
|
+
svg.appendChild(defs);
|
|
552
|
+
const background = createSvgElement(documentRef, 'rect');
|
|
553
|
+
background.setAttribute('x', String(minX));
|
|
554
|
+
background.setAttribute('y', String(minY));
|
|
555
|
+
background.setAttribute('width', String(width));
|
|
556
|
+
background.setAttribute('height', String(height));
|
|
557
|
+
background.setAttribute('fill', '#ffffff');
|
|
558
|
+
svg.appendChild(background);
|
|
559
|
+
for (const edge of edges) {
|
|
560
|
+
const source = vertexById.get(edge.getAttribute('source') || '');
|
|
561
|
+
const targetVertex = vertexById.get(edge.getAttribute('target') || '');
|
|
562
|
+
if (!(source === null || source === void 0 ? void 0 : source.geometry) || !(targetVertex === null || targetVertex === void 0 ? void 0 : targetVertex.geometry)) {
|
|
563
|
+
continue;
|
|
564
|
+
}
|
|
565
|
+
const style = parseDrawioStyle(edge.getAttribute('style'));
|
|
566
|
+
const stroke = getDrawioStyleValue(style, 'strokeColor', '#64748b');
|
|
567
|
+
const points = [
|
|
568
|
+
{
|
|
569
|
+
x: source.geometry.x + source.geometry.width / 2,
|
|
570
|
+
y: source.geometry.y + source.geometry.height / 2,
|
|
571
|
+
},
|
|
572
|
+
...(((_a = parseDrawioGeometry(edge)) === null || _a === void 0 ? void 0 : _a.points) || []),
|
|
573
|
+
{
|
|
574
|
+
x: targetVertex.geometry.x + targetVertex.geometry.width / 2,
|
|
575
|
+
y: targetVertex.geometry.y + targetVertex.geometry.height / 2,
|
|
576
|
+
},
|
|
577
|
+
];
|
|
578
|
+
const polyline = createSvgElement(documentRef, 'polyline');
|
|
579
|
+
polyline.setAttribute('points', points.map(point => `${point.x},${point.y}`).join(' '));
|
|
580
|
+
polyline.setAttribute('fill', 'none');
|
|
581
|
+
polyline.setAttribute('stroke', stroke);
|
|
582
|
+
polyline.setAttribute('stroke-width', '2');
|
|
583
|
+
if (style.get('dashed') === '1') {
|
|
584
|
+
polyline.setAttribute('stroke-dasharray', '8 7');
|
|
585
|
+
}
|
|
586
|
+
if (style.get('endArrow') !== 'none') {
|
|
587
|
+
polyline.setAttribute('marker-end', 'url(#drawio-arrow)');
|
|
588
|
+
}
|
|
589
|
+
svg.appendChild(polyline);
|
|
590
|
+
}
|
|
591
|
+
for (const vertex of vertices) {
|
|
592
|
+
const geometry = vertex.geometry;
|
|
593
|
+
const fill = getDrawioStyleValue(vertex.style, 'fillColor', '#f8fafc');
|
|
594
|
+
const stroke = getDrawioStyleValue(vertex.style, 'strokeColor', '#64748b');
|
|
595
|
+
const fontSize = Math.max(10, toNumber(vertex.style.get('fontSize'), 14));
|
|
596
|
+
const shape = vertex.style.has('ellipse')
|
|
597
|
+
? 'ellipse'
|
|
598
|
+
: vertex.style.get('shape') === 'rhombus'
|
|
599
|
+
? 'diamond'
|
|
600
|
+
: 'rect';
|
|
601
|
+
if (shape === 'ellipse') {
|
|
602
|
+
const ellipse = createSvgElement(documentRef, 'ellipse');
|
|
603
|
+
ellipse.setAttribute('cx', String(geometry.x + geometry.width / 2));
|
|
604
|
+
ellipse.setAttribute('cy', String(geometry.y + geometry.height / 2));
|
|
605
|
+
ellipse.setAttribute('rx', String(geometry.width / 2));
|
|
606
|
+
ellipse.setAttribute('ry', String(geometry.height / 2));
|
|
607
|
+
ellipse.setAttribute('fill', fill);
|
|
608
|
+
ellipse.setAttribute('stroke', stroke);
|
|
609
|
+
ellipse.setAttribute('stroke-width', '2');
|
|
610
|
+
svg.appendChild(ellipse);
|
|
611
|
+
}
|
|
612
|
+
else if (shape === 'diamond') {
|
|
613
|
+
const diamond = createSvgElement(documentRef, 'polygon');
|
|
614
|
+
diamond.setAttribute('points', [
|
|
615
|
+
`${geometry.x + geometry.width / 2},${geometry.y}`,
|
|
616
|
+
`${geometry.x + geometry.width},${geometry.y + geometry.height / 2}`,
|
|
617
|
+
`${geometry.x + geometry.width / 2},${geometry.y + geometry.height}`,
|
|
618
|
+
`${geometry.x},${geometry.y + geometry.height / 2}`,
|
|
619
|
+
].join(' '));
|
|
620
|
+
diamond.setAttribute('fill', fill);
|
|
621
|
+
diamond.setAttribute('stroke', stroke);
|
|
622
|
+
diamond.setAttribute('stroke-width', '2');
|
|
623
|
+
svg.appendChild(diamond);
|
|
624
|
+
}
|
|
625
|
+
else {
|
|
626
|
+
const rect = createSvgElement(documentRef, 'rect');
|
|
627
|
+
rect.setAttribute('x', String(geometry.x));
|
|
628
|
+
rect.setAttribute('y', String(geometry.y));
|
|
629
|
+
rect.setAttribute('width', String(geometry.width));
|
|
630
|
+
rect.setAttribute('height', String(geometry.height));
|
|
631
|
+
rect.setAttribute('rx', vertex.style.get('rounded') === '1' ? '10' : '2');
|
|
632
|
+
rect.setAttribute('fill', fill);
|
|
633
|
+
rect.setAttribute('stroke', stroke);
|
|
634
|
+
rect.setAttribute('stroke-width', '2');
|
|
635
|
+
svg.appendChild(rect);
|
|
636
|
+
}
|
|
637
|
+
appendDrawioWrappedText(documentRef, svg, vertex.text, geometry.x, geometry.y, geometry.width, geometry.height, fontSize, getDrawioStyleValue(vertex.style, 'fontColor', '#172033'));
|
|
638
|
+
}
|
|
639
|
+
appendRenderedSvg(target, svg, 'rough');
|
|
640
|
+
};
|
|
641
|
+
const renderOfficialDrawio = async (documentRef, text, target) => {
|
|
384
642
|
const ownerWindow = documentRef.defaultView || (typeof window !== 'undefined' ? window : undefined);
|
|
385
643
|
await loadDiagramsViewer(documentRef);
|
|
386
644
|
await waitForFileViewerNextPaint(ownerWindow);
|
|
@@ -403,6 +661,19 @@ const renderDrawio = async (documentRef, text, target) => {
|
|
|
403
661
|
throw new Error('diagrams.net viewer 未正确初始化');
|
|
404
662
|
}
|
|
405
663
|
ownerWindow.GraphViewer.createViewerForElement(host);
|
|
664
|
+
markRendered(target, 'official');
|
|
665
|
+
};
|
|
666
|
+
const renderDrawio = async (documentRef, text, target) => {
|
|
667
|
+
try {
|
|
668
|
+
await runWithTimeout(renderOfficialDrawio(documentRef, text, target), DRAWIO_OFFICIAL_TIMEOUT, 'diagrams.net 官方 Viewer 加载超时,自动切换本地 SVG 预览');
|
|
669
|
+
}
|
|
670
|
+
catch (error) {
|
|
671
|
+
console.warn(error);
|
|
672
|
+
diagramsViewerPromises.delete(documentRef);
|
|
673
|
+
delete target.dataset.drawingRendered;
|
|
674
|
+
target.replaceChildren();
|
|
675
|
+
renderDrawioFallback(documentRef, text, target);
|
|
676
|
+
}
|
|
406
677
|
};
|
|
407
678
|
export default async function renderDrawing(buffer, target, type = 'drawio', _context) {
|
|
408
679
|
const documentRef = target.ownerDocument || document;
|
package/dist/renderers/epub.js
CHANGED
|
@@ -32,6 +32,7 @@ const epubStyle = `
|
|
|
32
32
|
.epub-stage .epub-container{width:100%!important;max-width:100%;overflow-x:hidden!important;overflow-y:auto!important}
|
|
33
33
|
.epub-stage iframe{max-width:100%}
|
|
34
34
|
.epub-state{position:absolute;inset:18px;display:flex;align-items:center;justify-content:center;border-radius:8px;background:rgba(255,255,255,.92);color:#64748b;font-size:14px}
|
|
35
|
+
.epub-state[hidden]{display:none!important}
|
|
35
36
|
.epub-state.error{color:#b42318}
|
|
36
37
|
.file-viewer[data-viewer-theme='dark'] .epub-viewer{background:#172033;color:#e5eef8}
|
|
37
38
|
.file-viewer[data-viewer-theme='dark'] .epub-toolbar,.file-viewer[data-viewer-theme='dark'] .epub-toc,.file-viewer[data-viewer-theme='dark'] .epub-stage{background:#fff;color:#172033}
|
package/dist/renderers/model.js
CHANGED
|
@@ -13,6 +13,7 @@ const modelStyle = `
|
|
|
13
13
|
.model-stage{position:relative;flex:1;min-height:0;overflow:hidden}
|
|
14
14
|
.model-stage canvas{display:block;width:100%;height:100%;outline:none}
|
|
15
15
|
.model-state{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:10px;padding:24px;background:rgba(248,250,252,.88);color:#64748b;text-align:center;line-height:1.7}
|
|
16
|
+
.model-state[hidden]{display:none!important}
|
|
16
17
|
.model-state.error{color:#b42318}
|
|
17
18
|
.model-state strong{color:#b42318;font-size:18px}
|
|
18
19
|
@media (max-width:720px){.model-toolbar{min-height:64px;align-items:flex-start;flex-direction:column;gap:8px;padding:8px 10px}.model-meta{width:100%;justify-content:flex-start}}
|
package/dist/renderers/ofd.js
CHANGED
|
@@ -8,6 +8,7 @@ const ofdStyle = `
|
|
|
8
8
|
.ofd-viewer *{box-sizing:border-box}
|
|
9
9
|
.ofd-stage{min-height:100%;padding:18px 0 28px;overflow:auto;scrollbar-gutter:stable}
|
|
10
10
|
.ofd-state{position:absolute;inset:0;z-index:1;display:flex;align-items:center;justify-content:center;background:rgba(246,248,250,.92);color:#64748b;font-size:14px}
|
|
11
|
+
.ofd-state[hidden]{display:none!important}
|
|
11
12
|
.ofd-state.error{color:#b42318}
|
|
12
13
|
.ofd-page-frame{position:relative;display:block;margin:0 auto 20px;overflow:visible}
|
|
13
14
|
.ofd-page{display:block;margin-left:auto!important;margin-right:auto!important;box-shadow:0 10px 26px rgba(15,23,42,.12);transition:transform .16s ease}
|
package/dist/renderers/pdf.js
CHANGED
|
@@ -12,9 +12,14 @@ const SCALE_STEP = 0.1;
|
|
|
12
12
|
const FIT_HORIZONTAL_PADDING = 28;
|
|
13
13
|
const PAGE_BORDER_WIDTH = 18;
|
|
14
14
|
const PDF_EXPORT_MAX_PAGE_PIXELS = 8000000;
|
|
15
|
+
// PDF.js viewer CSS references image assets that are not shipped with the
|
|
16
|
+
// on-demand renderer chunk, so keep the preview self-contained and 404-free.
|
|
17
|
+
const normalizedPdfViewerStyle = pdfViewerStyle
|
|
18
|
+
.replace(/--page-border-image:\s*url\(images\/shadow\.png\)\s*9 9 repeat;/g, '--page-border-image:none;')
|
|
19
|
+
.replace(/background:\s*url\("\.\/images\/loading-icon\.gif"\)\s*center no-repeat;/g, 'background:none;');
|
|
15
20
|
const createStyle = (documentRef) => {
|
|
16
21
|
const style = documentRef.createElement('style');
|
|
17
|
-
style.textContent = `${
|
|
22
|
+
style.textContent = `${normalizedPdfViewerStyle}
|
|
18
23
|
.pdf-state[hidden],.pdf-nav-pane[hidden]{display:none!important}
|
|
19
24
|
`;
|
|
20
25
|
return style;
|
package/dist/renderers/typst.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { $typst } from '@myriaddreamin/typst.ts';
|
|
2
|
-
import { resolveFileViewerTypstCompilerWasmUrl, resolveFileViewerTypstRendererWasmUrl, } from '../platform/assets.js';
|
|
2
|
+
import { FALLBACK_FILE_VIEWER_TYPST_COMPILER_WASM_URL, FALLBACK_FILE_VIEWER_TYPST_RENDERER_WASM_URL, resolveFileViewerTypstCompilerWasmUrl, resolveFileViewerTypstRendererWasmUrl, } from '../platform/assets.js';
|
|
3
3
|
import { registerFileViewerZoomProvider, unregisterFileViewerZoomProvider, } from '../features/document/dom/index.js';
|
|
4
4
|
import { createFileViewerZoomChangeEmitter } from '../features/document/zoom.js';
|
|
5
5
|
import { formatCssPixels } from '../output/printLayout.js';
|
|
@@ -21,6 +21,11 @@ const typstStyle = `
|
|
|
21
21
|
.typst-loading p{margin:0;color:#6a778b;font-size:13px}
|
|
22
22
|
.typst-error{color:#9f1d1d}
|
|
23
23
|
.typst-error pre{max-height:360px;margin:14px 0 0;overflow:auto;border-radius:10px;background:#fff1f2;color:#9f1d1d;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,'Liberation Mono',monospace;font-size:12px;line-height:1.7;padding:14px;white-space:pre-wrap}
|
|
24
|
+
.typst-source-fallback{box-sizing:border-box;width:min(1040px,calc(100% - 32px));margin:28px auto 44px;border:1px solid rgba(20,35,53,.1);border-radius:16px;background:#fff;box-shadow:0 18px 44px rgba(15,23,42,.14);overflow:hidden}
|
|
25
|
+
.typst-source-fallback header{padding:18px 20px;border-bottom:1px solid rgba(120,134,155,.18);background:linear-gradient(135deg,#f0fdf4,#eff6ff)}
|
|
26
|
+
.typst-source-fallback strong{display:block;color:#172033;font-size:15px;font-weight:850}
|
|
27
|
+
.typst-source-fallback p{margin:6px 0 0;color:#5f6e84;font-size:13px;line-height:1.7}
|
|
28
|
+
.typst-source-fallback pre{box-sizing:border-box;max-height:none;margin:0;overflow:auto;background:#0f172a;color:#e2e8f0;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,'Liberation Mono',monospace;font-size:13px;line-height:1.75;padding:20px;tab-size:2;white-space:pre}
|
|
24
29
|
.file-viewer[data-viewer-theme='dark'] .typst-viewer{background:#101820;color:#e6edf3}
|
|
25
30
|
.file-viewer[data-viewer-theme='dark'] .typst-toolbar{border-bottom-color:rgba(139,148,158,.22);background:rgba(15,23,42,.9)}
|
|
26
31
|
.file-viewer[data-viewer-theme='dark'] .typst-toolbar strong{color:#f8fafc}
|
|
@@ -28,11 +33,22 @@ const typstStyle = `
|
|
|
28
33
|
.file-viewer[data-viewer-theme='dark'] .typst-page-shell{border-color:rgba(139,148,158,.26);box-shadow:0 24px 56px rgba(0,0,0,.38)}
|
|
29
34
|
.file-viewer[data-viewer-theme='dark'] .typst-loading,.file-viewer[data-viewer-theme='dark'] .typst-error{border-color:rgba(139,148,158,.22);background:#151b23;box-shadow:0 24px 56px rgba(0,0,0,.32)}
|
|
30
35
|
.file-viewer[data-viewer-theme='dark'] .typst-loading strong,.file-viewer[data-viewer-theme='dark'] .typst-error strong{color:#f8fafc}
|
|
36
|
+
.file-viewer[data-viewer-theme='dark'] .typst-source-fallback{border-color:rgba(139,148,158,.26);background:#151b23;box-shadow:0 24px 56px rgba(0,0,0,.32)}
|
|
37
|
+
.file-viewer[data-viewer-theme='dark'] .typst-source-fallback header{border-bottom-color:rgba(139,148,158,.22);background:linear-gradient(135deg,rgba(16,185,129,.18),rgba(59,130,246,.16))}
|
|
38
|
+
.file-viewer[data-viewer-theme='dark'] .typst-source-fallback strong{color:#f8fafc}
|
|
39
|
+
.file-viewer[data-viewer-theme='dark'] .typst-source-fallback p{color:#9aa7b8}
|
|
31
40
|
@keyframes typst-spin{to{transform:rotate(360deg)}}
|
|
32
41
|
@media (max-width:767px){.typst-toolbar{align-items:flex-start;flex-direction:column;gap:4px}.typst-pages{gap:16px;padding:16px 10px 28px}}
|
|
33
|
-
@media (prefers-color-scheme:dark){.file-viewer[data-viewer-theme='system'] .typst-viewer{background:#101820;color:#e6edf3}.file-viewer[data-viewer-theme='system'] .typst-toolbar{border-bottom-color:rgba(139,148,158,.22);background:rgba(15,23,42,.9)}.file-viewer[data-viewer-theme='system'] .typst-toolbar strong{color:#f8fafc}.file-viewer[data-viewer-theme='system'] .typst-toolbar span,.file-viewer[data-viewer-theme='system'] .typst-toolbar em{color:#9aa7b8}.file-viewer[data-viewer-theme='system'] .typst-page-shell{border-color:rgba(139,148,158,.26);box-shadow:0 24px 56px rgba(0,0,0,.38)}.file-viewer[data-viewer-theme='system'] .typst-loading,.file-viewer[data-viewer-theme='system'] .typst-error{border-color:rgba(139,148,158,.22);background:#151b23;box-shadow:0 24px 56px rgba(0,0,0,.32)}.file-viewer[data-viewer-theme='system'] .typst-loading strong,.file-viewer[data-viewer-theme='system'] .typst-error strong{color:#f8fafc}}
|
|
42
|
+
@media (prefers-color-scheme:dark){.file-viewer[data-viewer-theme='system'] .typst-viewer{background:#101820;color:#e6edf3}.file-viewer[data-viewer-theme='system'] .typst-toolbar{border-bottom-color:rgba(139,148,158,.22);background:rgba(15,23,42,.9)}.file-viewer[data-viewer-theme='system'] .typst-toolbar strong{color:#f8fafc}.file-viewer[data-viewer-theme='system'] .typst-toolbar span,.file-viewer[data-viewer-theme='system'] .typst-toolbar em{color:#9aa7b8}.file-viewer[data-viewer-theme='system'] .typst-page-shell{border-color:rgba(139,148,158,.26);box-shadow:0 24px 56px rgba(0,0,0,.38)}.file-viewer[data-viewer-theme='system'] .typst-loading,.file-viewer[data-viewer-theme='system'] .typst-error{border-color:rgba(139,148,158,.22);background:#151b23;box-shadow:0 24px 56px rgba(0,0,0,.32)}.file-viewer[data-viewer-theme='system'] .typst-loading strong,.file-viewer[data-viewer-theme='system'] .typst-error strong{color:#f8fafc}.file-viewer[data-viewer-theme='system'] .typst-source-fallback{border-color:rgba(139,148,158,.26);background:#151b23;box-shadow:0 24px 56px rgba(0,0,0,.32)}.file-viewer[data-viewer-theme='system'] .typst-source-fallback header{border-bottom-color:rgba(139,148,158,.22);background:linear-gradient(135deg,rgba(16,185,129,.18),rgba(59,130,246,.16))}.file-viewer[data-viewer-theme='system'] .typst-source-fallback strong{color:#f8fafc}.file-viewer[data-viewer-theme='system'] .typst-source-fallback p{color:#9aa7b8}}
|
|
34
43
|
`;
|
|
35
44
|
let typstEngineConfigKey = '';
|
|
45
|
+
const DEFAULT_TYPST_RENDER_TIMEOUT_MS = 20000;
|
|
46
|
+
class TypstRenderTimeoutError extends Error {
|
|
47
|
+
constructor(timeoutMs) {
|
|
48
|
+
super(`Typst 编译超过 ${Math.round(timeoutMs / 1000)} 秒,已切换为源码预览`);
|
|
49
|
+
this.name = 'TypstRenderTimeoutError';
|
|
50
|
+
}
|
|
51
|
+
}
|
|
36
52
|
const createStyle = (documentRef) => {
|
|
37
53
|
const style = documentRef.createElement('style');
|
|
38
54
|
style.textContent = typstStyle;
|
|
@@ -54,15 +70,7 @@ const getWindowOverride = (key) => {
|
|
|
54
70
|
}
|
|
55
71
|
return window[key];
|
|
56
72
|
};
|
|
57
|
-
const configureTypstEngine = (
|
|
58
|
-
var _a;
|
|
59
|
-
const typstOptions = (_a = context === null || context === void 0 ? void 0 : context.options) === null || _a === void 0 ? void 0 : _a.typst;
|
|
60
|
-
const compilerWasmUrl = resolveFileViewerTypstCompilerWasmUrl(typstOptions, [
|
|
61
|
-
getWindowOverride('__FLYFISH_TYPST_COMPILER_WASM_URL__'),
|
|
62
|
-
]);
|
|
63
|
-
const rendererWasmUrl = resolveFileViewerTypstRendererWasmUrl(typstOptions, [
|
|
64
|
-
getWindowOverride('__FLYFISH_TYPST_RENDERER_WASM_URL__'),
|
|
65
|
-
]);
|
|
73
|
+
const configureTypstEngine = (compilerWasmUrl, rendererWasmUrl) => {
|
|
66
74
|
const configKey = `${compilerWasmUrl}\n${rendererWasmUrl}`;
|
|
67
75
|
if (typstEngineConfigKey === configKey) {
|
|
68
76
|
return;
|
|
@@ -75,6 +83,69 @@ const configureTypstEngine = (context) => {
|
|
|
75
83
|
});
|
|
76
84
|
typstEngineConfigKey = configKey;
|
|
77
85
|
};
|
|
86
|
+
const pushUniqueTypstCandidate = (candidates, candidate) => {
|
|
87
|
+
if (candidates.some(item => item.compilerWasmUrl === candidate.compilerWasmUrl &&
|
|
88
|
+
item.rendererWasmUrl === candidate.rendererWasmUrl)) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
candidates.push(candidate);
|
|
92
|
+
};
|
|
93
|
+
const resolveTypstEngineCandidates = (context, documentBaseUrl) => {
|
|
94
|
+
var _a;
|
|
95
|
+
const typstOptions = (_a = context === null || context === void 0 ? void 0 : context.options) === null || _a === void 0 ? void 0 : _a.typst;
|
|
96
|
+
const compilerOverride = getWindowOverride('__FLYFISH_TYPST_COMPILER_WASM_URL__');
|
|
97
|
+
const rendererOverride = getWindowOverride('__FLYFISH_TYPST_RENDERER_WASM_URL__');
|
|
98
|
+
const compilerWasmUrl = resolveFileViewerTypstCompilerWasmUrl(typstOptions, [
|
|
99
|
+
compilerOverride,
|
|
100
|
+
], documentBaseUrl);
|
|
101
|
+
const rendererWasmUrl = resolveFileViewerTypstRendererWasmUrl(typstOptions, [
|
|
102
|
+
rendererOverride,
|
|
103
|
+
], documentBaseUrl);
|
|
104
|
+
const hasConfiguredAsset = Boolean((typstOptions === null || typstOptions === void 0 ? void 0 : typstOptions.compilerWasmUrl) ||
|
|
105
|
+
(typstOptions === null || typstOptions === void 0 ? void 0 : typstOptions.rendererWasmUrl) ||
|
|
106
|
+
compilerOverride ||
|
|
107
|
+
rendererOverride);
|
|
108
|
+
const candidates = [];
|
|
109
|
+
pushUniqueTypstCandidate(candidates, {
|
|
110
|
+
compilerWasmUrl,
|
|
111
|
+
rendererWasmUrl,
|
|
112
|
+
source: hasConfiguredAsset ? 'configured' : 'local',
|
|
113
|
+
preflight: !hasConfiguredAsset,
|
|
114
|
+
});
|
|
115
|
+
// Static hosts such as Cloudflare Pages reject files larger than 25 MiB.
|
|
116
|
+
// Keep the package self-contained, but let hosted demos fall back gracefully
|
|
117
|
+
// when the local compiler WASM was intentionally omitted from deployment.
|
|
118
|
+
pushUniqueTypstCandidate(candidates, {
|
|
119
|
+
compilerWasmUrl: FALLBACK_FILE_VIEWER_TYPST_COMPILER_WASM_URL,
|
|
120
|
+
rendererWasmUrl: FALLBACK_FILE_VIEWER_TYPST_RENDERER_WASM_URL,
|
|
121
|
+
source: 'cdn',
|
|
122
|
+
preflight: false,
|
|
123
|
+
});
|
|
124
|
+
return candidates;
|
|
125
|
+
};
|
|
126
|
+
const isHttpUrl = (url) => /^https?:\/\//i.test(url);
|
|
127
|
+
const isKnownMissingWasmUrl = async (url) => {
|
|
128
|
+
if (typeof fetch !== 'function' || !isHttpUrl(url)) {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
try {
|
|
132
|
+
const response = await fetch(url, {
|
|
133
|
+
cache: 'force-cache',
|
|
134
|
+
method: 'HEAD',
|
|
135
|
+
});
|
|
136
|
+
return response.status === 404 || response.status === 410;
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
const isTypstAssetLoadError = (error) => {
|
|
143
|
+
if (Array.isArray(error)) {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
147
|
+
return /wasm|webassembly|fetch|module|instantiate|compile|network|404|410/i.test(message);
|
|
148
|
+
};
|
|
78
149
|
const escapeAttribute = (value) => {
|
|
79
150
|
return value.replace(/[&<>"']/g, char => {
|
|
80
151
|
const entities = {
|
|
@@ -171,6 +242,29 @@ const formatTypstError = (error) => {
|
|
|
171
242
|
const clampZoom = (value) => {
|
|
172
243
|
return Math.min(3, Math.max(0.3, Number(value.toFixed(2))));
|
|
173
244
|
};
|
|
245
|
+
const normalizeRenderTimeoutMs = (timeoutMs) => {
|
|
246
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs === undefined) {
|
|
247
|
+
return DEFAULT_TYPST_RENDER_TIMEOUT_MS;
|
|
248
|
+
}
|
|
249
|
+
return Math.max(0, timeoutMs);
|
|
250
|
+
};
|
|
251
|
+
const withRenderTimeout = async (promise, timeoutMs) => {
|
|
252
|
+
if (timeoutMs <= 0) {
|
|
253
|
+
return promise;
|
|
254
|
+
}
|
|
255
|
+
let timeoutId;
|
|
256
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
257
|
+
timeoutId = setTimeout(() => reject(new TypstRenderTimeoutError(timeoutMs)), timeoutMs);
|
|
258
|
+
});
|
|
259
|
+
try {
|
|
260
|
+
return await Promise.race([promise, timeoutPromise]);
|
|
261
|
+
}
|
|
262
|
+
finally {
|
|
263
|
+
if (timeoutId) {
|
|
264
|
+
clearTimeout(timeoutId);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
};
|
|
174
268
|
const buildExportStyles = () => `
|
|
175
269
|
<style>
|
|
176
270
|
.typst-export-document {
|
|
@@ -269,6 +363,7 @@ export default async function renderTypst(buffer, target, _type, context) {
|
|
|
269
363
|
let state = 'loading';
|
|
270
364
|
let pages = [];
|
|
271
365
|
let errorMessage = '';
|
|
366
|
+
let sourceFallbackMessage = '';
|
|
272
367
|
let zoom = 1;
|
|
273
368
|
let renderToken = 0;
|
|
274
369
|
let disposed = false;
|
|
@@ -334,6 +429,16 @@ export default async function renderTypst(buffer, target, _type, context) {
|
|
|
334
429
|
error.append(createElement(documentRef, 'strong', undefined, 'Typst 渲染失败'), createElement(documentRef, 'pre', undefined, errorMessage));
|
|
335
430
|
body.replaceChildren(error);
|
|
336
431
|
};
|
|
432
|
+
const renderSourceFallback = () => {
|
|
433
|
+
const fallback = createElement(documentRef, 'main', 'typst-source-fallback');
|
|
434
|
+
fallback.setAttribute('aria-label', 'Typst source preview');
|
|
435
|
+
const header = createElement(documentRef, 'header');
|
|
436
|
+
header.append(createElement(documentRef, 'strong', undefined, '已切换为 Typst 源码预览'), createElement(documentRef, 'p', undefined, sourceFallbackMessage || '当前浏览器没有在预期时间内完成 Typst 编译,源码仍可完整查看。'));
|
|
437
|
+
const pre = createElement(documentRef, 'pre');
|
|
438
|
+
pre.textContent = source;
|
|
439
|
+
fallback.append(header, pre);
|
|
440
|
+
body.replaceChildren(fallback);
|
|
441
|
+
};
|
|
337
442
|
const renderPages = () => {
|
|
338
443
|
pageShells.clear();
|
|
339
444
|
const pagesRoot = createElement(documentRef, 'main', 'typst-pages');
|
|
@@ -351,37 +456,73 @@ export default async function renderTypst(buffer, target, _type, context) {
|
|
|
351
456
|
applyPageZoom();
|
|
352
457
|
};
|
|
353
458
|
const syncUi = () => {
|
|
354
|
-
summary.textContent = state === 'ready'
|
|
355
|
-
|
|
459
|
+
summary.textContent = state === 'ready'
|
|
460
|
+
? getPageSummary(pages)
|
|
461
|
+
: state === 'source'
|
|
462
|
+
? 'Typst source preview'
|
|
463
|
+
: 'Typst WASM renderer';
|
|
464
|
+
status.textContent = state === 'loading'
|
|
465
|
+
? '正在编译'
|
|
466
|
+
: state === 'error'
|
|
467
|
+
? '编译失败'
|
|
468
|
+
: state === 'source'
|
|
469
|
+
? '源码预览'
|
|
470
|
+
: '已渲染';
|
|
356
471
|
if (state === 'loading') {
|
|
357
472
|
renderLoading();
|
|
358
473
|
}
|
|
359
474
|
else if (state === 'error') {
|
|
360
475
|
renderError();
|
|
361
476
|
}
|
|
477
|
+
else if (state === 'source') {
|
|
478
|
+
renderSourceFallback();
|
|
479
|
+
}
|
|
362
480
|
else {
|
|
363
481
|
renderPages();
|
|
364
482
|
}
|
|
365
483
|
};
|
|
366
|
-
const
|
|
484
|
+
const renderTypstSvg = async () => {
|
|
367
485
|
var _a, _b;
|
|
486
|
+
const candidates = resolveTypstEngineCandidates(context, documentRef.baseURI);
|
|
487
|
+
const timeoutMs = normalizeRenderTimeoutMs((_b = (_a = context === null || context === void 0 ? void 0 : context.options) === null || _a === void 0 ? void 0 : _a.typst) === null || _b === void 0 ? void 0 : _b.renderTimeoutMs);
|
|
488
|
+
let lastError;
|
|
489
|
+
for (const candidate of candidates) {
|
|
490
|
+
if (candidate.preflight && await isKnownMissingWasmUrl(candidate.compilerWasmUrl)) {
|
|
491
|
+
lastError = new Error(`Typst compiler WASM missing: ${candidate.compilerWasmUrl}`);
|
|
492
|
+
continue;
|
|
493
|
+
}
|
|
494
|
+
try {
|
|
495
|
+
configureTypstEngine(candidate.compilerWasmUrl, candidate.rendererWasmUrl);
|
|
496
|
+
return await withRenderTimeout($typst.svg({
|
|
497
|
+
mainContent: source,
|
|
498
|
+
data_selection: {
|
|
499
|
+
body: true,
|
|
500
|
+
defs: true,
|
|
501
|
+
css: true,
|
|
502
|
+
js: false,
|
|
503
|
+
},
|
|
504
|
+
}), timeoutMs);
|
|
505
|
+
}
|
|
506
|
+
catch (error) {
|
|
507
|
+
lastError = error;
|
|
508
|
+
if (error instanceof TypstRenderTimeoutError || !isTypstAssetLoadError(error)) {
|
|
509
|
+
throw error;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
throw lastError instanceof Error ? lastError : new Error('Typst WASM 加载失败');
|
|
514
|
+
};
|
|
515
|
+
const render = async () => {
|
|
516
|
+
var _a, _b, _c;
|
|
368
517
|
const token = ++renderToken;
|
|
369
518
|
state = 'loading';
|
|
370
519
|
errorMessage = '';
|
|
520
|
+
sourceFallbackMessage = '';
|
|
371
521
|
pages = [];
|
|
372
522
|
(_a = context === null || context === void 0 ? void 0 : context.registerExportAdapter) === null || _a === void 0 ? void 0 : _a.call(context, null);
|
|
373
523
|
syncUi();
|
|
374
524
|
try {
|
|
375
|
-
|
|
376
|
-
const svg = await $typst.svg({
|
|
377
|
-
mainContent: source,
|
|
378
|
-
data_selection: {
|
|
379
|
-
body: true,
|
|
380
|
-
defs: true,
|
|
381
|
-
css: true,
|
|
382
|
-
js: false,
|
|
383
|
-
},
|
|
384
|
-
});
|
|
525
|
+
const svg = await renderTypstSvg();
|
|
385
526
|
if (disposed || token !== renderToken) {
|
|
386
527
|
return;
|
|
387
528
|
}
|
|
@@ -395,6 +536,13 @@ export default async function renderTypst(buffer, target, _type, context) {
|
|
|
395
536
|
if (disposed || token !== renderToken) {
|
|
396
537
|
return;
|
|
397
538
|
}
|
|
539
|
+
if (error instanceof TypstRenderTimeoutError) {
|
|
540
|
+
sourceFallbackMessage = error.message;
|
|
541
|
+
state = 'source';
|
|
542
|
+
syncUi();
|
|
543
|
+
(_c = context === null || context === void 0 ? void 0 : context.onProgressiveRender) === null || _c === void 0 ? void 0 : _c.call(context);
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
398
546
|
errorMessage = formatTypstError(error);
|
|
399
547
|
state = 'error';
|
|
400
548
|
syncUi();
|
package/dist/renderers/umd.js
CHANGED
|
@@ -28,7 +28,7 @@ const umdStyle = `
|
|
|
28
28
|
.umd-text{color:#1f2937;font-family:Georgia,'Times New Roman','Songti SC',SimSun,serif;font-size:17px;line-height:1.86;white-space:pre-wrap;word-break:break-word}
|
|
29
29
|
.umd-image-list{display:grid;gap:18px}.umd-image-list figure{margin:0;text-align:center}.umd-image-list img{max-width:100%;height:auto;border-radius:6px;box-shadow:0 10px 24px rgba(15,23,42,.12)}
|
|
30
30
|
.umd-empty,.umd-warning{color:#64748b;font-size:14px;line-height:1.7}.umd-warning{max-width:820px;margin:-28px auto 36px;padding:0 34px;color:#b45309;box-sizing:border-box}
|
|
31
|
-
.umd-state{position:absolute;inset:18px;display:flex;align-items:center;justify-content:center;border-radius:8px;background:rgba(255,255,255,.92);color:#64748b;font-size:14px}.umd-state.error{color:#b42318}
|
|
31
|
+
.umd-state{position:absolute;inset:18px;display:flex;align-items:center;justify-content:center;border-radius:8px;background:rgba(255,255,255,.92);color:#64748b;font-size:14px}.umd-state[hidden]{display:none!important}.umd-state.error{color:#b42318}
|
|
32
32
|
.file-viewer[data-viewer-theme='dark'] .umd-viewer{background:#101820;color:#e5edf6}.file-viewer[data-viewer-theme='dark'] .umd-toolbar,.file-viewer[data-viewer-theme='dark'] .umd-toc{background:rgba(15,23,42,.92);border-color:rgba(148,163,184,.18)}
|
|
33
33
|
.file-viewer[data-viewer-theme='dark'] .umd-stage{background:#111827;box-shadow:0 18px 45px rgba(0,0,0,.35),inset 0 0 0 1px rgba(148,163,184,.16)}
|
|
34
34
|
.file-viewer[data-viewer-theme='dark'] .umd-book-head h1,.file-viewer[data-viewer-theme='dark'] .umd-chapter h2,.file-viewer[data-viewer-theme='dark'] .umd-text,.file-viewer[data-viewer-theme='dark'] .umd-toc-head{color:#e5edf6}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@file-viewer/core",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.8",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Framework-neutral TypeScript core for Flyfish File Viewer",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"LICENSE"
|
|
59
59
|
],
|
|
60
60
|
"dependencies": {
|
|
61
|
-
"@file-viewer/pptx": "^2.0.
|
|
61
|
+
"@file-viewer/pptx": "^2.0.8",
|
|
62
62
|
"@flyfish-dev/cad-viewer": "^0.6.4",
|
|
63
63
|
"@excalidraw/excalidraw": "^0.18.1",
|
|
64
64
|
"@kenjiuno/msgreader": "^1.28.0",
|