@eternalheart/vue-file-preview 1.5.7 → 1.6.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.md +6 -0
- package/README.zh-CN.md +6 -0
- package/lib/FilePreviewContent.vue.d.ts +4 -1
- package/lib/FilePreviewContent.vue.d.ts.map +1 -1
- package/lib/FilePreviewEmbed.vue.d.ts +4 -1
- package/lib/FilePreviewEmbed.vue.d.ts.map +1 -1
- package/lib/FilePreviewModal.vue.d.ts +3 -0
- package/lib/FilePreviewModal.vue.d.ts.map +1 -1
- package/lib/chunks/{index-BX4Z60kq.mjs → index-2RMNnT0y.mjs} +22 -22
- package/lib/chunks/{index-BX4Z60kq.mjs.map → index-2RMNnT0y.mjs.map} +1 -1
- package/lib/chunks/{index-zUjR2PO_.mjs → index-B3Bv_Etp.mjs} +14 -14
- package/lib/chunks/{index-zUjR2PO_.mjs.map → index-B3Bv_Etp.mjs.map} +1 -1
- package/lib/chunks/{index-lfAz_44B.mjs → index-BAbJRSyS.mjs} +2 -2
- package/lib/chunks/{index-lfAz_44B.mjs.map → index-BAbJRSyS.mjs.map} +1 -1
- package/lib/chunks/{index-DAlOJW1K.mjs → index-BMz5d0Rs.mjs} +12 -12
- package/lib/chunks/{index-DAlOJW1K.mjs.map → index-BMz5d0Rs.mjs.map} +1 -1
- package/lib/chunks/{index-B81eS8ZU.mjs → index-C-ikNsDh.mjs} +2 -2
- package/lib/chunks/{index-B81eS8ZU.mjs.map → index-C-ikNsDh.mjs.map} +1 -1
- package/lib/chunks/index-C1Ev7K-W.mjs +265 -0
- package/lib/chunks/index-C1Ev7K-W.mjs.map +1 -0
- package/lib/chunks/{index-D3HEYT2-.mjs → index-CKKL3xiq.mjs} +24 -24
- package/lib/chunks/{index-D3HEYT2-.mjs.map → index-CKKL3xiq.mjs.map} +1 -1
- package/lib/chunks/{index-C6rL8w0j.mjs → index-CLDPeQbI.mjs} +7 -7
- package/lib/chunks/{index-C6rL8w0j.mjs.map → index-CLDPeQbI.mjs.map} +1 -1
- package/lib/chunks/{index-Cr5BqQ4w.mjs → index-CdUSfYfF.mjs} +19 -19
- package/lib/chunks/{index-Cr5BqQ4w.mjs.map → index-CdUSfYfF.mjs.map} +1 -1
- package/lib/chunks/{index-CCelprzZ.mjs → index-Cl9ZiWVl.mjs} +2 -2
- package/lib/chunks/{index-CCelprzZ.mjs.map → index-Cl9ZiWVl.mjs.map} +1 -1
- package/lib/chunks/{index-DxvfLZT4.mjs → index-Cy83gsDL.mjs} +49 -49
- package/lib/chunks/{index-DxvfLZT4.mjs.map → index-Cy83gsDL.mjs.map} +1 -1
- package/lib/chunks/{index-DrKqRTg_.mjs → index-D-YwSyrE.mjs} +24 -24
- package/lib/chunks/{index-DrKqRTg_.mjs.map → index-D-YwSyrE.mjs.map} +1 -1
- package/lib/chunks/{index-7qDT5hbP.mjs → index-DD9QZ4a2.mjs} +10 -10
- package/lib/chunks/{index-7qDT5hbP.mjs.map → index-DD9QZ4a2.mjs.map} +1 -1
- package/lib/chunks/{index-DezIRCWU.mjs → index-DL3qn-zG.mjs} +2 -2
- package/lib/chunks/{index-DezIRCWU.mjs.map → index-DL3qn-zG.mjs.map} +1 -1
- package/lib/chunks/{index-C7YsVakC.mjs → index-DcLACobl.mjs} +9 -9
- package/lib/chunks/{index-C7YsVakC.mjs.map → index-DcLACobl.mjs.map} +1 -1
- package/lib/chunks/{index-Bfj4Hl_r.mjs → index-Dcruruhk.mjs} +2 -2
- package/lib/chunks/{index-Bfj4Hl_r.mjs.map → index-Dcruruhk.mjs.map} +1 -1
- package/lib/chunks/{index-CkUaOFQc.mjs → index-Dqqq0VnB.mjs} +2 -2
- package/lib/chunks/{index-CkUaOFQc.mjs.map → index-Dqqq0VnB.mjs.map} +1 -1
- package/lib/chunks/{index-Vj1-8fIZ.mjs → index-DxSw5Ekp.mjs} +2 -2
- package/lib/chunks/{index-Vj1-8fIZ.mjs.map → index-DxSw5Ekp.mjs.map} +1 -1
- package/lib/chunks/{index-DOyDC7Qw.mjs → index-EPkoXaAV.mjs} +2 -2
- package/lib/chunks/{index-DOyDC7Qw.mjs.map → index-EPkoXaAV.mjs.map} +1 -1
- package/lib/chunks/{index-B8AUVu3R.mjs → index-M_Nf3MY5.mjs} +377 -326
- package/lib/chunks/index-M_Nf3MY5.mjs.map +1 -0
- package/lib/chunks/{useShikiHighlight-CZ3m7eHx.mjs → useShikiHighlight-CIk_v_UB.mjs} +2 -2
- package/lib/chunks/{useShikiHighlight-CZ3m7eHx.mjs.map → useShikiHighlight-CIk_v_UB.mjs.map} +1 -1
- package/lib/index.cjs +45 -45
- package/lib/index.cjs.map +1 -1
- package/lib/index.css +1 -1
- package/lib/index.mjs +2 -2
- package/lib/renderers/Cad/index.vue.d.ts +11 -0
- package/lib/renderers/Cad/index.vue.d.ts.map +1 -0
- package/lib/renderers/Cad/toolbar.d.ts +16 -0
- package/lib/renderers/Cad/toolbar.d.ts.map +1 -0
- package/lib/renderers/lazy.d.ts +1 -0
- package/lib/renderers/lazy.d.ts.map +1 -1
- package/lib/renderers/registry.d.ts.map +1 -1
- package/package.json +4 -1
- package/lib/chunks/index-B8AUVu3R.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -271,6 +271,10 @@ Differences from `FilePreviewModal`:
|
|
|
271
271
|
- **Formats**: TTF, OTF, WOFF, WOFF2
|
|
272
272
|
- **Features**: Font metadata (family, designer, version), character set preview, custom text input, multi-size display
|
|
273
273
|
|
|
274
|
+
### CAD / 3D Models
|
|
275
|
+
- **Formats**: DXF, STL, OBJ, GLTF, GLB
|
|
276
|
+
- **Features**: Interactive 3D viewer with orbit/zoom/pan controls, wireframe/solid toggle, grid & axes display, auto-centering
|
|
277
|
+
|
|
274
278
|
### Code & Text
|
|
275
279
|
- **Markdown**: GitHub Flavored Markdown, code highlighting
|
|
276
280
|
- **Code Files**: JS, TS, Python, Java, C++, Go, Rust, and 40+ languages
|
|
@@ -360,6 +364,7 @@ Differences from `FilePreviewModal`:
|
|
|
360
364
|
| `headless` | `boolean` | ❌ | Headless mode — hides toolbar and navigation arrows |
|
|
361
365
|
| `theme` | `Theme` | ❌ | Theme mode: `'auto' \| 'dark' \| 'light'` (default `'dark'`) |
|
|
362
366
|
| `showDownload` | `boolean` | ❌ | Whether to show the download button (default `true`) |
|
|
367
|
+
| `showClose` | `boolean` | ❌ | Whether to show the close button (default `true` for modal) |
|
|
363
368
|
|
|
364
369
|
### FilePreviewModal Events
|
|
365
370
|
|
|
@@ -382,6 +387,7 @@ Differences from `FilePreviewModal`:
|
|
|
382
387
|
| `headless` | `boolean` | ❌ | `false` | Headless mode — hides toolbar and navigation arrows |
|
|
383
388
|
| `theme` | `Theme` | ❌ | `'dark'` | Theme mode: `'auto' \| 'dark' \| 'light'` |
|
|
384
389
|
| `showDownload` | `boolean` | ❌ | `true` | Whether to show the download button |
|
|
390
|
+
| `showClose` | `boolean` | ❌ | `false` | Whether to show the close button (default `false` for embed) |
|
|
385
391
|
|
|
386
392
|
### FilePreviewEmbed Events
|
|
387
393
|
|
package/README.zh-CN.md
CHANGED
|
@@ -271,6 +271,10 @@ const files = [
|
|
|
271
271
|
- **格式**: TTF, OTF, WOFF, WOFF2
|
|
272
272
|
- **功能**: 字体元数据(字体家族、设计师、版本)、字符集预览、自定义文本输入、多字号展示
|
|
273
273
|
|
|
274
|
+
### CAD / 3D 模型
|
|
275
|
+
- **格式**: DXF, STL, OBJ, GLTF, GLB
|
|
276
|
+
- **功能**: 交互式 3D 查看器(旋转/缩放/平移)、线框/实体切换、网格与坐标轴显示、自动居中
|
|
277
|
+
|
|
274
278
|
### 代码 & 文本
|
|
275
279
|
- **Markdown**: GitHub Flavored Markdown,代码高亮
|
|
276
280
|
- **代码文件**: JS, TS, Python, Java, C++, Go, Rust 等 40+ 种语言
|
|
@@ -360,6 +364,7 @@ const files = [
|
|
|
360
364
|
| `headless` | `boolean` | ❌ | 无头模式,隐藏工具栏和导航箭头 |
|
|
361
365
|
| `theme` | `Theme` | ❌ | 主题模式: `'auto' \| 'dark' \| 'light'`(默认 `'dark'`) |
|
|
362
366
|
| `showDownload` | `boolean` | ❌ | 是否显示下载按钮(默认 `true`) |
|
|
367
|
+
| `showClose` | `boolean` | ❌ | 是否显示关闭按钮(modal 模式默认 `true`) |
|
|
363
368
|
|
|
364
369
|
### FilePreviewModal 事件
|
|
365
370
|
|
|
@@ -382,6 +387,7 @@ const files = [
|
|
|
382
387
|
| `headless` | `boolean` | ❌ | `false` | 无头模式,隐藏工具栏和导航箭头 |
|
|
383
388
|
| `theme` | `Theme` | ❌ | `'dark'` | 主题模式: `'auto' \| 'dark' \| 'light'` |
|
|
384
389
|
| `showDownload` | `boolean` | ❌ | `true` | 是否显示下载按钮 |
|
|
390
|
+
| `showClose` | `boolean` | ❌ | `false` | 是否显示关闭按钮(embed 模式默认 `false`) |
|
|
385
391
|
|
|
386
392
|
### FilePreviewEmbed 事件
|
|
387
393
|
|
|
@@ -24,8 +24,10 @@ interface Props {
|
|
|
24
24
|
shouldFetchAsBlob?: ShouldFetchAsBlob;
|
|
25
25
|
/** 自定义下载回调;不传时库内默认通过 fetcher 拉 Blob 触发下载 */
|
|
26
26
|
onDownload?: (file: PreviewFile) => void | Promise<void>;
|
|
27
|
-
/**
|
|
27
|
+
/** 关闭回调 */
|
|
28
28
|
onClose?: () => void;
|
|
29
|
+
/** 是否显示关闭按钮,默认根据 mode 决定(modal: true, embed: false) */
|
|
30
|
+
showClose?: boolean;
|
|
29
31
|
/** 是否显示下载按钮,默认 true */
|
|
30
32
|
showDownload?: boolean;
|
|
31
33
|
}
|
|
@@ -50,6 +52,7 @@ declare const _default: import("vue").DefineComponent<Props, {}, {}, {}, {}, imp
|
|
|
50
52
|
headless: boolean;
|
|
51
53
|
shouldFetchAsBlob: ShouldFetchAsBlob;
|
|
52
54
|
onDownload: (file: PreviewFile) => void | Promise<void>;
|
|
55
|
+
showClose: boolean;
|
|
53
56
|
showDownload: boolean;
|
|
54
57
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
55
58
|
export default _default;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FilePreviewContent.vue.d.ts","sourceRoot":"","sources":["../src/FilePreviewContent.vue"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"FilePreviewContent.vue.d.ts","sourceRoot":"","sources":["../src/FilePreviewContent.vue"],"names":[],"mappings":"AAokBA,OAAO,EAKL,KAAK,WAAW,EAChB,KAAK,gBAAgB,EACrB,KAAK,MAAM,EACX,KAAK,QAAQ,EACb,KAAK,KAAK,EACV,KAAK,0BAA0B,EAC/B,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACvB,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EAAE,cAAc,EAAyB,MAAM,SAAS,CAAC;AAarE,UAAU,KAAK;IACb,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,cAAc,EAAE,CAAC;IACnC,kCAAkC;IAClC,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;IACzB,qCAAqC;IACrC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS;IACT,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IACtD,8BAA8B;IAC9B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qBAAqB;IACrB,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,mDAAmD;IACnD,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,4BAA4B;IAC5B,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,4EAA4E;IAC5E,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,4CAA4C;IAC5C,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD,WAAW;IACX,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,uDAAuD;IACvD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,uBAAuB;IACvB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;;;;;;;;;;iBAbe,kBAAkB;oBAEf,cAAc;UAdxB,OAAO,GAAG,OAAO;aAoBd,MAAM,IAAI;WAVZ,KAAK;qBAZK,cAAc,EAAE;qBAIhB,MAAM;YAEf,MAAM;cAEJ,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;cAE1C,OAAO;uBAQE,iBAAiB;gBAExB,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;eAI5C,OAAO;kBAEJ,OAAO;;AA8tBxB,wBAQG"}
|
|
@@ -24,8 +24,10 @@ interface Props {
|
|
|
24
24
|
shouldFetchAsBlob?: ShouldFetchAsBlob;
|
|
25
25
|
/** 自定义下载回调;不传时库内默认通过 fetcher 拉 Blob 触发下载 */
|
|
26
26
|
onDownload?: (file: PreviewFile) => void | Promise<void>;
|
|
27
|
-
/**
|
|
27
|
+
/** 关闭回调 */
|
|
28
28
|
onClose?: () => void;
|
|
29
|
+
/** 是否显示关闭按钮,默认根据 mode 决定(embed: false) */
|
|
30
|
+
showClose?: boolean;
|
|
29
31
|
/** 是否显示下载按钮,默认 true */
|
|
30
32
|
showDownload?: boolean;
|
|
31
33
|
}
|
|
@@ -51,6 +53,7 @@ declare const _default: import("vue").DefineComponent<Props, {}, {}, {}, {}, imp
|
|
|
51
53
|
headless: boolean;
|
|
52
54
|
shouldFetchAsBlob: ShouldFetchAsBlob;
|
|
53
55
|
onDownload: (file: PreviewFile) => void | Promise<void>;
|
|
56
|
+
showClose: boolean;
|
|
54
57
|
showDownload: boolean;
|
|
55
58
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
56
59
|
export default _default;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FilePreviewEmbed.vue.d.ts","sourceRoot":"","sources":["../src/FilePreviewEmbed.vue"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"FilePreviewEmbed.vue.d.ts","sourceRoot":"","sources":["../src/FilePreviewEmbed.vue"],"names":[],"mappings":"AAoIA,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,0BAA0B,EAAE,cAAc,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACjM,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAG9C,UAAU,KAAK;IACb,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,cAAc,EAAE,CAAC;IACnC,uBAAuB;IACvB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,uBAAuB;IACvB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,SAAS;IACT,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IACtD,sBAAsB;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qBAAqB;IACrB,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,mDAAmD;IACnD,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,4BAA4B;IAC5B,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,4EAA4E;IAC5E,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,4CAA4C;IAC5C,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD,WAAW;IACX,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,0CAA0C;IAC1C,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,uBAAuB;IACvB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;;;;;;;;;;iBAbe,kBAAkB;oBAEf,cAAc;YAZtB,MAAM,GAAG,MAAM;WAFhB,MAAM,GAAG,MAAM;aAoBb,MAAM,IAAI;WAVZ,KAAK;kBAbE,MAAM;qBACH,cAAc,EAAE;YAMzB,MAAM;cAEJ,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;cAE1C,OAAO;uBAQE,iBAAiB;gBAExB,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;eAI5C,OAAO;kBAEJ,OAAO;;AAuLxB,wBAQG"}
|
|
@@ -21,6 +21,8 @@ interface Props {
|
|
|
21
21
|
shouldFetchAsBlob?: ShouldFetchAsBlob;
|
|
22
22
|
/** 自定义下载回调;不传时库内默认通过 fetcher 拉 Blob 触发下载 */
|
|
23
23
|
onDownload?: (file: PreviewFile) => void | Promise<void>;
|
|
24
|
+
/** 是否显示关闭按钮,默认根据 mode 决定(modal: true) */
|
|
25
|
+
showClose?: boolean;
|
|
24
26
|
/** 是否显示下载按钮,默认 true */
|
|
25
27
|
showDownload?: boolean;
|
|
26
28
|
}
|
|
@@ -42,6 +44,7 @@ declare const _default: import("vue").DefineComponent<Props, {}, {}, {}, {}, imp
|
|
|
42
44
|
headless: boolean;
|
|
43
45
|
shouldFetchAsBlob: ShouldFetchAsBlob;
|
|
44
46
|
onDownload: (file: PreviewFile) => void | Promise<void>;
|
|
47
|
+
showClose: boolean;
|
|
45
48
|
showDownload: boolean;
|
|
46
49
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
47
50
|
export default _default;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FilePreviewModal.vue.d.ts","sourceRoot":"","sources":["../src/FilePreviewModal.vue"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"FilePreviewModal.vue.d.ts","sourceRoot":"","sources":["../src/FilePreviewModal.vue"],"names":[],"mappings":"AA6IA,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,0BAA0B,EAAE,cAAc,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACjM,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAI9C,UAAU,KAAK;IACb,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,OAAO,CAAC;IAChB,eAAe,CAAC,EAAE,cAAc,EAAE,CAAC;IACnC,SAAS;IACT,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IACtD,sBAAsB;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qBAAqB;IACrB,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,mDAAmD;IACnD,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,4BAA4B;IAC5B,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,4EAA4E;IAC5E,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,4CAA4C;IAC5C,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD,yCAAyC;IACzC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,uBAAuB;IACvB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;;;;;;;;;;iBAXe,kBAAkB;oBAEf,cAAc;WAJvB,KAAK;qBARK,cAAc,EAAE;YAEzB,MAAM;cAEJ,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;cAE1C,OAAO;uBAQE,iBAAiB;gBAExB,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;eAE5C,OAAO;kBAEJ,OAAO;;AAiOxB,wBAQG"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { defineComponent as Me, ref as
|
|
1
|
+
import { defineComponent as Me, ref as i, watch as H, onBeforeUnmount as ee, onMounted as _e, computed as te, openBlock as x, createElementBlock as C, normalizeStyle as ae, createVNode as Re, unref as le, createElementVNode as X, createTextVNode as Le, toDisplayString as S, createCommentVNode as L, createBlock as oe, normalizeClass as ke } from "vue";
|
|
2
2
|
import { ZoomOut as Ee, ZoomIn as Ce, Scan as Te, Maximize2 as Ue, RotateCcw as Ye, RotateCw as ze, RefreshCw as Se, Loader2 as Xe } from "lucide-vue-next";
|
|
3
|
-
import { u as De,
|
|
3
|
+
import { u as De, i as We, n as Be, N as Oe } from "./index-M_Nf3MY5.mjs";
|
|
4
4
|
import { _ as ne } from "./RendererError.vue_vue_type_script_setup_true_lang-BNoENOWX.mjs";
|
|
5
5
|
import { T as je } from "./base.types-DaftHyg7.mjs";
|
|
6
6
|
const Pe = {
|
|
@@ -22,13 +22,13 @@ const Pe = {
|
|
|
22
22
|
fileSize: {},
|
|
23
23
|
file: {}
|
|
24
24
|
},
|
|
25
|
-
setup(
|
|
26
|
-
const f =
|
|
25
|
+
setup(ie, { expose: se }) {
|
|
26
|
+
const f = ie, V = new je(), { t: m } = De(), s = i(1), k = i(0), E = i(!1), w = i(null), d = i(!1), D = i(0), M = i(null), _ = i(""), R = i(1), T = i(1), F = i(!1), o = i({ x: 0, y: 0 }), g = i(!1);
|
|
27
27
|
let U = { x: 0, y: 0 };
|
|
28
|
-
const h =
|
|
29
|
-
H([
|
|
28
|
+
const h = i(1), r = i({ width: 0, height: 0 });
|
|
29
|
+
H([s, k], () => {
|
|
30
30
|
V.notify();
|
|
31
|
-
}),
|
|
31
|
+
}), se({
|
|
32
32
|
getToolbarGroups: () => [
|
|
33
33
|
{
|
|
34
34
|
items: [
|
|
@@ -37,11 +37,11 @@ const Pe = {
|
|
|
37
37
|
icon: Ee,
|
|
38
38
|
tooltip: m.value("toolbar.zoom_out"),
|
|
39
39
|
action: ce,
|
|
40
|
-
disabled:
|
|
40
|
+
disabled: s.value <= 0.01
|
|
41
41
|
},
|
|
42
42
|
{
|
|
43
43
|
type: "text",
|
|
44
|
-
content: `${Math.round(
|
|
44
|
+
content: `${Math.round(s.value * 100)}%`,
|
|
45
45
|
minWidth: "3rem"
|
|
46
46
|
},
|
|
47
47
|
{
|
|
@@ -49,7 +49,7 @@ const Pe = {
|
|
|
49
49
|
icon: Ce,
|
|
50
50
|
tooltip: m.value("toolbar.zoom_in"),
|
|
51
51
|
action: ue,
|
|
52
|
-
disabled:
|
|
52
|
+
disabled: s.value >= 10
|
|
53
53
|
}
|
|
54
54
|
]
|
|
55
55
|
},
|
|
@@ -99,30 +99,30 @@ const Pe = {
|
|
|
99
99
|
onToolbarChange: (e) => V.subscribe(e)
|
|
100
100
|
});
|
|
101
101
|
const ue = () => {
|
|
102
|
-
|
|
102
|
+
s.value = Math.min(s.value + 0.1, 10);
|
|
103
103
|
}, ce = () => {
|
|
104
|
-
|
|
104
|
+
s.value = Math.max(s.value - 0.1, 0.01);
|
|
105
105
|
}, re = () => {
|
|
106
106
|
k.value = (k.value + 90) % 360;
|
|
107
107
|
}, ve = () => {
|
|
108
108
|
k.value = (k.value - 90 + 360) % 360;
|
|
109
109
|
}, fe = () => {
|
|
110
|
-
|
|
110
|
+
s.value = 1, k.value = 0, o.value = { x: 0, y: 0 };
|
|
111
111
|
}, de = () => {
|
|
112
112
|
G();
|
|
113
113
|
}, G = () => {
|
|
114
114
|
if (v.value && r.value.width > 0 && r.value.height > 0) {
|
|
115
115
|
const e = v.value.clientWidth, t = v.value.clientHeight, a = e / r.value.width, u = t / r.value.height;
|
|
116
|
-
|
|
116
|
+
s.value = Math.max(0.01, Math.min(10, Math.min(a, u))), o.value = { x: 0, y: 0 };
|
|
117
117
|
}
|
|
118
|
-
}, he =
|
|
118
|
+
}, he = i(null), v = i(null);
|
|
119
119
|
let Y = null, W = null, B = null;
|
|
120
120
|
const p = /* @__PURE__ */ new Map();
|
|
121
121
|
let O = !1, j = 0, I = 1, Z = { x: 0, y: 0 }, A = 0;
|
|
122
122
|
H(
|
|
123
123
|
() => [f.url, f.file],
|
|
124
124
|
async () => {
|
|
125
|
-
if (_.value = "", E.value = !1, w.value = null, d.value = !1, M.value = null, D.value = 0, o.value = { x: 0, y: 0 }, h.value = 1,
|
|
125
|
+
if (_.value = "", E.value = !1, w.value = null, d.value = !1, M.value = null, D.value = 0, o.value = { x: 0, y: 0 }, h.value = 1, s.value = 1, R.value = 1, T.value = 1, F.value = !1, Y && (URL.revokeObjectURL(Y), Y = null), p.forEach((e) => URL.revokeObjectURL(e)), p.clear(), W = null, B = null, !f.file) {
|
|
126
126
|
_.value = f.url;
|
|
127
127
|
return;
|
|
128
128
|
}
|
|
@@ -187,10 +187,10 @@ const Pe = {
|
|
|
187
187
|
Y && URL.revokeObjectURL(Y), p.forEach((t) => URL.revokeObjectURL(t)), p.clear();
|
|
188
188
|
const e = v.value;
|
|
189
189
|
e && e.removeEventListener("wheel", N);
|
|
190
|
-
}), H(
|
|
190
|
+
}), H(s, (e) => {
|
|
191
191
|
h.value = e;
|
|
192
192
|
}), H(h, (e) => {
|
|
193
|
-
|
|
193
|
+
s.value = e;
|
|
194
194
|
});
|
|
195
195
|
const P = (e, t) => {
|
|
196
196
|
const a = v.value;
|
|
@@ -205,12 +205,12 @@ const Pe = {
|
|
|
205
205
|
const t = e.currentTarget;
|
|
206
206
|
if (r.value = { width: t.naturalWidth, height: t.naturalHeight }, v.value && r.value.width > 0 && r.value.height > 0) {
|
|
207
207
|
const a = v.value.clientWidth, u = v.value.clientHeight, n = a / r.value.width, c = u / r.value.height, l = Math.min(n, c);
|
|
208
|
-
|
|
208
|
+
s.value = Math.max(0.01, Math.min(10, l)), o.value = { x: 0, y: 0 };
|
|
209
209
|
}
|
|
210
210
|
}, me = () => {
|
|
211
211
|
w.value = m.value("image.load_failed"), E.value = !0;
|
|
212
212
|
}, ge = () => {
|
|
213
|
-
o.value = { x: 0, y: 0 },
|
|
213
|
+
o.value = { x: 0, y: 0 }, s.value = 1;
|
|
214
214
|
}, N = (e) => {
|
|
215
215
|
e.preventDefault(), e.stopPropagation();
|
|
216
216
|
const t = v.value;
|
|
@@ -255,7 +255,7 @@ const Pe = {
|
|
|
255
255
|
y: t[0].clientY - o.value.y
|
|
256
256
|
};
|
|
257
257
|
const a = Date.now();
|
|
258
|
-
a - A < 300 && (o.value = { x: 0, y: 0 },
|
|
258
|
+
a - A < 300 && (o.value = { x: 0, y: 0 }, s.value = 1), A = a;
|
|
259
259
|
} else t.length === 2 && (g.value = !1, j = Math.hypot(
|
|
260
260
|
t[1].clientX - t[0].clientX,
|
|
261
261
|
t[1].clientY - t[0].clientY
|
|
@@ -360,4 +360,4 @@ const Pe = {
|
|
|
360
360
|
export {
|
|
361
361
|
at as default
|
|
362
362
|
};
|
|
363
|
-
//# sourceMappingURL=index-
|
|
363
|
+
//# sourceMappingURL=index-2RMNnT0y.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index-BX4Z60kq.mjs","sources":["../../src/renderers/Image/index.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, watch, onMounted, onBeforeUnmount, computed } from 'vue';\nimport { Loader2, ZoomIn, ZoomOut, RotateCw, RotateCcw, Scan, RefreshCw, Maximize2 } from 'lucide-vue-next';\nimport { formatFileSize, detectImageFormat, getLoaderForMimeType } from '@eternalheart/file-preview-core';\nimport type { PreviewFile } from '@eternalheart/file-preview-core';\nimport { useTranslator } from '../../composables/useTranslator';\nimport RendererError from '../RendererError.vue';\nimport { ToolbarEventEmitter } from '../base.types';\nimport type { RendererHandle } from '../base.types';\nimport type { ToolbarGroup } from '../toolbar.types';\n\nconst props = defineProps<{\n url: string;\n fileSize?: number;\n file?: PreviewFile | File;\n}>();\n\nconst emitter = new ToolbarEventEmitter();\n\nconst { t } = useTranslator();\n\n// 内部状态管理\nconst zoom = ref(1);\nconst rotation = ref(0);\nconst loaded = ref(false);\nconst error = ref<string | null>(null);\nconst decoding = ref(false);\nconst decodeProgress = ref(0);\nconst decodeError = ref<string | null>(null);\nconst imageSrc = ref<string>('');\nconst currentPage = ref(1);\nconst totalPages = ref(1);\nconst isRawThumbnail = ref(false);\nconst position = ref({ x: 0, y: 0 });\nconst isDragging = ref(false);\nlet dragStart = { x: 0, y: 0 };\nconst internalZoom = ref(1);\nconst naturalSize = ref({ width: 0, height: 0 });\n\n// 通知工具栏变化\nwatch([zoom, rotation], () => {\n emitter.notify();\n});\n\n// 工具栏配置(对齐 React 的 4 个按钮组)\nconst getToolbarGroups = (): ToolbarGroup[] => [\n {\n items: [\n {\n type: 'button',\n icon: ZoomOut,\n tooltip: t.value('toolbar.zoom_out'),\n action: handleZoomOut,\n disabled: zoom.value <= 0.01,\n },\n {\n type: 'text',\n content: `${Math.round(zoom.value * 100)}%`,\n minWidth: '3rem',\n },\n {\n type: 'button',\n icon: ZoomIn,\n tooltip: t.value('toolbar.zoom_in'),\n action: handleZoomIn,\n disabled: zoom.value >= 10,\n },\n ],\n },\n {\n items: [\n {\n type: 'button',\n icon: Scan,\n tooltip: t.value('toolbar.fit_to_window'),\n action: handleFitToWidth,\n },\n {\n type: 'button',\n icon: Maximize2,\n tooltip: t.value('toolbar.original_size'),\n action: handleOriginalSize,\n },\n ],\n },\n {\n items: [\n {\n type: 'button',\n icon: RotateCcw,\n tooltip: t.value('toolbar.rotate_left'),\n action: handleRotateLeft,\n },\n {\n type: 'button',\n icon: RotateCw,\n tooltip: t.value('toolbar.rotate_right'),\n action: handleRotateRight,\n },\n ],\n },\n {\n items: [\n {\n type: 'button',\n icon: RefreshCw,\n tooltip: t.value('toolbar.reset'),\n action: handleReset,\n },\n ],\n },\n];\n\n// 暴露接口\ndefineExpose<RendererHandle>({\n getToolbarGroups,\n onToolbarChange: (listener) => emitter.subscribe(listener),\n});\n\n// 工具栏操作\nconst handleZoomIn = () => {\n zoom.value = Math.min(zoom.value + 0.1, 10);\n};\n\nconst handleZoomOut = () => {\n zoom.value = Math.max(zoom.value - 0.1, 0.01);\n};\n\nconst handleRotateRight = () => {\n rotation.value = (rotation.value + 90) % 360;\n};\n\nconst handleRotateLeft = () => {\n rotation.value = (rotation.value - 90 + 360) % 360;\n};\n\nconst handleOriginalSize = () => {\n zoom.value = 1;\n rotation.value = 0;\n position.value = { x: 0, y: 0 };\n};\n\nconst handleReset = () => {\n handleFitToWidth();\n};\n\nconst handleFitToWidth = () => {\n if (containerRef.value && naturalSize.value.width > 0 && naturalSize.value.height > 0) {\n const containerWidth = containerRef.value.clientWidth;\n const containerHeight = containerRef.value.clientHeight;\n const scaleX = containerWidth / naturalSize.value.width;\n const scaleY = containerHeight / naturalSize.value.height;\n zoom.value = Math.max(0.01, Math.min(10, Math.min(scaleX, scaleY)));\n position.value = { x: 0, y: 0 };\n }\n};\n\nconst imgRef = ref<HTMLImageElement | null>(null);\nconst containerRef = ref<HTMLDivElement | null>(null);\nlet blobUrl: string | null = null;\nlet fileBlobCache: Blob | null = null;\nlet loaderCache: any = null;\nconst pageCache = new Map<number, string>();\n\nlet isTouchDevice = false;\nlet touchStartDistance = 0;\nlet touchStartZoom = 1;\nlet touchStartPos = { x: 0, y: 0 };\nlet lastTapTime = 0;\n\n// 解码逻辑\nwatch(\n () => [props.url, props.file] as const,\n async () => {\n // 重置状态:清空 src 以避免上一张图片的 onLoad/onError 误触发到新文件\n imageSrc.value = '';\n loaded.value = false;\n error.value = null;\n decoding.value = false;\n decodeError.value = null;\n decodeProgress.value = 0;\n position.value = { x: 0, y: 0 };\n internalZoom.value = 1;\n zoom.value = 1;\n currentPage.value = 1;\n totalPages.value = 1;\n isRawThumbnail.value = false;\n\n // 清理旧的 blob URL 与缓存\n if (blobUrl) {\n URL.revokeObjectURL(blobUrl);\n blobUrl = null;\n }\n pageCache.forEach((url) => URL.revokeObjectURL(url));\n pageCache.clear();\n fileBlobCache = null;\n loaderCache = null;\n\n // 如果没有 file 对象,直接使用 url\n if (!props.file) {\n imageSrc.value = props.url;\n return;\n }\n\n try {\n // 检测图片格式\n const mimeType = await detectImageFormat(props.file);\n const loader = await getLoaderForMimeType(mimeType);\n\n // 如果不需要解码,直接使用原 URL\n if (!loader || !(await loader.needsDecode(mimeType))) {\n imageSrc.value = props.url;\n return;\n }\n\n // 需要解码\n decoding.value = true;\n\n // 获取文件 Blob\n let fileBlob: Blob;\n if (props.file instanceof Blob) {\n fileBlob = props.file;\n } else {\n const response = await fetch(props.url);\n if (!response.ok) throw new Error('Failed to fetch file');\n fileBlob = await response.blob();\n }\n\n // 缓存 Blob 与 loader\n fileBlobCache = fileBlob;\n loaderCache = loader;\n\n // 标记 RAW 缩略图模式\n const isRaw = mimeType.startsWith('image/x-');\n if (isRaw) {\n isRawThumbnail.value = true;\n }\n\n // 获取元数据(用于检测多页 TIFF)\n if (loader.getMetadata) {\n try {\n const metadata = await loader.getMetadata(fileBlob);\n if (metadata.pageCount && metadata.pageCount > 1) {\n totalPages.value = metadata.pageCount;\n }\n } catch {\n // 忽略元数据获取失败\n }\n }\n\n // 调用 loader 解码(第 1 页 / 缩略图模式)\n const decodedBlob = await loader.decode(fileBlob, {\n page: 1,\n fullQuality: false,\n onProgress: (percent: number) => {\n decodeProgress.value = percent;\n },\n });\n\n // 生成 blob URL\n const url = typeof decodedBlob === 'string'\n ? decodedBlob\n : URL.createObjectURL(decodedBlob);\n\n blobUrl = url;\n pageCache.set(1, url);\n imageSrc.value = url;\n decoding.value = false;\n } catch (err: any) {\n decodeError.value = err?.message || '解码失败';\n decoding.value = false;\n }\n },\n { immediate: true }\n);\n\n// 多页 TIFF 翻页\nconst handlePageChange = async (page: number) => {\n if (!fileBlobCache || !loaderCache) return;\n if (page < 1 || page > totalPages.value) return;\n\n // 命中缓存:直接切换\n const cached = pageCache.get(page);\n if (cached) {\n currentPage.value = page;\n imageSrc.value = cached;\n return;\n }\n\n // 解码新页面\n decoding.value = true;\n try {\n const decodedBlob = await loaderCache.decode(fileBlobCache, { page });\n const url = typeof decodedBlob === 'string'\n ? decodedBlob\n : URL.createObjectURL(decodedBlob);\n\n // LRU:缓存超过 10 页时删除最早的\n if (pageCache.size >= 10) {\n const firstKey = pageCache.keys().next().value;\n if (firstKey !== undefined) {\n const oldUrl = pageCache.get(firstKey);\n if (oldUrl) URL.revokeObjectURL(oldUrl);\n pageCache.delete(firstKey);\n }\n }\n\n pageCache.set(page, url);\n currentPage.value = page;\n imageSrc.value = url;\n decoding.value = false;\n } catch (err: any) {\n decodeError.value = err?.message || '翻页解码失败';\n decoding.value = false;\n }\n};\n\n// Cleanup on unmount\nonBeforeUnmount(() => {\n if (blobUrl) {\n URL.revokeObjectURL(blobUrl);\n }\n pageCache.forEach((url) => URL.revokeObjectURL(url));\n pageCache.clear();\n const container = containerRef.value;\n if (container) {\n container.removeEventListener('wheel', handleWheelNative);\n }\n});\n\n// 同步 zoom 和 internalZoom\nwatch(zoom, (z) => {\n internalZoom.value = z;\n});\n\nwatch(internalZoom, (z) => {\n zoom.value = z;\n});\n\nconst clampPosition = (pos: { x: number; y: number }, currentZoom: number) => {\n const container = containerRef.value;\n if (!container || naturalSize.value.width === 0) return pos;\n\n const containerW = container.clientWidth;\n const containerH = container.clientHeight;\n const imgW = naturalSize.value.width * currentZoom;\n const imgH = naturalSize.value.height * currentZoom;\n\n const margin = Math.min(80, containerW * 0.15, containerH * 0.15);\n const rangeX = (containerW + imgW) / 2 - margin;\n const rangeY = (containerH + imgH) / 2 - margin;\n\n return {\n x: rangeX > 0 ? Math.max(-rangeX, Math.min(rangeX, pos.x)) : 0,\n y: rangeY > 0 ? Math.max(-rangeY, Math.min(rangeY, pos.y)) : 0,\n };\n};\n\nconst handleLoad = (e: Event) => {\n loaded.value = true;\n const img = e.currentTarget as HTMLImageElement;\n naturalSize.value = { width: img.naturalWidth, height: img.naturalHeight };\n\n // 自动适应窗口\n if (containerRef.value && naturalSize.value.width > 0 && naturalSize.value.height > 0) {\n const containerWidth = containerRef.value.clientWidth;\n const containerHeight = containerRef.value.clientHeight;\n const scaleX = containerWidth / naturalSize.value.width;\n const scaleY = containerHeight / naturalSize.value.height;\n const newZoom = Math.min(scaleX, scaleY);\n zoom.value = Math.max(0.01, Math.min(10, newZoom));\n position.value = { x: 0, y: 0 };\n }\n};\n\nconst handleError = () => {\n error.value = t.value('image.load_failed');\n loaded.value = true;\n};\n\nconst handleDoubleClick = () => {\n position.value = { x: 0, y: 0 };\n zoom.value = 1;\n};\n\n// 鼠标滚轮缩放\nconst handleWheelNative = (e: WheelEvent) => {\n e.preventDefault();\n e.stopPropagation();\n\n const container = containerRef.value;\n if (!container) return;\n\n const rect = container.getBoundingClientRect();\n const mouseX = e.clientX - rect.left - rect.width / 2;\n const mouseY = e.clientY - rect.top - rect.height / 2;\n\n const delta = e.deltaY > 0 ? -0.05 : 0.05;\n const prev = internalZoom.value;\n const newZoom = Math.max(0.01, Math.min(10, prev + delta));\n const scale = newZoom / prev;\n\n position.value = clampPosition(\n {\n x: mouseX - scale * (mouseX - position.value.x),\n y: mouseY - scale * (mouseY - position.value.y),\n },\n newZoom\n );\n\n internalZoom.value = newZoom;\n};\n\nonMounted(() => {\n const container = containerRef.value;\n if (container) {\n container.addEventListener('wheel', handleWheelNative, { passive: false });\n container.addEventListener('touchstart', handleTouchStart, { passive: false });\n container.addEventListener('touchmove', handleTouchMove, { passive: false });\n container.addEventListener('touchend', handleTouchEnd);\n container.addEventListener('touchcancel', handleTouchEnd);\n }\n});\n\nonBeforeUnmount(() => {\n const container = containerRef.value;\n if (container) {\n container.removeEventListener('wheel', handleWheelNative);\n container.removeEventListener('touchstart', handleTouchStart);\n container.removeEventListener('touchmove', handleTouchMove);\n container.removeEventListener('touchend', handleTouchEnd);\n container.removeEventListener('touchcancel', handleTouchEnd);\n }\n});\n\nconst handleMouseDown = (e: MouseEvent) => {\n if (isTouchDevice) return;\n if (e.button !== 0) return;\n isDragging.value = true;\n dragStart = {\n x: e.clientX - position.value.x,\n y: e.clientY - position.value.y,\n };\n};\n\nconst handleMouseMove = (e: MouseEvent) => {\n if (isTouchDevice) return;\n if (!isDragging.value) return;\n position.value = clampPosition(\n {\n x: e.clientX - dragStart.x,\n y: e.clientY - dragStart.y,\n },\n internalZoom.value\n );\n};\n\nconst handleMouseUp = () => {\n if (isTouchDevice) return;\n isDragging.value = false;\n};\n\n// 触屏事件处理\nconst handleTouchStart = (e: TouchEvent) => {\n isTouchDevice = true;\n e.preventDefault();\n\n const touches = e.touches;\n if (touches.length === 1) {\n // 单指拖拽\n isDragging.value = true;\n dragStart = {\n x: touches[0].clientX - position.value.x,\n y: touches[0].clientY - position.value.y,\n };\n\n // 双击检测\n const now = Date.now();\n if (now - lastTapTime < 300) {\n // 双击复原:居中 + 缩放100%\n position.value = { x: 0, y: 0 };\n zoom.value = 1;\n }\n lastTapTime = now;\n } else if (touches.length === 2) {\n // 双指缩放初始化\n isDragging.value = false;\n const distance = Math.hypot(\n touches[1].clientX - touches[0].clientX,\n touches[1].clientY - touches[0].clientY\n );\n touchStartDistance = distance;\n touchStartZoom = internalZoom.value;\n touchStartPos = { ...position.value };\n }\n};\n\nconst handleTouchMove = (e: TouchEvent) => {\n e.preventDefault();\n\n const touches = e.touches;\n if (touches.length === 1 && isDragging.value) {\n // 单指拖拽\n position.value = clampPosition(\n {\n x: touches[0].clientX - dragStart.x,\n y: touches[0].clientY - dragStart.y,\n },\n internalZoom.value\n );\n } else if (touches.length === 2) {\n // 双指缩放\n const container = containerRef.value;\n if (!container) return;\n\n const distance = Math.hypot(\n touches[1].clientX - touches[0].clientX,\n touches[1].clientY - touches[0].clientY\n );\n\n // 最小距离变化阈值,防止抖动\n if (Math.abs(distance - touchStartDistance) < 5) return;\n\n const scale = distance / touchStartDistance;\n const newZoom = Math.max(0.01, Math.min(10, touchStartZoom * scale));\n\n // 双指中心点作为缩放原点\n const rect = container.getBoundingClientRect();\n const centerX = (touches[0].clientX + touches[1].clientX) / 2 - rect.left - rect.width / 2;\n const centerY = (touches[0].clientY + touches[1].clientY) / 2 - rect.top - rect.height / 2;\n\n const zoomScale = newZoom / internalZoom.value;\n position.value = clampPosition(\n {\n x: centerX - zoomScale * (centerX - touchStartPos.x),\n y: centerY - zoomScale * (centerY - touchStartPos.y),\n },\n newZoom\n );\n\n internalZoom.value = newZoom;\n }\n};\n\nconst handleTouchEnd = () => {\n isDragging.value = false;\n touchStartDistance = 0;\n};\n\nconst transformStyle = computed(() => ({\n transform: `translate(${position.value.x}px, ${position.value.y}px) scale(${internalZoom.value}) rotate(${rotation.value}deg)`,\n transformOrigin: 'center',\n transition: isDragging.value ? 'none' : 'transform 0.3s ease-out',\n opacity: loaded.value && !error.value && !decodeError.value ? 1 : 0,\n}));\n\nconst sizeText = computed(() => {\n if (props.fileSize == null) return '';\n return ` · ${formatFileSize(props.fileSize)}`;\n});\n</script>\n\n<template>\n <div\n ref=\"containerRef\"\n class=\"vfp-relative vfp-flex vfp-items-center vfp-justify-center vfp-w-full vfp-h-full vfp-overflow-hidden\"\n :style=\"{ cursor: isDragging ? 'grabbing' : 'grab', touchAction: 'none' }\"\n @mousedown=\"handleMouseDown\"\n @mousemove=\"handleMouseMove\"\n @mouseup=\"handleMouseUp\"\n @mouseleave=\"handleMouseUp\"\n >\n <!-- 解码中 -->\n <div\n v-if=\"decoding\"\n class=\"vfp-absolute vfp-inset-0 vfp-flex vfp-flex-col vfp-items-center vfp-justify-center vfp-bg-surface-1/80 vfp-z-10\"\n >\n <Loader2 class=\"vfp-w-12 vfp-h-12 vfp-text-fg-primary vfp-animate-spin\" />\n <p class=\"vfp-mt-4 vfp-text-fg-secondary\">\n 正在解码... <span v-if=\"decodeProgress > 0\">{{ Math.round(decodeProgress) }}%</span>\n </p>\n </div>\n\n <!-- 解码错误 -->\n <RendererError v-if=\"decodeError\" :message=\"t('image.decode_failed')\" :detail=\"decodeError\" />\n\n <div v-if=\"!loaded && !error && !decoding && !decodeError\" class=\"vfp-flex vfp-items-center vfp-justify-center\">\n <div\n class=\"vfp-w-12 vfp-h-12 vfp-border-4 vfp-border-line-strong vfp-border-t-spinner-head vfp-rounded-full vfp-animate-spin\"\n />\n </div>\n\n <RendererError v-if=\"error\" :message=\"error\" />\n\n <img\n v-if=\"imageSrc\"\n ref=\"imgRef\"\n :src=\"imageSrc\"\n alt=\"Preview\"\n :class=\"['vfp-max-w-none vfp-select-none', (!loaded || error || decodeError) && 'vfp-hidden']\"\n :style=\"transformStyle\"\n :draggable=\"false\"\n @load=\"handleLoad\"\n @error=\"handleError\"\n @dblclick=\"handleDoubleClick\"\n />\n\n <div\n v-if=\"loaded && !error && naturalSize.width > 0\"\n class=\"vfp-absolute vfp-bottom-2 vfp-right-3 vfp-text-[10px] vfp-text-fg-disabled hover:vfp-text-fg-secondary vfp-transition-colors vfp-pointer-events-auto vfp-select-none vfp-cursor-default\"\n >\n {{ naturalSize.width }} × {{ naturalSize.height }}{{ sizeText }}\n </div>\n\n <!-- 多页 TIFF 翻页器 -->\n <div\n v-if=\"totalPages > 1\"\n class=\"vfp-absolute vfp-bottom-2 vfp-left-1/2 -vfp-translate-x-1/2 vfp-flex vfp-items-center vfp-gap-2 vfp-px-3 vfp-py-1.5 vfp-bg-surface-toolbar vfp-border vfp-border-line vfp-rounded-lg vfp-text-sm vfp-text-fg-primary vfp-shadow-md\"\n >\n <button\n type=\"button\"\n :disabled=\"currentPage <= 1 || decoding\"\n class=\"vfp-px-2 vfp-py-0.5 vfp-rounded hover:vfp-bg-surface-nav-hover disabled:vfp-opacity-40 disabled:vfp-cursor-not-allowed\"\n @click=\"handlePageChange(currentPage - 1)\"\n >\n 上一页\n </button>\n <span class=\"vfp-text-fg-secondary vfp-tabular-nums\">\n {{ currentPage }} / {{ totalPages }}\n </span>\n <button\n type=\"button\"\n :disabled=\"currentPage >= totalPages || decoding\"\n class=\"vfp-px-2 vfp-py-0.5 vfp-rounded hover:vfp-bg-surface-nav-hover disabled:vfp-opacity-40 disabled:vfp-cursor-not-allowed\"\n @click=\"handlePageChange(currentPage + 1)\"\n >\n 下一页\n </button>\n </div>\n </div>\n</template>\n"],"names":["props","__props","emitter","ToolbarEventEmitter","t","useTranslator","zoom","ref","rotation","loaded","error","decoding","decodeProgress","decodeError","imageSrc","currentPage","totalPages","isRawThumbnail","position","isDragging","dragStart","internalZoom","naturalSize","watch","__expose","ZoomOut","handleZoomOut","ZoomIn","handleZoomIn","Scan","handleFitToWidth","Maximize2","handleOriginalSize","RotateCcw","handleRotateLeft","RotateCw","handleRotateRight","RefreshCw","handleReset","listener","containerRef","containerWidth","containerHeight","scaleX","scaleY","imgRef","blobUrl","fileBlobCache","loaderCache","pageCache","isTouchDevice","touchStartDistance","touchStartZoom","touchStartPos","lastTapTime","url","mimeType","detectImageFormat","loader","getLoaderForMimeType","fileBlob","response","metadata","decodedBlob","percent","err","handlePageChange","page","cached","firstKey","oldUrl","onBeforeUnmount","container","handleWheelNative","z","clampPosition","pos","currentZoom","containerW","containerH","imgW","imgH","margin","rangeX","rangeY","handleLoad","img","newZoom","handleError","handleDoubleClick","rect","mouseX","mouseY","delta","prev","scale","onMounted","handleTouchStart","handleTouchMove","handleTouchEnd","handleMouseDown","handleMouseMove","handleMouseUp","touches","now","distance","centerX","centerY","zoomScale","transformStyle","computed","sizeText","formatFileSize","_createElementBlock","_openBlock","_hoisted_1","_createVNode","_unref","Loader2","_createElementVNode","_hoisted_2","_createBlock","RendererError","_hoisted_4","_cache","_normalizeClass","_hoisted_6","_toDisplayString","_hoisted_7","$event","_hoisted_8","_hoisted_9","_hoisted_10"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index-2RMNnT0y.mjs","sources":["../../src/renderers/Image/index.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { ref, watch, onMounted, onBeforeUnmount, computed } from 'vue';\nimport { Loader2, ZoomIn, ZoomOut, RotateCw, RotateCcw, Scan, RefreshCw, Maximize2 } from 'lucide-vue-next';\nimport { formatFileSize, detectImageFormat, getLoaderForMimeType } from '@eternalheart/file-preview-core';\nimport type { PreviewFile } from '@eternalheart/file-preview-core';\nimport { useTranslator } from '../../composables/useTranslator';\nimport RendererError from '../RendererError.vue';\nimport { ToolbarEventEmitter } from '../base.types';\nimport type { RendererHandle } from '../base.types';\nimport type { ToolbarGroup } from '../toolbar.types';\n\nconst props = defineProps<{\n url: string;\n fileSize?: number;\n file?: PreviewFile | File;\n}>();\n\nconst emitter = new ToolbarEventEmitter();\n\nconst { t } = useTranslator();\n\n// 内部状态管理\nconst zoom = ref(1);\nconst rotation = ref(0);\nconst loaded = ref(false);\nconst error = ref<string | null>(null);\nconst decoding = ref(false);\nconst decodeProgress = ref(0);\nconst decodeError = ref<string | null>(null);\nconst imageSrc = ref<string>('');\nconst currentPage = ref(1);\nconst totalPages = ref(1);\nconst isRawThumbnail = ref(false);\nconst position = ref({ x: 0, y: 0 });\nconst isDragging = ref(false);\nlet dragStart = { x: 0, y: 0 };\nconst internalZoom = ref(1);\nconst naturalSize = ref({ width: 0, height: 0 });\n\n// 通知工具栏变化\nwatch([zoom, rotation], () => {\n emitter.notify();\n});\n\n// 工具栏配置(对齐 React 的 4 个按钮组)\nconst getToolbarGroups = (): ToolbarGroup[] => [\n {\n items: [\n {\n type: 'button',\n icon: ZoomOut,\n tooltip: t.value('toolbar.zoom_out'),\n action: handleZoomOut,\n disabled: zoom.value <= 0.01,\n },\n {\n type: 'text',\n content: `${Math.round(zoom.value * 100)}%`,\n minWidth: '3rem',\n },\n {\n type: 'button',\n icon: ZoomIn,\n tooltip: t.value('toolbar.zoom_in'),\n action: handleZoomIn,\n disabled: zoom.value >= 10,\n },\n ],\n },\n {\n items: [\n {\n type: 'button',\n icon: Scan,\n tooltip: t.value('toolbar.fit_to_window'),\n action: handleFitToWidth,\n },\n {\n type: 'button',\n icon: Maximize2,\n tooltip: t.value('toolbar.original_size'),\n action: handleOriginalSize,\n },\n ],\n },\n {\n items: [\n {\n type: 'button',\n icon: RotateCcw,\n tooltip: t.value('toolbar.rotate_left'),\n action: handleRotateLeft,\n },\n {\n type: 'button',\n icon: RotateCw,\n tooltip: t.value('toolbar.rotate_right'),\n action: handleRotateRight,\n },\n ],\n },\n {\n items: [\n {\n type: 'button',\n icon: RefreshCw,\n tooltip: t.value('toolbar.reset'),\n action: handleReset,\n },\n ],\n },\n];\n\n// 暴露接口\ndefineExpose<RendererHandle>({\n getToolbarGroups,\n onToolbarChange: (listener) => emitter.subscribe(listener),\n});\n\n// 工具栏操作\nconst handleZoomIn = () => {\n zoom.value = Math.min(zoom.value + 0.1, 10);\n};\n\nconst handleZoomOut = () => {\n zoom.value = Math.max(zoom.value - 0.1, 0.01);\n};\n\nconst handleRotateRight = () => {\n rotation.value = (rotation.value + 90) % 360;\n};\n\nconst handleRotateLeft = () => {\n rotation.value = (rotation.value - 90 + 360) % 360;\n};\n\nconst handleOriginalSize = () => {\n zoom.value = 1;\n rotation.value = 0;\n position.value = { x: 0, y: 0 };\n};\n\nconst handleReset = () => {\n handleFitToWidth();\n};\n\nconst handleFitToWidth = () => {\n if (containerRef.value && naturalSize.value.width > 0 && naturalSize.value.height > 0) {\n const containerWidth = containerRef.value.clientWidth;\n const containerHeight = containerRef.value.clientHeight;\n const scaleX = containerWidth / naturalSize.value.width;\n const scaleY = containerHeight / naturalSize.value.height;\n zoom.value = Math.max(0.01, Math.min(10, Math.min(scaleX, scaleY)));\n position.value = { x: 0, y: 0 };\n }\n};\n\nconst imgRef = ref<HTMLImageElement | null>(null);\nconst containerRef = ref<HTMLDivElement | null>(null);\nlet blobUrl: string | null = null;\nlet fileBlobCache: Blob | null = null;\nlet loaderCache: any = null;\nconst pageCache = new Map<number, string>();\n\nlet isTouchDevice = false;\nlet touchStartDistance = 0;\nlet touchStartZoom = 1;\nlet touchStartPos = { x: 0, y: 0 };\nlet lastTapTime = 0;\n\n// 解码逻辑\nwatch(\n () => [props.url, props.file] as const,\n async () => {\n // 重置状态:清空 src 以避免上一张图片的 onLoad/onError 误触发到新文件\n imageSrc.value = '';\n loaded.value = false;\n error.value = null;\n decoding.value = false;\n decodeError.value = null;\n decodeProgress.value = 0;\n position.value = { x: 0, y: 0 };\n internalZoom.value = 1;\n zoom.value = 1;\n currentPage.value = 1;\n totalPages.value = 1;\n isRawThumbnail.value = false;\n\n // 清理旧的 blob URL 与缓存\n if (blobUrl) {\n URL.revokeObjectURL(blobUrl);\n blobUrl = null;\n }\n pageCache.forEach((url) => URL.revokeObjectURL(url));\n pageCache.clear();\n fileBlobCache = null;\n loaderCache = null;\n\n // 如果没有 file 对象,直接使用 url\n if (!props.file) {\n imageSrc.value = props.url;\n return;\n }\n\n try {\n // 检测图片格式\n const mimeType = await detectImageFormat(props.file);\n const loader = await getLoaderForMimeType(mimeType);\n\n // 如果不需要解码,直接使用原 URL\n if (!loader || !(await loader.needsDecode(mimeType))) {\n imageSrc.value = props.url;\n return;\n }\n\n // 需要解码\n decoding.value = true;\n\n // 获取文件 Blob\n let fileBlob: Blob;\n if (props.file instanceof Blob) {\n fileBlob = props.file;\n } else {\n const response = await fetch(props.url);\n if (!response.ok) throw new Error('Failed to fetch file');\n fileBlob = await response.blob();\n }\n\n // 缓存 Blob 与 loader\n fileBlobCache = fileBlob;\n loaderCache = loader;\n\n // 标记 RAW 缩略图模式\n const isRaw = mimeType.startsWith('image/x-');\n if (isRaw) {\n isRawThumbnail.value = true;\n }\n\n // 获取元数据(用于检测多页 TIFF)\n if (loader.getMetadata) {\n try {\n const metadata = await loader.getMetadata(fileBlob);\n if (metadata.pageCount && metadata.pageCount > 1) {\n totalPages.value = metadata.pageCount;\n }\n } catch {\n // 忽略元数据获取失败\n }\n }\n\n // 调用 loader 解码(第 1 页 / 缩略图模式)\n const decodedBlob = await loader.decode(fileBlob, {\n page: 1,\n fullQuality: false,\n onProgress: (percent: number) => {\n decodeProgress.value = percent;\n },\n });\n\n // 生成 blob URL\n const url = typeof decodedBlob === 'string'\n ? decodedBlob\n : URL.createObjectURL(decodedBlob);\n\n blobUrl = url;\n pageCache.set(1, url);\n imageSrc.value = url;\n decoding.value = false;\n } catch (err: any) {\n decodeError.value = err?.message || '解码失败';\n decoding.value = false;\n }\n },\n { immediate: true }\n);\n\n// 多页 TIFF 翻页\nconst handlePageChange = async (page: number) => {\n if (!fileBlobCache || !loaderCache) return;\n if (page < 1 || page > totalPages.value) return;\n\n // 命中缓存:直接切换\n const cached = pageCache.get(page);\n if (cached) {\n currentPage.value = page;\n imageSrc.value = cached;\n return;\n }\n\n // 解码新页面\n decoding.value = true;\n try {\n const decodedBlob = await loaderCache.decode(fileBlobCache, { page });\n const url = typeof decodedBlob === 'string'\n ? decodedBlob\n : URL.createObjectURL(decodedBlob);\n\n // LRU:缓存超过 10 页时删除最早的\n if (pageCache.size >= 10) {\n const firstKey = pageCache.keys().next().value;\n if (firstKey !== undefined) {\n const oldUrl = pageCache.get(firstKey);\n if (oldUrl) URL.revokeObjectURL(oldUrl);\n pageCache.delete(firstKey);\n }\n }\n\n pageCache.set(page, url);\n currentPage.value = page;\n imageSrc.value = url;\n decoding.value = false;\n } catch (err: any) {\n decodeError.value = err?.message || '翻页解码失败';\n decoding.value = false;\n }\n};\n\n// Cleanup on unmount\nonBeforeUnmount(() => {\n if (blobUrl) {\n URL.revokeObjectURL(blobUrl);\n }\n pageCache.forEach((url) => URL.revokeObjectURL(url));\n pageCache.clear();\n const container = containerRef.value;\n if (container) {\n container.removeEventListener('wheel', handleWheelNative);\n }\n});\n\n// 同步 zoom 和 internalZoom\nwatch(zoom, (z) => {\n internalZoom.value = z;\n});\n\nwatch(internalZoom, (z) => {\n zoom.value = z;\n});\n\nconst clampPosition = (pos: { x: number; y: number }, currentZoom: number) => {\n const container = containerRef.value;\n if (!container || naturalSize.value.width === 0) return pos;\n\n const containerW = container.clientWidth;\n const containerH = container.clientHeight;\n const imgW = naturalSize.value.width * currentZoom;\n const imgH = naturalSize.value.height * currentZoom;\n\n const margin = Math.min(80, containerW * 0.15, containerH * 0.15);\n const rangeX = (containerW + imgW) / 2 - margin;\n const rangeY = (containerH + imgH) / 2 - margin;\n\n return {\n x: rangeX > 0 ? Math.max(-rangeX, Math.min(rangeX, pos.x)) : 0,\n y: rangeY > 0 ? Math.max(-rangeY, Math.min(rangeY, pos.y)) : 0,\n };\n};\n\nconst handleLoad = (e: Event) => {\n loaded.value = true;\n const img = e.currentTarget as HTMLImageElement;\n naturalSize.value = { width: img.naturalWidth, height: img.naturalHeight };\n\n // 自动适应窗口\n if (containerRef.value && naturalSize.value.width > 0 && naturalSize.value.height > 0) {\n const containerWidth = containerRef.value.clientWidth;\n const containerHeight = containerRef.value.clientHeight;\n const scaleX = containerWidth / naturalSize.value.width;\n const scaleY = containerHeight / naturalSize.value.height;\n const newZoom = Math.min(scaleX, scaleY);\n zoom.value = Math.max(0.01, Math.min(10, newZoom));\n position.value = { x: 0, y: 0 };\n }\n};\n\nconst handleError = () => {\n error.value = t.value('image.load_failed');\n loaded.value = true;\n};\n\nconst handleDoubleClick = () => {\n position.value = { x: 0, y: 0 };\n zoom.value = 1;\n};\n\n// 鼠标滚轮缩放\nconst handleWheelNative = (e: WheelEvent) => {\n e.preventDefault();\n e.stopPropagation();\n\n const container = containerRef.value;\n if (!container) return;\n\n const rect = container.getBoundingClientRect();\n const mouseX = e.clientX - rect.left - rect.width / 2;\n const mouseY = e.clientY - rect.top - rect.height / 2;\n\n const delta = e.deltaY > 0 ? -0.05 : 0.05;\n const prev = internalZoom.value;\n const newZoom = Math.max(0.01, Math.min(10, prev + delta));\n const scale = newZoom / prev;\n\n position.value = clampPosition(\n {\n x: mouseX - scale * (mouseX - position.value.x),\n y: mouseY - scale * (mouseY - position.value.y),\n },\n newZoom\n );\n\n internalZoom.value = newZoom;\n};\n\nonMounted(() => {\n const container = containerRef.value;\n if (container) {\n container.addEventListener('wheel', handleWheelNative, { passive: false });\n container.addEventListener('touchstart', handleTouchStart, { passive: false });\n container.addEventListener('touchmove', handleTouchMove, { passive: false });\n container.addEventListener('touchend', handleTouchEnd);\n container.addEventListener('touchcancel', handleTouchEnd);\n }\n});\n\nonBeforeUnmount(() => {\n const container = containerRef.value;\n if (container) {\n container.removeEventListener('wheel', handleWheelNative);\n container.removeEventListener('touchstart', handleTouchStart);\n container.removeEventListener('touchmove', handleTouchMove);\n container.removeEventListener('touchend', handleTouchEnd);\n container.removeEventListener('touchcancel', handleTouchEnd);\n }\n});\n\nconst handleMouseDown = (e: MouseEvent) => {\n if (isTouchDevice) return;\n if (e.button !== 0) return;\n isDragging.value = true;\n dragStart = {\n x: e.clientX - position.value.x,\n y: e.clientY - position.value.y,\n };\n};\n\nconst handleMouseMove = (e: MouseEvent) => {\n if (isTouchDevice) return;\n if (!isDragging.value) return;\n position.value = clampPosition(\n {\n x: e.clientX - dragStart.x,\n y: e.clientY - dragStart.y,\n },\n internalZoom.value\n );\n};\n\nconst handleMouseUp = () => {\n if (isTouchDevice) return;\n isDragging.value = false;\n};\n\n// 触屏事件处理\nconst handleTouchStart = (e: TouchEvent) => {\n isTouchDevice = true;\n e.preventDefault();\n\n const touches = e.touches;\n if (touches.length === 1) {\n // 单指拖拽\n isDragging.value = true;\n dragStart = {\n x: touches[0].clientX - position.value.x,\n y: touches[0].clientY - position.value.y,\n };\n\n // 双击检测\n const now = Date.now();\n if (now - lastTapTime < 300) {\n // 双击复原:居中 + 缩放100%\n position.value = { x: 0, y: 0 };\n zoom.value = 1;\n }\n lastTapTime = now;\n } else if (touches.length === 2) {\n // 双指缩放初始化\n isDragging.value = false;\n const distance = Math.hypot(\n touches[1].clientX - touches[0].clientX,\n touches[1].clientY - touches[0].clientY\n );\n touchStartDistance = distance;\n touchStartZoom = internalZoom.value;\n touchStartPos = { ...position.value };\n }\n};\n\nconst handleTouchMove = (e: TouchEvent) => {\n e.preventDefault();\n\n const touches = e.touches;\n if (touches.length === 1 && isDragging.value) {\n // 单指拖拽\n position.value = clampPosition(\n {\n x: touches[0].clientX - dragStart.x,\n y: touches[0].clientY - dragStart.y,\n },\n internalZoom.value\n );\n } else if (touches.length === 2) {\n // 双指缩放\n const container = containerRef.value;\n if (!container) return;\n\n const distance = Math.hypot(\n touches[1].clientX - touches[0].clientX,\n touches[1].clientY - touches[0].clientY\n );\n\n // 最小距离变化阈值,防止抖动\n if (Math.abs(distance - touchStartDistance) < 5) return;\n\n const scale = distance / touchStartDistance;\n const newZoom = Math.max(0.01, Math.min(10, touchStartZoom * scale));\n\n // 双指中心点作为缩放原点\n const rect = container.getBoundingClientRect();\n const centerX = (touches[0].clientX + touches[1].clientX) / 2 - rect.left - rect.width / 2;\n const centerY = (touches[0].clientY + touches[1].clientY) / 2 - rect.top - rect.height / 2;\n\n const zoomScale = newZoom / internalZoom.value;\n position.value = clampPosition(\n {\n x: centerX - zoomScale * (centerX - touchStartPos.x),\n y: centerY - zoomScale * (centerY - touchStartPos.y),\n },\n newZoom\n );\n\n internalZoom.value = newZoom;\n }\n};\n\nconst handleTouchEnd = () => {\n isDragging.value = false;\n touchStartDistance = 0;\n};\n\nconst transformStyle = computed(() => ({\n transform: `translate(${position.value.x}px, ${position.value.y}px) scale(${internalZoom.value}) rotate(${rotation.value}deg)`,\n transformOrigin: 'center',\n transition: isDragging.value ? 'none' : 'transform 0.3s ease-out',\n opacity: loaded.value && !error.value && !decodeError.value ? 1 : 0,\n}));\n\nconst sizeText = computed(() => {\n if (props.fileSize == null) return '';\n return ` · ${formatFileSize(props.fileSize)}`;\n});\n</script>\n\n<template>\n <div\n ref=\"containerRef\"\n class=\"vfp-relative vfp-flex vfp-items-center vfp-justify-center vfp-w-full vfp-h-full vfp-overflow-hidden\"\n :style=\"{ cursor: isDragging ? 'grabbing' : 'grab', touchAction: 'none' }\"\n @mousedown=\"handleMouseDown\"\n @mousemove=\"handleMouseMove\"\n @mouseup=\"handleMouseUp\"\n @mouseleave=\"handleMouseUp\"\n >\n <!-- 解码中 -->\n <div\n v-if=\"decoding\"\n class=\"vfp-absolute vfp-inset-0 vfp-flex vfp-flex-col vfp-items-center vfp-justify-center vfp-bg-surface-1/80 vfp-z-10\"\n >\n <Loader2 class=\"vfp-w-12 vfp-h-12 vfp-text-fg-primary vfp-animate-spin\" />\n <p class=\"vfp-mt-4 vfp-text-fg-secondary\">\n 正在解码... <span v-if=\"decodeProgress > 0\">{{ Math.round(decodeProgress) }}%</span>\n </p>\n </div>\n\n <!-- 解码错误 -->\n <RendererError v-if=\"decodeError\" :message=\"t('image.decode_failed')\" :detail=\"decodeError\" />\n\n <div v-if=\"!loaded && !error && !decoding && !decodeError\" class=\"vfp-flex vfp-items-center vfp-justify-center\">\n <div\n class=\"vfp-w-12 vfp-h-12 vfp-border-4 vfp-border-line-strong vfp-border-t-spinner-head vfp-rounded-full vfp-animate-spin\"\n />\n </div>\n\n <RendererError v-if=\"error\" :message=\"error\" />\n\n <img\n v-if=\"imageSrc\"\n ref=\"imgRef\"\n :src=\"imageSrc\"\n alt=\"Preview\"\n :class=\"['vfp-max-w-none vfp-select-none', (!loaded || error || decodeError) && 'vfp-hidden']\"\n :style=\"transformStyle\"\n :draggable=\"false\"\n @load=\"handleLoad\"\n @error=\"handleError\"\n @dblclick=\"handleDoubleClick\"\n />\n\n <div\n v-if=\"loaded && !error && naturalSize.width > 0\"\n class=\"vfp-absolute vfp-bottom-2 vfp-right-3 vfp-text-[10px] vfp-text-fg-disabled hover:vfp-text-fg-secondary vfp-transition-colors vfp-pointer-events-auto vfp-select-none vfp-cursor-default\"\n >\n {{ naturalSize.width }} × {{ naturalSize.height }}{{ sizeText }}\n </div>\n\n <!-- 多页 TIFF 翻页器 -->\n <div\n v-if=\"totalPages > 1\"\n class=\"vfp-absolute vfp-bottom-2 vfp-left-1/2 -vfp-translate-x-1/2 vfp-flex vfp-items-center vfp-gap-2 vfp-px-3 vfp-py-1.5 vfp-bg-surface-toolbar vfp-border vfp-border-line vfp-rounded-lg vfp-text-sm vfp-text-fg-primary vfp-shadow-md\"\n >\n <button\n type=\"button\"\n :disabled=\"currentPage <= 1 || decoding\"\n class=\"vfp-px-2 vfp-py-0.5 vfp-rounded hover:vfp-bg-surface-nav-hover disabled:vfp-opacity-40 disabled:vfp-cursor-not-allowed\"\n @click=\"handlePageChange(currentPage - 1)\"\n >\n 上一页\n </button>\n <span class=\"vfp-text-fg-secondary vfp-tabular-nums\">\n {{ currentPage }} / {{ totalPages }}\n </span>\n <button\n type=\"button\"\n :disabled=\"currentPage >= totalPages || decoding\"\n class=\"vfp-px-2 vfp-py-0.5 vfp-rounded hover:vfp-bg-surface-nav-hover disabled:vfp-opacity-40 disabled:vfp-cursor-not-allowed\"\n @click=\"handlePageChange(currentPage + 1)\"\n >\n 下一页\n </button>\n </div>\n </div>\n</template>\n"],"names":["props","__props","emitter","ToolbarEventEmitter","t","useTranslator","zoom","ref","rotation","loaded","error","decoding","decodeProgress","decodeError","imageSrc","currentPage","totalPages","isRawThumbnail","position","isDragging","dragStart","internalZoom","naturalSize","watch","__expose","ZoomOut","handleZoomOut","ZoomIn","handleZoomIn","Scan","handleFitToWidth","Maximize2","handleOriginalSize","RotateCcw","handleRotateLeft","RotateCw","handleRotateRight","RefreshCw","handleReset","listener","containerRef","containerWidth","containerHeight","scaleX","scaleY","imgRef","blobUrl","fileBlobCache","loaderCache","pageCache","isTouchDevice","touchStartDistance","touchStartZoom","touchStartPos","lastTapTime","url","mimeType","detectImageFormat","loader","getLoaderForMimeType","fileBlob","response","metadata","decodedBlob","percent","err","handlePageChange","page","cached","firstKey","oldUrl","onBeforeUnmount","container","handleWheelNative","z","clampPosition","pos","currentZoom","containerW","containerH","imgW","imgH","margin","rangeX","rangeY","handleLoad","img","newZoom","handleError","handleDoubleClick","rect","mouseX","mouseY","delta","prev","scale","onMounted","handleTouchStart","handleTouchMove","handleTouchEnd","handleMouseDown","handleMouseMove","handleMouseUp","touches","now","distance","centerX","centerY","zoomScale","transformStyle","computed","sizeText","formatFileSize","_createElementBlock","_openBlock","_hoisted_1","_createVNode","_unref","Loader2","_createElementVNode","_hoisted_2","_createBlock","RendererError","_hoisted_4","_cache","_normalizeClass","_hoisted_6","_toDisplayString","_hoisted_7","$event","_hoisted_8","_hoisted_9","_hoisted_10"],"mappings":"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"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { defineComponent as
|
|
1
|
+
import { defineComponent as P, computed as D, ref as c, watch as k, nextTick as K, openBlock as d, createElementBlock as u, createElementVNode as H, createBlock as O, toDisplayString as R } from "vue";
|
|
2
2
|
import N from "markdown-it";
|
|
3
|
-
import
|
|
3
|
+
import U from "@traptitech/markdown-it-katex";
|
|
4
4
|
import { codeToHtml as E } from "shiki";
|
|
5
|
-
import { Code as
|
|
6
|
-
import { u as
|
|
5
|
+
import { Code as Y, Eye as F } from "lucide-vue-next";
|
|
6
|
+
import { u as W, a as z, b as J, U as Q, _ as X } from "./index-M_Nf3MY5.mjs";
|
|
7
7
|
import { _ as Z } from "./RendererError.vue_vue_type_script_setup_true_lang-BNoENOWX.mjs";
|
|
8
8
|
import { T as ee } from "./base.types-DaftHyg7.mjs";
|
|
9
9
|
const te = {
|
|
@@ -19,13 +19,13 @@ const te = {
|
|
|
19
19
|
}, ne = ["innerHTML"], le = {
|
|
20
20
|
key: 3,
|
|
21
21
|
class: "vfp-w-full vfp-h-full vfp-overflow-auto vfp-py-6 vfp-px-4"
|
|
22
|
-
}, ae = ["innerHTML"], B = '<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg>', L = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg>', se = '<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>', ce = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>', ie = /* @__PURE__ */
|
|
22
|
+
}, ae = ["innerHTML"], B = '<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg>', L = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg>', se = '<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>', ce = '<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>', ie = /* @__PURE__ */ P({
|
|
23
23
|
__name: "index",
|
|
24
24
|
props: {
|
|
25
25
|
url: {}
|
|
26
26
|
},
|
|
27
27
|
setup(q, { expose: V }) {
|
|
28
|
-
const _ = q, x = new ee(), { t: i } =
|
|
28
|
+
const _ = q, x = new ee(), { t: i } = W(), j = z(), G = J(), y = D(() => G.value === "light" ? "github-light" : "github-dark"), s = c("preview"), b = c(""), v = c(""), f = c(""), p = c(!0), h = c(null), m = c(null);
|
|
29
29
|
k([s, p], () => {
|
|
30
30
|
x.notify();
|
|
31
31
|
}), V({
|
|
@@ -35,7 +35,7 @@ const te = {
|
|
|
35
35
|
items: [
|
|
36
36
|
{
|
|
37
37
|
type: "button",
|
|
38
|
-
icon: s.value === "preview" ?
|
|
38
|
+
icon: s.value === "preview" ? Y : F,
|
|
39
39
|
tooltip: s.value === "preview" ? i.value("toolbar.source") : i.value("toolbar.preview"),
|
|
40
40
|
action: () => {
|
|
41
41
|
s.value = s.value === "preview" ? "source" : "preview";
|
|
@@ -53,7 +53,7 @@ const te = {
|
|
|
53
53
|
typographer: !1,
|
|
54
54
|
breaks: !1
|
|
55
55
|
});
|
|
56
|
-
l.use(
|
|
56
|
+
l.use(U), l.renderer.rules.fence = (e, r) => {
|
|
57
57
|
const t = e[r], n = t.info ? t.info.trim() : "", o = t.content;
|
|
58
58
|
return n ? `<div class="code-block-wrapper">
|
|
59
59
|
<div class="code-block-header">
|
|
@@ -76,7 +76,7 @@ const te = {
|
|
|
76
76
|
const C = async () => {
|
|
77
77
|
p.value = !0, h.value = null;
|
|
78
78
|
try {
|
|
79
|
-
const e = await
|
|
79
|
+
const e = await Q(_.url, { fetcher: j.value });
|
|
80
80
|
b.value = e, v.value = l.render(e), p.value = !1;
|
|
81
81
|
try {
|
|
82
82
|
f.value = await E(e, { lang: "markdown", theme: y.value });
|
|
@@ -100,7 +100,7 @@ const te = {
|
|
|
100
100
|
t.innerHTML = r ? B : L, t.title = i.value("markdown.copy_code");
|
|
101
101
|
}, 2e3);
|
|
102
102
|
}), t;
|
|
103
|
-
},
|
|
103
|
+
}, I = async () => {
|
|
104
104
|
var t, n;
|
|
105
105
|
if (!m.value) return;
|
|
106
106
|
const e = m.value.querySelectorAll('pre[data-shiki-pending="1"]');
|
|
@@ -123,12 +123,12 @@ const te = {
|
|
|
123
123
|
}
|
|
124
124
|
};
|
|
125
125
|
return k(() => _.url, C, { immediate: !0 }), k(v, async () => {
|
|
126
|
-
v.value && (await
|
|
126
|
+
v.value && (await K(), I());
|
|
127
127
|
}), k(y, () => {
|
|
128
128
|
b.value && C();
|
|
129
129
|
}), (e, r) => p.value ? (d(), u("div", te, [...r[0] || (r[0] = [
|
|
130
130
|
H("div", { class: "vfp-w-12 vfp-h-12 vfp-border-4 vfp-border-line-strong vfp-border-t-spinner-head vfp-rounded-full vfp-animate-spin" }, null, -1)
|
|
131
|
-
])])) : h.value ? (d(),
|
|
131
|
+
])])) : h.value ? (d(), O(Z, {
|
|
132
132
|
key: 1,
|
|
133
133
|
message: h.value
|
|
134
134
|
}, null, 8, ["message"])) : s.value === "source" ? (d(), u("div", oe, [
|
|
@@ -136,7 +136,7 @@ const te = {
|
|
|
136
136
|
key: 1,
|
|
137
137
|
class: "shiki-wrapper",
|
|
138
138
|
innerHTML: f.value
|
|
139
|
-
}, null, 8, ne)) : (d(), u("pre", re,
|
|
139
|
+
}, null, 8, ne)) : (d(), u("pre", re, R(b.value), 1))
|
|
140
140
|
])) : (d(), u("div", le, [
|
|
141
141
|
H("div", {
|
|
142
142
|
ref_key: "containerRef",
|
|
@@ -150,4 +150,4 @@ const te = {
|
|
|
150
150
|
export {
|
|
151
151
|
ke as default
|
|
152
152
|
};
|
|
153
|
-
//# sourceMappingURL=index-
|
|
153
|
+
//# sourceMappingURL=index-B3Bv_Etp.mjs.map
|