@file-viewer/core 2.0.11 → 2.1.0
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 +2 -2
- package/README.md +2 -2
- package/dist/config/options.d.ts +1 -1
- package/dist/config/options.js +1 -1
- package/dist/contracts/types.d.ts +71 -1
- package/dist/headless.d.ts +2 -2
- package/dist/headless.js +1 -1
- package/dist/index.d.ts +9 -6
- package/dist/index.js +105 -48
- package/dist/platform/assets.d.ts +3 -1
- package/dist/platform/assets.js +18 -2
- package/dist/registry/capabilities.d.ts +2 -2
- package/dist/registry/capabilities.js +2 -1
- package/dist/registry/formats.d.ts +20 -7
- package/dist/registry/formats.js +14 -5
- package/dist/registry/registry.d.ts +8 -1
- package/dist/registry/registry.js +29 -0
- package/dist/renderers/image.js +1 -10
- package/dist/renderers/index.d.ts +320 -2
- package/dist/renderers/index.js +27 -157
- package/dist/viewer/createViewer.js +86 -3
- package/package.json +17 -44
- package/dist/renderers/archive.d.ts +0 -2
- package/dist/renderers/archive.js +0 -547
- package/dist/renderers/archiveCache.d.ts +0 -10
- package/dist/renderers/archiveCache.js +0 -96
- package/dist/renderers/archiveFallback.d.ts +0 -7
- package/dist/renderers/archiveFallback.js +0 -166
- package/dist/renderers/archiveShared.d.ts +0 -23
- package/dist/renderers/archiveShared.js +0 -71
- package/dist/renderers/audio.d.ts +0 -8
- package/dist/renderers/audio.js +0 -219
- package/dist/renderers/cad.d.ts +0 -2
- package/dist/renderers/cad.js +0 -446
- package/dist/renderers/code.d.ts +0 -11
- package/dist/renderers/code.js +0 -233
- package/dist/renderers/data.d.ts +0 -7
- package/dist/renderers/data.js +0 -370
- package/dist/renderers/drawing.d.ts +0 -10
- package/dist/renderers/drawing.js +0 -882
- package/dist/renderers/eda.d.ts +0 -2
- package/dist/renderers/eda.js +0 -434
- package/dist/renderers/edaParser.d.ts +0 -77
- package/dist/renderers/edaParser.js +0 -569
- package/dist/renderers/email.d.ts +0 -2
- package/dist/renderers/email.js +0 -463
- package/dist/renderers/epub.d.ts +0 -2
- package/dist/renderers/epub.js +0 -331
- package/dist/renderers/geo.d.ts +0 -2
- package/dist/renderers/geo.js +0 -284
- package/dist/renderers/markdown.d.ts +0 -2
- package/dist/renderers/markdown.js +0 -83
- package/dist/renderers/model.d.ts +0 -2
- package/dist/renderers/model.js +0 -567
- package/dist/renderers/ofd.d.ts +0 -2
- package/dist/renderers/ofd.js +0 -256
- package/dist/renderers/openDocument.d.ts +0 -2
- package/dist/renderers/openDocument.js +0 -122
- package/dist/renderers/pdf.d.ts +0 -3
- package/dist/renderers/pdf.js +0 -1001
- package/dist/renderers/pdfStyles.d.ts +0 -1
- package/dist/renderers/pdfStyles.js +0 -1
- package/dist/renderers/pptx.d.ts +0 -2
- package/dist/renderers/pptx.js +0 -217
- package/dist/renderers/spreadsheet/state.d.ts +0 -80
- package/dist/renderers/spreadsheet/state.js +0 -96
- package/dist/renderers/spreadsheet/view.d.ts +0 -25
- package/dist/renderers/spreadsheet/view.js +0 -833
- package/dist/renderers/spreadsheet/worker/index.d.ts +0 -2
- package/dist/renderers/spreadsheet/worker/index.js +0 -1
- package/dist/renderers/spreadsheet/worker/sheetjs/SheetJsModel.d.ts +0 -73
- package/dist/renderers/spreadsheet/worker/sheetjs/SheetJsModel.js +0 -623
- package/dist/renderers/spreadsheet/worker/sheetjs/color.d.ts +0 -2
- package/dist/renderers/spreadsheet/worker/sheetjs/color.js +0 -73
- package/dist/renderers/spreadsheet/worker/sheetjs/index.d.ts +0 -1
- package/dist/renderers/spreadsheet/worker/sheetjs/index.js +0 -1
- package/dist/renderers/spreadsheet/worker/sheetjs/parser.d.ts +0 -18
- package/dist/renderers/spreadsheet/worker/sheetjs/parser.js +0 -106
- package/dist/renderers/spreadsheet/worker/sheetjs/sheet.worker.d.ts +0 -1
- package/dist/renderers/spreadsheet/worker/sheetjs/sheet.worker.js +0 -11
- package/dist/renderers/spreadsheet/worker/type.d.ts +0 -57
- package/dist/renderers/spreadsheet/worker/type.js +0 -1
- package/dist/renderers/spreadsheet.d.ts +0 -3
- package/dist/renderers/spreadsheet.js +0 -929
- package/dist/renderers/typst.d.ts +0 -8
- package/dist/renderers/typst.js +0 -547
- package/dist/renderers/umd/parser.d.ts +0 -30
- package/dist/renderers/umd/parser.js +0 -408
- package/dist/renderers/umd.d.ts +0 -2
- package/dist/renderers/umd.js +0 -297
- package/dist/renderers/video.d.ts +0 -8
- package/dist/renderers/video.js +0 -108
- package/dist/renderers/wordDoc.d.ts +0 -5
- package/dist/renderers/wordDoc.js +0 -284
- package/dist/renderers/wordDocx.d.ts +0 -5
- package/dist/renderers/wordDocx.js +0 -985
- package/dist/renderers/wordDocx.worker.d.ts +0 -1
- package/dist/renderers/wordDocx.worker.js +0 -96
- package/vendor/ofd/dltech/jbig2/arithmetic_decoder.js +0 -183
- package/vendor/ofd/dltech/jbig2/ccitt.js +0 -1070
- package/vendor/ofd/dltech/jbig2/compatibility.js +0 -12
- package/vendor/ofd/dltech/jbig2/core_utils.js +0 -180
- package/vendor/ofd/dltech/jbig2/is_node.js +0 -27
- package/vendor/ofd/dltech/jbig2/jbig2.js +0 -2589
- package/vendor/ofd/dltech/jbig2/jbig2_stream.js +0 -81
- package/vendor/ofd/dltech/jbig2/primitives.js +0 -387
- package/vendor/ofd/dltech/jbig2/stream.js +0 -1348
- package/vendor/ofd/dltech/jbig2/util.js +0 -972
- package/vendor/ofd/dltech/ofd/ofd.d.ts +0 -11
- package/vendor/ofd/dltech/ofd/ofd.js +0 -100
- package/vendor/ofd/dltech/ofd/ofd_parser.js +0 -395
- package/vendor/ofd/dltech/ofd/ofd_render.js +0 -473
- package/vendor/ofd/dltech/ofd/ofd_util.js +0 -350
- package/vendor/ofd/dltech/ofd/pipeline.js +0 -26
package/dist/renderers/code.js
DELETED
|
@@ -1,233 +0,0 @@
|
|
|
1
|
-
import { createFileViewerZoomChangeEmitter as createZoomChangeEmitter } from '../features/document/zoom.js';
|
|
2
|
-
import { readFileViewerText as readText } from '../source/index.js';
|
|
3
|
-
import { registerFileViewerZoomProvider, unregisterFileViewerZoomProvider } from '../features/document/dom/index.js';
|
|
4
|
-
const languageMap = {
|
|
5
|
-
bash: 'bash',
|
|
6
|
-
c: 'cpp',
|
|
7
|
-
cc: 'cpp',
|
|
8
|
-
cjs: 'javascript',
|
|
9
|
-
cpp: 'cpp',
|
|
10
|
-
cs: 'csharp',
|
|
11
|
-
css: 'css',
|
|
12
|
-
diff: 'diff',
|
|
13
|
-
gv: 'plaintext',
|
|
14
|
-
go: 'go',
|
|
15
|
-
h: 'cpp',
|
|
16
|
-
hcl: 'plaintext',
|
|
17
|
-
hpp: 'cpp',
|
|
18
|
-
html: 'xml',
|
|
19
|
-
htm: 'xml',
|
|
20
|
-
http: 'http',
|
|
21
|
-
ini: 'ini',
|
|
22
|
-
ipynb: 'json',
|
|
23
|
-
java: 'java',
|
|
24
|
-
js: 'javascript',
|
|
25
|
-
json: 'json',
|
|
26
|
-
json5: 'json',
|
|
27
|
-
jsonc: 'json',
|
|
28
|
-
jsx: 'javascript',
|
|
29
|
-
kt: 'kotlin',
|
|
30
|
-
log: 'plaintext',
|
|
31
|
-
md: 'markdown',
|
|
32
|
-
markdown: 'markdown',
|
|
33
|
-
mjs: 'javascript',
|
|
34
|
-
php: 'php',
|
|
35
|
-
proto: 'protobuf',
|
|
36
|
-
py: 'python',
|
|
37
|
-
rb: 'ruby',
|
|
38
|
-
react: 'javascript',
|
|
39
|
-
rs: 'rust',
|
|
40
|
-
sh: 'bash',
|
|
41
|
-
sql: 'sql',
|
|
42
|
-
swift: 'swift',
|
|
43
|
-
tex: 'latex',
|
|
44
|
-
toml: 'ini',
|
|
45
|
-
ts: 'typescript',
|
|
46
|
-
tsx: 'typescript',
|
|
47
|
-
txt: 'plaintext',
|
|
48
|
-
vue: 'xml',
|
|
49
|
-
xml: 'xml',
|
|
50
|
-
yaml: 'yaml',
|
|
51
|
-
yml: 'yaml'
|
|
52
|
-
};
|
|
53
|
-
const languageLoaders = {
|
|
54
|
-
bash: () => import('highlight.js/lib/languages/bash'),
|
|
55
|
-
cpp: () => import('highlight.js/lib/languages/cpp'),
|
|
56
|
-
csharp: () => import('highlight.js/lib/languages/csharp'),
|
|
57
|
-
css: () => import('highlight.js/lib/languages/css'),
|
|
58
|
-
diff: () => import('highlight.js/lib/languages/diff'),
|
|
59
|
-
go: () => import('highlight.js/lib/languages/go'),
|
|
60
|
-
http: () => import('highlight.js/lib/languages/http'),
|
|
61
|
-
ini: () => import('highlight.js/lib/languages/ini'),
|
|
62
|
-
java: () => import('highlight.js/lib/languages/java'),
|
|
63
|
-
javascript: () => import('highlight.js/lib/languages/javascript'),
|
|
64
|
-
json: () => import('highlight.js/lib/languages/json'),
|
|
65
|
-
kotlin: () => import('highlight.js/lib/languages/kotlin'),
|
|
66
|
-
latex: () => import('highlight.js/lib/languages/latex'),
|
|
67
|
-
markdown: () => import('highlight.js/lib/languages/markdown'),
|
|
68
|
-
php: () => import('highlight.js/lib/languages/php'),
|
|
69
|
-
protobuf: () => import('highlight.js/lib/languages/protobuf'),
|
|
70
|
-
python: () => import('highlight.js/lib/languages/python'),
|
|
71
|
-
ruby: () => import('highlight.js/lib/languages/ruby'),
|
|
72
|
-
rust: () => import('highlight.js/lib/languages/rust'),
|
|
73
|
-
sql: () => import('highlight.js/lib/languages/sql'),
|
|
74
|
-
swift: () => import('highlight.js/lib/languages/swift'),
|
|
75
|
-
typescript: () => import('highlight.js/lib/languages/typescript'),
|
|
76
|
-
xml: () => import('highlight.js/lib/languages/xml'),
|
|
77
|
-
yaml: () => import('highlight.js/lib/languages/yaml')
|
|
78
|
-
};
|
|
79
|
-
const codeStyle = `
|
|
80
|
-
.code-viewer{min-height:100%;--code-bg:#f6f8fa;--code-toolbar-bg:rgba(255,255,255,.92);--code-border:rgba(31,35,40,.12);--code-text:#24292f;--code-muted:#57606a;--code-keyword:#cf222e;--code-title:#8250df;--code-string:#0a3069;--code-number:#0550ae;--code-comment:#6e7781;--code-attr:#953800;--code-built-in:#116329;background:var(--code-bg);color:var(--code-text);box-sizing:border-box}
|
|
81
|
-
.code-toolbar{position:sticky;top:0;z-index:1;display:flex;height:42px;align-items:center;justify-content:space-between;gap:16px;padding:0 16px;border-bottom:1px solid var(--code-border);background:var(--code-toolbar-bg);backdrop-filter:blur(12px);box-sizing:border-box}
|
|
82
|
-
.code-toolbar span,.code-toolbar strong{color:var(--code-muted);font-size:12px;font-weight:700;letter-spacing:0}
|
|
83
|
-
.code-area{display:block;min-width:min-content;margin:0;padding:18px 20px 28px;overflow:auto;background:transparent;box-sizing:border-box}
|
|
84
|
-
.code-area code{display:block;padding:0;overflow:visible;background:transparent;color:inherit;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,'Liberation Mono',monospace;font-size:var(--code-font-size,13px);line-height:1.7;tab-size:2;white-space:pre}
|
|
85
|
-
.code-area .hljs-comment,.code-area .hljs-quote{color:var(--code-comment)}
|
|
86
|
-
.code-area .hljs-keyword,.code-area .hljs-selector-tag,.code-area .hljs-subst{color:var(--code-keyword)}
|
|
87
|
-
.code-area .hljs-string,.code-area .hljs-doctag,.code-area .hljs-regexp{color:var(--code-string)}
|
|
88
|
-
.code-area .hljs-title,.code-area .hljs-section,.code-area .hljs-selector-id{color:var(--code-title);font-weight:700}
|
|
89
|
-
.code-area .hljs-number,.code-area .hljs-literal,.code-area .hljs-variable,.code-area .hljs-template-variable{color:var(--code-number)}
|
|
90
|
-
.code-area .hljs-attr,.code-area .hljs-attribute,.code-area .hljs-name,.code-area .hljs-selector-class{color:var(--code-attr)}
|
|
91
|
-
.code-area .hljs-built_in,.code-area .hljs-type,.code-area .hljs-class .hljs-title{color:var(--code-built-in)}
|
|
92
|
-
.file-viewer[data-viewer-theme='dark'] .code-viewer{--code-bg:#0d1117;--code-toolbar-bg:rgba(13,17,23,.92);--code-border:rgba(139,148,158,.24);--code-text:#e6edf3;--code-muted:#8b949e;--code-keyword:#ff7b72;--code-title:#d2a8ff;--code-string:#a5d6ff;--code-number:#79c0ff;--code-comment:#8b949e;--code-attr:#ffa657;--code-built-in:#7ee787}
|
|
93
|
-
@media (prefers-color-scheme:dark){.file-viewer[data-viewer-theme='system'] .code-viewer{--code-bg:#0d1117;--code-toolbar-bg:rgba(13,17,23,.92);--code-border:rgba(139,148,158,.24);--code-text:#e6edf3;--code-muted:#8b949e;--code-keyword:#ff7b72;--code-title:#d2a8ff;--code-string:#a5d6ff;--code-number:#79c0ff;--code-comment:#8b949e;--code-attr:#ffa657;--code-built-in:#7ee787}}
|
|
94
|
-
`;
|
|
95
|
-
let highlighterPromise = null;
|
|
96
|
-
const registeredLanguages = new Set();
|
|
97
|
-
const createElement = (tagName, className, text) => {
|
|
98
|
-
const element = document.createElement(tagName);
|
|
99
|
-
if (className) {
|
|
100
|
-
element.className = className;
|
|
101
|
-
}
|
|
102
|
-
if (typeof text === 'string') {
|
|
103
|
-
element.textContent = text;
|
|
104
|
-
}
|
|
105
|
-
return element;
|
|
106
|
-
};
|
|
107
|
-
const createStyle = () => {
|
|
108
|
-
const style = document.createElement('style');
|
|
109
|
-
style.textContent = codeStyle;
|
|
110
|
-
return style;
|
|
111
|
-
};
|
|
112
|
-
const resolveLanguage = (type) => {
|
|
113
|
-
return languageMap[type.trim().toLowerCase()] || 'plaintext';
|
|
114
|
-
};
|
|
115
|
-
const escapeHtml = (value) => {
|
|
116
|
-
return value.replace(/[&<>"']/g, char => {
|
|
117
|
-
const entities = {
|
|
118
|
-
'&': '&',
|
|
119
|
-
'<': '<',
|
|
120
|
-
'>': '>',
|
|
121
|
-
'"': '"',
|
|
122
|
-
"'": '''
|
|
123
|
-
};
|
|
124
|
-
return entities[char];
|
|
125
|
-
});
|
|
126
|
-
};
|
|
127
|
-
const loadHighlighter = async () => {
|
|
128
|
-
if (!highlighterPromise) {
|
|
129
|
-
highlighterPromise = import('highlight.js/lib/core').then(module => module.default);
|
|
130
|
-
}
|
|
131
|
-
return highlighterPromise;
|
|
132
|
-
};
|
|
133
|
-
const registerLanguageOnce = async (hljs, name) => {
|
|
134
|
-
if (registeredLanguages.has(name)) {
|
|
135
|
-
return true;
|
|
136
|
-
}
|
|
137
|
-
const loader = languageLoaders[name];
|
|
138
|
-
if (!loader) {
|
|
139
|
-
return false;
|
|
140
|
-
}
|
|
141
|
-
const { default: language } = await loader();
|
|
142
|
-
hljs.registerLanguage(name, language);
|
|
143
|
-
registeredLanguages.add(name);
|
|
144
|
-
return true;
|
|
145
|
-
};
|
|
146
|
-
const clampZoom = (value) => {
|
|
147
|
-
return Math.min(2.6, Math.max(0.6, Number(value.toFixed(2))));
|
|
148
|
-
};
|
|
149
|
-
const lineCountOf = (value) => {
|
|
150
|
-
return value.split(/\r\n|\r|\n/).length;
|
|
151
|
-
};
|
|
152
|
-
/**
|
|
153
|
-
* Framework-neutral text/code renderer.
|
|
154
|
-
*
|
|
155
|
-
* highlight.js core and language definitions are loaded lazily by format. HTML
|
|
156
|
-
* and XML are highlighted as escaped source text, never executed as real DOM.
|
|
157
|
-
* @param buffer 文本二进制内容
|
|
158
|
-
* @param target 目标
|
|
159
|
-
* @param type 文件扩展名,用于选择 highlight.js 语言
|
|
160
|
-
*/
|
|
161
|
-
export default async function renderText(buffer, target, type) {
|
|
162
|
-
const text = await readText(buffer);
|
|
163
|
-
const extension = type || 'txt';
|
|
164
|
-
const language = resolveLanguage(extension);
|
|
165
|
-
let disposed = false;
|
|
166
|
-
let zoom = 1;
|
|
167
|
-
const zoomEmitter = createZoomChangeEmitter();
|
|
168
|
-
const root = createElement('div', 'code-viewer');
|
|
169
|
-
root.dataset.viewerZoomProvider = 'code';
|
|
170
|
-
const toolbar = createElement('div', 'code-toolbar');
|
|
171
|
-
toolbar.append(createElement('span', '', extension.toUpperCase()), createElement('strong', '', `${lineCountOf(text)} lines`));
|
|
172
|
-
const pre = createElement('pre', 'code-area');
|
|
173
|
-
const code = createElement('code', `hljs language-${language}`);
|
|
174
|
-
code.innerHTML = language === 'plaintext'
|
|
175
|
-
? escapeHtml(text)
|
|
176
|
-
: '正在加载高亮...';
|
|
177
|
-
pre.append(code);
|
|
178
|
-
root.append(toolbar, pre);
|
|
179
|
-
root.style.setProperty('--code-font-size', `${13 * zoom}px`);
|
|
180
|
-
target.replaceChildren(createStyle(), root);
|
|
181
|
-
const updateHighlighted = async () => {
|
|
182
|
-
if (language === 'plaintext') {
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
try {
|
|
186
|
-
const hljs = await loadHighlighter();
|
|
187
|
-
const hasLanguage = await registerLanguageOnce(hljs, language);
|
|
188
|
-
if (disposed) {
|
|
189
|
-
return;
|
|
190
|
-
}
|
|
191
|
-
code.innerHTML = hasLanguage
|
|
192
|
-
? hljs.highlight(text, { language, ignoreIllegals: true }).value
|
|
193
|
-
: escapeHtml(text);
|
|
194
|
-
}
|
|
195
|
-
catch {
|
|
196
|
-
if (!disposed) {
|
|
197
|
-
code.innerHTML = escapeHtml(text);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
};
|
|
201
|
-
void updateHighlighted();
|
|
202
|
-
const getZoomState = () => ({
|
|
203
|
-
scale: zoom,
|
|
204
|
-
label: `${Math.round(zoom * 100)}%`,
|
|
205
|
-
canZoomIn: zoom < 2.6,
|
|
206
|
-
canZoomOut: zoom > 0.6,
|
|
207
|
-
canReset: zoom !== 1,
|
|
208
|
-
minScale: 0.6,
|
|
209
|
-
maxScale: 2.6
|
|
210
|
-
});
|
|
211
|
-
const setZoom = (scale) => {
|
|
212
|
-
zoom = clampZoom(scale);
|
|
213
|
-
root.style.setProperty('--code-font-size', `${13 * zoom}px`);
|
|
214
|
-
zoomEmitter.emit();
|
|
215
|
-
return getZoomState();
|
|
216
|
-
};
|
|
217
|
-
registerFileViewerZoomProvider(root, {
|
|
218
|
-
zoomIn: () => setZoom(zoom + 0.1),
|
|
219
|
-
zoomOut: () => setZoom(zoom - 0.1),
|
|
220
|
-
resetZoom: () => setZoom(1),
|
|
221
|
-
setZoom,
|
|
222
|
-
getState: getZoomState,
|
|
223
|
-
subscribe: zoomEmitter.subscribe
|
|
224
|
-
});
|
|
225
|
-
return {
|
|
226
|
-
$el: target,
|
|
227
|
-
unmount() {
|
|
228
|
-
disposed = true;
|
|
229
|
-
unregisterFileViewerZoomProvider(root);
|
|
230
|
-
target.replaceChildren();
|
|
231
|
-
}
|
|
232
|
-
};
|
|
233
|
-
}
|
package/dist/renderers/data.d.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import type { FileRenderContext, FileViewerRenderedInstance } from '../contracts/types';
|
|
2
|
-
declare global {
|
|
3
|
-
interface Window {
|
|
4
|
-
__FLYFISH_DATA_SQL_WASM_URL__?: string;
|
|
5
|
-
}
|
|
6
|
-
}
|
|
7
|
-
export default function renderDataAsset(buffer: ArrayBuffer, target: HTMLDivElement, type?: string, context?: FileRenderContext): Promise<FileViewerRenderedInstance>;
|
package/dist/renderers/data.js
DELETED
|
@@ -1,370 +0,0 @@
|
|
|
1
|
-
import { resolveFileViewerDataSqlWasmUrl } from '../platform/assets.js';
|
|
2
|
-
const dataStyle = `
|
|
3
|
-
.data-viewer{min-height:100%;padding:28px;background:#eef1f4;color:#132235}
|
|
4
|
-
.data-card{max-width:1080px;margin:0 auto;overflow:hidden;border:1px solid rgba(15,23,42,.08);border-radius:8px;background:#fff;box-shadow:0 18px 48px rgba(15,23,42,.12)}
|
|
5
|
-
.data-header{padding:20px 24px;border-bottom:1px solid rgba(15,23,42,.08)}
|
|
6
|
-
.data-header span{color:#0f766e;font-size:12px;font-weight:800}
|
|
7
|
-
.data-header h2{margin:6px 0 0;font-size:24px}
|
|
8
|
-
.data-summary{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:1px;background:rgba(15,23,42,.08)}
|
|
9
|
-
.data-summary div{min-width:0;padding:15px 18px;background:#f8fafc}
|
|
10
|
-
.data-summary span{display:block;color:#64748b;font-size:12px}
|
|
11
|
-
.data-summary strong{display:block;margin-top:5px;overflow:hidden;color:#132235;font-size:15px;text-overflow:ellipsis;white-space:nowrap}
|
|
12
|
-
.font-preview{padding:34px 28px;border-top:1px solid rgba(15,23,42,.08);font-size:42px;line-height:1.45;word-break:break-word}
|
|
13
|
-
.asset-image{padding:24px;border-top:1px solid rgba(15,23,42,.08);background:#f8fafc;text-align:center}
|
|
14
|
-
.asset-image img{max-width:100%;max-height:70vh;box-shadow:0 10px 30px rgba(15,23,42,.16)}
|
|
15
|
-
.asset-text{margin:0;padding:18px 24px;overflow:auto;border-top:1px solid rgba(15,23,42,.08);background:#111827;color:#e5e7eb;font-size:13px;line-height:1.7}
|
|
16
|
-
.data-table-wrap{max-height:520px;overflow:auto;border-top:1px solid rgba(15,23,42,.08)}
|
|
17
|
-
.data-table{width:100%;border-collapse:collapse;font-size:13px}
|
|
18
|
-
.data-table th,.data-table td{max-width:260px;padding:10px 12px;border-bottom:1px solid rgba(15,23,42,.08);overflow:hidden;text-align:left;text-overflow:ellipsis;white-space:nowrap}
|
|
19
|
-
.data-table th{position:sticky;top:0;background:#f8fafc;color:#64748b;z-index:1}
|
|
20
|
-
`;
|
|
21
|
-
const fontMimeMap = {
|
|
22
|
-
otf: 'font/otf',
|
|
23
|
-
ttf: 'font/ttf',
|
|
24
|
-
woff: 'font/woff',
|
|
25
|
-
woff2: 'font/woff2',
|
|
26
|
-
};
|
|
27
|
-
const sampleText = 'Flyfish Viewer 轻量预览 AaBbCc 1234567890';
|
|
28
|
-
const createStyle = (documentRef) => {
|
|
29
|
-
const style = documentRef.createElement('style');
|
|
30
|
-
style.textContent = dataStyle;
|
|
31
|
-
return style;
|
|
32
|
-
};
|
|
33
|
-
const createElement = (documentRef, tagName, className, text) => {
|
|
34
|
-
const element = documentRef.createElement(tagName);
|
|
35
|
-
if (className) {
|
|
36
|
-
element.className = className;
|
|
37
|
-
}
|
|
38
|
-
if (text !== undefined) {
|
|
39
|
-
element.textContent = text;
|
|
40
|
-
}
|
|
41
|
-
return element;
|
|
42
|
-
};
|
|
43
|
-
const formatBytes = (value) => {
|
|
44
|
-
if (value < 1024) {
|
|
45
|
-
return `${value} B`;
|
|
46
|
-
}
|
|
47
|
-
if (value < 1024 * 1024) {
|
|
48
|
-
return `${(value / 1024).toFixed(1)} KB`;
|
|
49
|
-
}
|
|
50
|
-
return `${(value / 1024 / 1024).toFixed(1)} MB`;
|
|
51
|
-
};
|
|
52
|
-
const makeRows = (rows) => {
|
|
53
|
-
return rows.slice(0, 30).map(row => {
|
|
54
|
-
const next = {};
|
|
55
|
-
Object.entries(row).slice(0, 24).forEach(([key, value]) => {
|
|
56
|
-
if (typeof value === 'bigint') {
|
|
57
|
-
next[key] = value.toString();
|
|
58
|
-
}
|
|
59
|
-
else if (value instanceof Uint8Array) {
|
|
60
|
-
next[key] = `[bytes:${value.byteLength}]`;
|
|
61
|
-
}
|
|
62
|
-
else if (value && typeof value === 'object') {
|
|
63
|
-
next[key] = JSON.stringify(value).slice(0, 180);
|
|
64
|
-
}
|
|
65
|
-
else {
|
|
66
|
-
next[key] = value;
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
return next;
|
|
70
|
-
});
|
|
71
|
-
};
|
|
72
|
-
const extractReadableText = (buffer, max = 8000) => {
|
|
73
|
-
const bytes = new Uint8Array(buffer);
|
|
74
|
-
const ascii = Array.from(bytes.slice(0, Math.min(bytes.length, max)))
|
|
75
|
-
.map(byte => (byte >= 32 && byte <= 126) || byte === 10 || byte === 13 || byte === 9
|
|
76
|
-
? String.fromCharCode(byte)
|
|
77
|
-
: ' ')
|
|
78
|
-
.join('')
|
|
79
|
-
.replace(/[ \t]{3,}/g, ' ');
|
|
80
|
-
return ascii.trim().slice(0, max);
|
|
81
|
-
};
|
|
82
|
-
const readMagic = (buffer, length = 12) => {
|
|
83
|
-
return String.fromCharCode(...new Uint8Array(buffer.slice(0, length)));
|
|
84
|
-
};
|
|
85
|
-
const imageDataToUrl = (documentRef, imageData) => {
|
|
86
|
-
const canvas = documentRef.createElement('canvas');
|
|
87
|
-
canvas.width = imageData.width;
|
|
88
|
-
canvas.height = imageData.height;
|
|
89
|
-
const context = canvas.getContext('2d');
|
|
90
|
-
context === null || context === void 0 ? void 0 : context.putImageData(imageData, 0, 0);
|
|
91
|
-
return canvas.toDataURL('image/png');
|
|
92
|
-
};
|
|
93
|
-
const getWindowSqlWasmOverride = (documentRef) => {
|
|
94
|
-
var _a;
|
|
95
|
-
return ((_a = documentRef.defaultView) === null || _a === void 0 ? void 0 : _a.__FLYFISH_DATA_SQL_WASM_URL__) ||
|
|
96
|
-
(typeof window !== 'undefined' ? window.__FLYFISH_DATA_SQL_WASM_URL__ : undefined);
|
|
97
|
-
};
|
|
98
|
-
const renderFont = async (documentRef, buffer, type) => {
|
|
99
|
-
var _a;
|
|
100
|
-
const family = `FlyfishPreviewFont-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
101
|
-
const ownerWindow = documentRef.defaultView || (typeof window !== 'undefined' ? window : undefined);
|
|
102
|
-
const FontFaceConstructor = (ownerWindow === null || ownerWindow === void 0 ? void 0 : ownerWindow.FontFace) || (typeof FontFace !== 'undefined' ? FontFace : undefined);
|
|
103
|
-
if (!FontFaceConstructor) {
|
|
104
|
-
throw new Error('当前浏览器不支持 FontFace API');
|
|
105
|
-
}
|
|
106
|
-
const face = new FontFaceConstructor(family, buffer);
|
|
107
|
-
await face.load();
|
|
108
|
-
(_a = documentRef.fonts) === null || _a === void 0 ? void 0 : _a.add(face);
|
|
109
|
-
return {
|
|
110
|
-
title: '字体文件预览',
|
|
111
|
-
fontFamily: family,
|
|
112
|
-
summary: [
|
|
113
|
-
{ label: '格式', value: type.toUpperCase() },
|
|
114
|
-
{ label: '大小', value: formatBytes(buffer.byteLength) },
|
|
115
|
-
{ label: '渲染方式', value: 'Browser FontFace API' },
|
|
116
|
-
],
|
|
117
|
-
};
|
|
118
|
-
};
|
|
119
|
-
const renderPsd = async (documentRef, buffer) => {
|
|
120
|
-
var _a;
|
|
121
|
-
const { readPsd } = await import('ag-psd');
|
|
122
|
-
const psd = readPsd(buffer, { useImageData: true });
|
|
123
|
-
const image = psd.imageData ? imageDataToUrl(documentRef, psd.imageData) : undefined;
|
|
124
|
-
return {
|
|
125
|
-
title: 'PSD 图像结构预览',
|
|
126
|
-
image,
|
|
127
|
-
summary: [
|
|
128
|
-
{ label: '画布', value: `${psd.width || 0} x ${psd.height || 0}` },
|
|
129
|
-
{ label: '图层', value: String(((_a = psd.children) === null || _a === void 0 ? void 0 : _a.length) || 0) },
|
|
130
|
-
{ label: '渲染方式', value: 'ag-psd' },
|
|
131
|
-
],
|
|
132
|
-
rows: makeRows((psd.children || []).map((layer) => ({
|
|
133
|
-
name: layer.name || '-',
|
|
134
|
-
left: layer.left,
|
|
135
|
-
top: layer.top,
|
|
136
|
-
right: layer.right,
|
|
137
|
-
bottom: layer.bottom,
|
|
138
|
-
hidden: Boolean(layer.hidden),
|
|
139
|
-
}))),
|
|
140
|
-
};
|
|
141
|
-
};
|
|
142
|
-
const renderSqlite = async (documentRef, buffer, context) => {
|
|
143
|
-
var _a, _b, _c;
|
|
144
|
-
const { default: initSqlJs } = await import('sql.js');
|
|
145
|
-
const sqlWasmUrl = resolveFileViewerDataSqlWasmUrl((_a = context === null || context === void 0 ? void 0 : context.options) === null || _a === void 0 ? void 0 : _a.data, [
|
|
146
|
-
getWindowSqlWasmOverride(documentRef),
|
|
147
|
-
], documentRef.baseURI);
|
|
148
|
-
const SQL = await initSqlJs({ locateFile: () => sqlWasmUrl });
|
|
149
|
-
const db = new SQL.Database(new Uint8Array(buffer));
|
|
150
|
-
try {
|
|
151
|
-
const tableResult = db.exec("select name, type from sqlite_master where type in ('table','view') and name not like 'sqlite_%' order by type, name");
|
|
152
|
-
const tables = ((_b = tableResult[0]) === null || _b === void 0 ? void 0 : _b.values) || [];
|
|
153
|
-
const firstTable = String(((_c = tables[0]) === null || _c === void 0 ? void 0 : _c[0]) || '');
|
|
154
|
-
const rows = firstTable
|
|
155
|
-
? db.exec(`select * from "${firstTable.replace(/"/g, '""')}" limit 30`)[0]
|
|
156
|
-
: null;
|
|
157
|
-
return {
|
|
158
|
-
title: 'SQLite 数据库预览',
|
|
159
|
-
summary: [
|
|
160
|
-
{ label: '对象数', value: String(tables.length) },
|
|
161
|
-
{ label: '示例表', value: firstTable || '-' },
|
|
162
|
-
{ label: '渲染方式', value: 'sql.js WASM' },
|
|
163
|
-
],
|
|
164
|
-
rows: rows
|
|
165
|
-
? makeRows(rows.values.map((values) => Object.fromEntries(rows.columns.map((column, index) => [column, values[index]]))))
|
|
166
|
-
: makeRows(tables.map((value) => ({ name: value[0], type: value[1] }))),
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
finally {
|
|
170
|
-
db.close();
|
|
171
|
-
}
|
|
172
|
-
};
|
|
173
|
-
const renderParquet = async (buffer) => {
|
|
174
|
-
var _a, _b, _c;
|
|
175
|
-
const { parquetMetadataAsync, parquetReadObjects } = await import('hyparquet');
|
|
176
|
-
const file = {
|
|
177
|
-
byteLength: buffer.byteLength,
|
|
178
|
-
slice: (start, end) => buffer.slice(start, end),
|
|
179
|
-
};
|
|
180
|
-
const metadata = await parquetMetadataAsync(file);
|
|
181
|
-
const rows = await parquetReadObjects({ file, rowFormat: 'object', rowEnd: 30 });
|
|
182
|
-
return {
|
|
183
|
-
title: 'Parquet 列式数据预览',
|
|
184
|
-
summary: [
|
|
185
|
-
{ label: '行数', value: ((_b = (_a = metadata.num_rows) === null || _a === void 0 ? void 0 : _a.toString) === null || _b === void 0 ? void 0 : _b.call(_a)) || '-' },
|
|
186
|
-
{ label: '列数', value: String(((_c = metadata.schema) === null || _c === void 0 ? void 0 : _c.filter(item => item.name).length) || 0) },
|
|
187
|
-
{ label: '渲染方式', value: 'hyparquet' },
|
|
188
|
-
],
|
|
189
|
-
rows: makeRows(rows),
|
|
190
|
-
};
|
|
191
|
-
};
|
|
192
|
-
const renderAvro = async (buffer) => {
|
|
193
|
-
const avro = await import('avsc/etc/browser/avsc.js');
|
|
194
|
-
const decoder = avro.createBlobDecoder(new Blob([buffer]));
|
|
195
|
-
const rows = [];
|
|
196
|
-
let schema = '';
|
|
197
|
-
await new Promise((resolve, reject) => {
|
|
198
|
-
decoder.on('metadata', (type) => {
|
|
199
|
-
var _a;
|
|
200
|
-
schema = ((_a = type === null || type === void 0 ? void 0 : type.toString) === null || _a === void 0 ? void 0 : _a.call(type)) || '';
|
|
201
|
-
});
|
|
202
|
-
decoder.on('data', (value) => {
|
|
203
|
-
if (rows.length < 30) {
|
|
204
|
-
rows.push(value);
|
|
205
|
-
}
|
|
206
|
-
});
|
|
207
|
-
decoder.on('end', resolve);
|
|
208
|
-
decoder.on('error', reject);
|
|
209
|
-
});
|
|
210
|
-
return {
|
|
211
|
-
title: 'Avro 对象容器预览',
|
|
212
|
-
summary: [
|
|
213
|
-
{ label: '示例行', value: String(rows.length) },
|
|
214
|
-
{ label: 'Schema', value: schema ? '已读取' : '未读取' },
|
|
215
|
-
{ label: '渲染方式', value: 'avsc' },
|
|
216
|
-
],
|
|
217
|
-
rows: makeRows(rows),
|
|
218
|
-
text: schema.slice(0, 6000),
|
|
219
|
-
};
|
|
220
|
-
};
|
|
221
|
-
const renderWasm = async (buffer) => {
|
|
222
|
-
const module = await WebAssembly.compile(buffer.slice(0));
|
|
223
|
-
const imports = WebAssembly.Module.imports(module);
|
|
224
|
-
const exports = WebAssembly.Module.exports(module);
|
|
225
|
-
return {
|
|
226
|
-
title: 'WebAssembly 模块预览',
|
|
227
|
-
summary: [
|
|
228
|
-
{ label: '导入', value: String(imports.length) },
|
|
229
|
-
{ label: '导出', value: String(exports.length) },
|
|
230
|
-
{ label: '渲染方式', value: 'WebAssembly.Module' },
|
|
231
|
-
],
|
|
232
|
-
rows: makeRows([
|
|
233
|
-
...imports.map(item => ({ kind: 'import', module: item.module, name: item.name, type: item.kind })),
|
|
234
|
-
...exports.map(item => ({ kind: 'export', module: '-', name: item.name, type: item.kind })),
|
|
235
|
-
]),
|
|
236
|
-
};
|
|
237
|
-
};
|
|
238
|
-
const renderPostScriptLike = async (buffer, type) => ({
|
|
239
|
-
title: type === 'eps' ? 'EPS 矢量文件摘要' : 'Illustrator 文件摘要',
|
|
240
|
-
summary: [
|
|
241
|
-
{ label: 'Magic', value: readMagic(buffer).replace(/\s/g, ' ') },
|
|
242
|
-
{ label: '大小', value: formatBytes(buffer.byteLength) },
|
|
243
|
-
{ label: '说明', value: type === 'ai' ? '非 PDF-compatible AI 按摘要展示' : 'PostScript 摘要展示' },
|
|
244
|
-
],
|
|
245
|
-
text: extractReadableText(buffer),
|
|
246
|
-
});
|
|
247
|
-
const renderWebArchive = async (buffer) => ({
|
|
248
|
-
title: 'WebArchive 摘要预览',
|
|
249
|
-
summary: [
|
|
250
|
-
{ label: '容器', value: readMagic(buffer).startsWith('bplist') ? 'Binary plist' : 'WebArchive' },
|
|
251
|
-
{ label: '大小', value: formatBytes(buffer.byteLength) },
|
|
252
|
-
{ label: '说明', value: '安全提取可读片段,不执行网页脚本' },
|
|
253
|
-
],
|
|
254
|
-
text: extractReadableText(buffer),
|
|
255
|
-
});
|
|
256
|
-
const buildPreview = async (documentRef, buffer, type, context) => {
|
|
257
|
-
if (type in fontMimeMap) {
|
|
258
|
-
return renderFont(documentRef, buffer, type);
|
|
259
|
-
}
|
|
260
|
-
if (type === 'psd') {
|
|
261
|
-
return renderPsd(documentRef, buffer);
|
|
262
|
-
}
|
|
263
|
-
if (type === 'sqlite') {
|
|
264
|
-
return renderSqlite(documentRef, buffer, context);
|
|
265
|
-
}
|
|
266
|
-
if (type === 'parquet') {
|
|
267
|
-
return renderParquet(buffer);
|
|
268
|
-
}
|
|
269
|
-
if (type === 'avro') {
|
|
270
|
-
return renderAvro(buffer);
|
|
271
|
-
}
|
|
272
|
-
if (type === 'wasm') {
|
|
273
|
-
return renderWasm(buffer);
|
|
274
|
-
}
|
|
275
|
-
if (type === 'ai' || type === 'eps') {
|
|
276
|
-
return renderPostScriptLike(buffer, type);
|
|
277
|
-
}
|
|
278
|
-
if (type === 'webarchive') {
|
|
279
|
-
return renderWebArchive(buffer);
|
|
280
|
-
}
|
|
281
|
-
return {
|
|
282
|
-
title: '数据资产摘要',
|
|
283
|
-
summary: [
|
|
284
|
-
{ label: '格式', value: type.toUpperCase() },
|
|
285
|
-
{ label: '大小', value: formatBytes(buffer.byteLength) },
|
|
286
|
-
],
|
|
287
|
-
text: extractReadableText(buffer),
|
|
288
|
-
};
|
|
289
|
-
};
|
|
290
|
-
const appendSummary = (documentRef, parent, summary) => {
|
|
291
|
-
const summaryRoot = createElement(documentRef, 'div', 'data-summary');
|
|
292
|
-
summary.forEach(item => {
|
|
293
|
-
const cell = createElement(documentRef, 'div');
|
|
294
|
-
cell.append(createElement(documentRef, 'span', undefined, item.label), createElement(documentRef, 'strong', undefined, item.value));
|
|
295
|
-
summaryRoot.appendChild(cell);
|
|
296
|
-
});
|
|
297
|
-
parent.appendChild(summaryRoot);
|
|
298
|
-
};
|
|
299
|
-
const appendRows = (documentRef, parent, rows) => {
|
|
300
|
-
if (!(rows === null || rows === void 0 ? void 0 : rows.length)) {
|
|
301
|
-
return;
|
|
302
|
-
}
|
|
303
|
-
const keys = Object.keys(rows[0]);
|
|
304
|
-
const wrap = createElement(documentRef, 'div', 'data-table-wrap');
|
|
305
|
-
const table = createElement(documentRef, 'table', 'data-table');
|
|
306
|
-
const thead = createElement(documentRef, 'thead');
|
|
307
|
-
const headRow = createElement(documentRef, 'tr');
|
|
308
|
-
keys.forEach(key => {
|
|
309
|
-
headRow.appendChild(createElement(documentRef, 'th', undefined, key));
|
|
310
|
-
});
|
|
311
|
-
thead.appendChild(headRow);
|
|
312
|
-
const tbody = createElement(documentRef, 'tbody');
|
|
313
|
-
rows.forEach(row => {
|
|
314
|
-
const tr = createElement(documentRef, 'tr');
|
|
315
|
-
keys.forEach(key => {
|
|
316
|
-
var _a;
|
|
317
|
-
tr.appendChild(createElement(documentRef, 'td', undefined, String((_a = row[key]) !== null && _a !== void 0 ? _a : '')));
|
|
318
|
-
});
|
|
319
|
-
tbody.appendChild(tr);
|
|
320
|
-
});
|
|
321
|
-
table.append(thead, tbody);
|
|
322
|
-
wrap.appendChild(table);
|
|
323
|
-
parent.appendChild(wrap);
|
|
324
|
-
};
|
|
325
|
-
const renderPreviewDom = (documentRef, preview, type) => {
|
|
326
|
-
const root = createElement(documentRef, 'div', 'data-viewer');
|
|
327
|
-
const card = createElement(documentRef, 'section', 'data-card');
|
|
328
|
-
const header = createElement(documentRef, 'header', 'data-header');
|
|
329
|
-
header.append(createElement(documentRef, 'span', undefined, type.toUpperCase()), createElement(documentRef, 'h2', undefined, preview.title));
|
|
330
|
-
card.appendChild(header);
|
|
331
|
-
appendSummary(documentRef, card, preview.summary);
|
|
332
|
-
if (preview.fontFamily) {
|
|
333
|
-
const fontPreview = createElement(documentRef, 'div', 'font-preview', sampleText);
|
|
334
|
-
fontPreview.style.fontFamily = preview.fontFamily;
|
|
335
|
-
card.appendChild(fontPreview);
|
|
336
|
-
}
|
|
337
|
-
if (preview.image) {
|
|
338
|
-
const imageWrap = createElement(documentRef, 'div', 'asset-image');
|
|
339
|
-
const image = createElement(documentRef, 'img');
|
|
340
|
-
image.src = preview.image;
|
|
341
|
-
image.alt = '资产预览';
|
|
342
|
-
imageWrap.appendChild(image);
|
|
343
|
-
card.appendChild(imageWrap);
|
|
344
|
-
}
|
|
345
|
-
if (preview.text) {
|
|
346
|
-
card.appendChild(createElement(documentRef, 'pre', 'asset-text', preview.text));
|
|
347
|
-
}
|
|
348
|
-
appendRows(documentRef, card, preview.rows);
|
|
349
|
-
root.appendChild(card);
|
|
350
|
-
return root;
|
|
351
|
-
};
|
|
352
|
-
export default async function renderDataAsset(buffer, target, type = 'bin', context) {
|
|
353
|
-
const documentRef = target.ownerDocument || document;
|
|
354
|
-
const normalizedType = type.toLowerCase();
|
|
355
|
-
if (normalizedType === 'ai' && readMagic(buffer, 5) === '%PDF-' && (context === null || context === void 0 ? void 0 : context.renderNestedBuffer)) {
|
|
356
|
-
const rendered = await context.renderNestedBuffer(buffer, 'pdf', target, context);
|
|
357
|
-
if (rendered) {
|
|
358
|
-
return rendered;
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
const preview = await buildPreview(documentRef, buffer, normalizedType, context);
|
|
362
|
-
const root = renderPreviewDom(documentRef, preview, normalizedType);
|
|
363
|
-
target.replaceChildren(createStyle(documentRef), root);
|
|
364
|
-
return {
|
|
365
|
-
$el: root,
|
|
366
|
-
unmount() {
|
|
367
|
-
target.replaceChildren();
|
|
368
|
-
},
|
|
369
|
-
};
|
|
370
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { FileRenderContext, FileViewerRenderedInstance } from '../contracts/types';
|
|
2
|
-
declare global {
|
|
3
|
-
interface Window {
|
|
4
|
-
GraphViewer?: {
|
|
5
|
-
createViewerForElement: (element: HTMLElement, callback?: (viewer: unknown) => void) => unknown;
|
|
6
|
-
processElements: (className?: string) => void;
|
|
7
|
-
};
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
export default function renderDrawing(buffer: ArrayBuffer, target: HTMLDivElement, type?: string, context?: FileRenderContext): Promise<FileViewerRenderedInstance>;
|