添加预览图片功能

This commit is contained in:
2026-07-19 19:15:22 +08:00
parent 164e658e5b
commit 3c6b142098
11 changed files with 196 additions and 113 deletions

View File

@@ -58,5 +58,11 @@
"userTurns": 4, "userTurns": 4,
"basisHash": "aaae120b1f33c2f2", "basisHash": "aaae120b1f33c2f2",
"updatedAt": 1783674217586 "updatedAt": 1783674217586
},
"topic_20260710-090003_60717bd90da5ec68": {
"stage": 1,
"userTurns": 1,
"basisHash": "cd60850543a6985b",
"updatedAt": 1783700550206
} }
} }

View File

@@ -1,14 +1,3 @@
{ {
"topic_20260630-144952_fd0584fe8f266d6a": 1782830992641, "topic_20260707-131941_481a70d2be9881dd": 1783430381000
"topic_20260703-022850_50330b4eb93f116d": 1783045730616,
"topic_20260706-145507_076a32df5f4276b5": 1783349707873,
"topic_20260706-153317_238f65a7a470703c": 1783351997029,
"topic_20260710-024855_5c4b1833149d66bb": 1783651735446,
"topic_20260710-030936_cedad2907c3954fe": 1783652976310,
"topic_20260710-032907_600adc6b00791607": 1783654147796,
"topic_20260710-034033_6432ee6566f7b432": 1783654833341,
"topic_20260710-034724_3c6fb29d8d0a4119": 1783655244536,
"topic_20260710-054543_5e7c427932aecd7b": 1783662343716,
"topic_20260710-063053_353d579222d3b70b": 1783665053391,
"topic_20260710-090003_60717bd90da5ec68": 1783674003004
} }

View File

@@ -38,6 +38,7 @@
"topic_20260707-093602_5c75f77654cab0c3": "manual", "topic_20260707-093602_5c75f77654cab0c3": "manual",
"topic_20260707-094645_05255aaa45d5f673": "manual", "topic_20260707-094645_05255aaa45d5f673": "manual",
"topic_20260707-095503_bf879b65e4666516": "auto", "topic_20260707-095503_bf879b65e4666516": "auto",
"topic_20260707-131941_481a70d2be9881dd": "manual",
"topic_20260708-020616_9ab54d98daf65053": "auto", "topic_20260708-020616_9ab54d98daf65053": "auto",
"topic_20260708-093419_71d1fea2e4b5a40f": "auto", "topic_20260708-093419_71d1fea2e4b5a40f": "auto",
"topic_20260708-095428_990617acd4168b40": "auto", "topic_20260708-095428_990617acd4168b40": "auto",

View File

@@ -38,6 +38,8 @@
"topic_20260707-093602_5c75f77654cab0c3": "隐藏单栏和双栏的切换功能把mark…", "topic_20260707-093602_5c75f77654cab0c3": "隐藏单栏和双栏的切换功能把mark…",
"topic_20260707-094645_05255aaa45d5f673": "帮我移除markdownEditor…", "topic_20260707-094645_05255aaa45d5f673": "帮我移除markdownEditor…",
"topic_20260707-095503_bf879b65e4666516": "帮我移除markdownEditor…", "topic_20260707-095503_bf879b65e4666516": "帮我移除markdownEditor…",
"topic_20260707-131941_481a70d2be9881dd": "新的会话",
"topic_20260707-132824_49b096d569ffa3d3": "现在帮我把这个markdownEdi…",
"topic_20260708-020616_9ab54d98daf65053": "帮我分析一下现在的markdownE…", "topic_20260708-020616_9ab54d98daf65053": "帮我分析一下现在的markdownE…",
"topic_20260708-093419_71d1fea2e4b5a40f": "src/components/Dir…", "topic_20260708-093419_71d1fea2e4b5a40f": "src/components/Dir…",
"topic_20260708-095428_990617acd4168b40": "帮我检查一下这个应用在进行放大缩小的…", "topic_20260708-095428_990617acd4168b40": "帮我检查一下这个应用在进行放大缩小的…",
@@ -48,5 +50,5 @@
"topic_20260710-034724_3c6fb29d8d0a4119": "目录树中鼠标悬浮弹框dropmenu…", "topic_20260710-034724_3c6fb29d8d0a4119": "目录树中鼠标悬浮弹框dropmenu…",
"topic_20260710-054543_5e7c427932aecd7b": "现在在markdown的编辑区域中…", "topic_20260710-054543_5e7c427932aecd7b": "现在在markdown的编辑区域中…",
"topic_20260710-063053_353d579222d3b70b": "这个应用里有一个全局搜索的功能,帮我…", "topic_20260710-063053_353d579222d3b70b": "这个应用里有一个全局搜索的功能,帮我…",
"topic_20260710-090003_60717bd90da5ec68": "新的会话" "topic_20260710-090003_60717bd90da5ec68": "当用户在markdownEditor…"
} }

155
package-lock.json generated
View File

@@ -16,6 +16,7 @@
"@codemirror/search": "^6.7.1", "@codemirror/search": "^6.7.1",
"@codemirror/state": "^6.7.1", "@codemirror/state": "^6.7.1",
"@codemirror/view": "^6.43.6", "@codemirror/view": "^6.43.6",
"@fancyapps/ui": "^6.1.14",
"@lezer/highlight": "^1.2.3", "@lezer/highlight": "^1.2.3",
"@lucide/vue": "^1.23.0", "@lucide/vue": "^1.23.0",
"@tauri-apps/api": "^2", "@tauri-apps/api": "^2",
@@ -1460,6 +1461,12 @@
"node": "^20.19.0 || ^22.13.0 || >=24" "node": "^20.19.0 || ^22.13.0 || >=24"
} }
}, },
"node_modules/@fancyapps/ui": {
"version": "6.1.14",
"resolved": "https://registry.npmjs.org/@fancyapps/ui/-/ui-6.1.14.tgz",
"integrity": "sha512-cBV73XXHrc0qMca+PJJ6qay3xweIgsoYse13rGRstgUs0xA5WAv1tgbSzPCIOOjiLs2S2jiVFJxyszgvgyfxMg==",
"license": "SEE LICENSE IN LICENSE.md"
},
"node_modules/@floating-ui/core": { "node_modules/@floating-ui/core": {
"version": "1.8.0", "version": "1.8.0",
"resolved": "https://registry.npmmirror.com/@floating-ui/core/-/core-1.8.0.tgz", "resolved": "https://registry.npmmirror.com/@floating-ui/core/-/core-1.8.0.tgz",
@@ -2031,9 +2038,6 @@
"arm" "arm"
], ],
"dev": true, "dev": true,
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2048,9 +2052,6 @@
"arm" "arm"
], ],
"dev": true, "dev": true,
"libc": [
"musl"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2065,9 +2066,6 @@
"arm64" "arm64"
], ],
"dev": true, "dev": true,
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2082,9 +2080,6 @@
"arm64" "arm64"
], ],
"dev": true, "dev": true,
"libc": [
"musl"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2099,9 +2094,6 @@
"loong64" "loong64"
], ],
"dev": true, "dev": true,
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2116,9 +2108,6 @@
"loong64" "loong64"
], ],
"dev": true, "dev": true,
"libc": [
"musl"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2133,9 +2122,6 @@
"ppc64" "ppc64"
], ],
"dev": true, "dev": true,
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2150,9 +2136,6 @@
"ppc64" "ppc64"
], ],
"dev": true, "dev": true,
"libc": [
"musl"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2167,9 +2150,6 @@
"riscv64" "riscv64"
], ],
"dev": true, "dev": true,
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2184,9 +2164,6 @@
"riscv64" "riscv64"
], ],
"dev": true, "dev": true,
"libc": [
"musl"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2201,9 +2178,6 @@
"s390x" "s390x"
], ],
"dev": true, "dev": true,
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2218,9 +2192,6 @@
"x64" "x64"
], ],
"dev": true, "dev": true,
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2235,9 +2206,6 @@
"x64" "x64"
], ],
"dev": true, "dev": true,
"libc": [
"musl"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2570,9 +2538,6 @@
"arm64" "arm64"
], ],
"dev": true, "dev": true,
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2590,9 +2555,6 @@
"arm64" "arm64"
], ],
"dev": true, "dev": true,
"libc": [
"musl"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2610,9 +2572,6 @@
"x64" "x64"
], ],
"dev": true, "dev": true,
"libc": [
"glibc"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2630,9 +2589,6 @@
"x64" "x64"
], ],
"dev": true, "dev": true,
"libc": [
"musl"
],
"license": "MIT", "license": "MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2672,6 +2628,72 @@
"node": ">=14.0.0" "node": ">=14.0.0"
} }
}, },
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": {
"version": "1.11.1",
"dev": true,
"inBundle": true,
"license": "MIT",
"optional": true,
"dependencies": {
"@emnapi/wasi-threads": "1.2.2",
"tslib": "^2.4.0"
}
},
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": {
"version": "1.11.1",
"dev": true,
"inBundle": true,
"license": "MIT",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": {
"version": "1.2.2",
"dev": true,
"inBundle": true,
"license": "MIT",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": {
"version": "1.1.4",
"dev": true,
"inBundle": true,
"license": "MIT",
"optional": true,
"dependencies": {
"@tybys/wasm-util": "^0.10.1"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/Brooooooklyn"
},
"peerDependencies": {
"@emnapi/core": "^1.7.1",
"@emnapi/runtime": "^1.7.1"
}
},
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": {
"version": "0.10.2",
"dev": true,
"inBundle": true,
"license": "MIT",
"optional": true,
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": {
"version": "2.8.1",
"dev": true,
"inBundle": true,
"license": "0BSD",
"optional": true
},
"node_modules/@tailwindcss/oxide-win32-arm64-msvc": { "node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
"version": "4.3.2", "version": "4.3.2",
"resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.2.tgz", "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.2.tgz",
@@ -2846,9 +2868,6 @@
"arm64" "arm64"
], ],
"dev": true, "dev": true,
"libc": [
"glibc"
],
"license": "Apache-2.0 OR MIT", "license": "Apache-2.0 OR MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2866,9 +2885,6 @@
"arm64" "arm64"
], ],
"dev": true, "dev": true,
"libc": [
"musl"
],
"license": "Apache-2.0 OR MIT", "license": "Apache-2.0 OR MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2886,9 +2902,6 @@
"riscv64" "riscv64"
], ],
"dev": true, "dev": true,
"libc": [
"glibc"
],
"license": "Apache-2.0 OR MIT", "license": "Apache-2.0 OR MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2906,9 +2919,6 @@
"x64" "x64"
], ],
"dev": true, "dev": true,
"libc": [
"glibc"
],
"license": "Apache-2.0 OR MIT", "license": "Apache-2.0 OR MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -2926,9 +2936,6 @@
"x64" "x64"
], ],
"dev": true, "dev": true,
"libc": [
"musl"
],
"license": "Apache-2.0 OR MIT", "license": "Apache-2.0 OR MIT",
"optional": true, "optional": true,
"os": [ "os": [
@@ -5963,9 +5970,6 @@
"arm64" "arm64"
], ],
"dev": true, "dev": true,
"libc": [
"glibc"
],
"license": "MPL-2.0", "license": "MPL-2.0",
"optional": true, "optional": true,
"os": [ "os": [
@@ -5987,9 +5991,6 @@
"arm64" "arm64"
], ],
"dev": true, "dev": true,
"libc": [
"musl"
],
"license": "MPL-2.0", "license": "MPL-2.0",
"optional": true, "optional": true,
"os": [ "os": [
@@ -6011,9 +6012,6 @@
"x64" "x64"
], ],
"dev": true, "dev": true,
"libc": [
"glibc"
],
"license": "MPL-2.0", "license": "MPL-2.0",
"optional": true, "optional": true,
"os": [ "os": [
@@ -6035,9 +6033,6 @@
"x64" "x64"
], ],
"dev": true, "dev": true,
"libc": [
"musl"
],
"license": "MPL-2.0", "license": "MPL-2.0",
"optional": true, "optional": true,
"os": [ "os": [
@@ -6350,7 +6345,7 @@
}, },
"node_modules/mdast-util-newline-to-break": { "node_modules/mdast-util-newline-to-break": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmmirror.com/mdast-util-newline-to-break/-/mdast-util-newline-to-break-2.0.0.tgz", "resolved": "https://registry.npmjs.org/mdast-util-newline-to-break/-/mdast-util-newline-to-break-2.0.0.tgz",
"integrity": "sha512-MbgeFca0hLYIEx/2zGsszCSEJJ1JSCdiY5xQxRcLDDGa8EPvlLPupJ4DSajbMPAnC0je8jfb9TiUATnxxrHUog==", "integrity": "sha512-MbgeFca0hLYIEx/2zGsszCSEJJ1JSCdiY5xQxRcLDDGa8EPvlLPupJ4DSajbMPAnC0je8jfb9TiUATnxxrHUog==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -7940,7 +7935,7 @@
}, },
"node_modules/remark-breaks": { "node_modules/remark-breaks": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmmirror.com/remark-breaks/-/remark-breaks-4.0.0.tgz", "resolved": "https://registry.npmjs.org/remark-breaks/-/remark-breaks-4.0.0.tgz",
"integrity": "sha512-IjEjJOkH4FuJvHZVIW0QCDWxcG96kCq7An/KVH2NfJe6rKZU2AsHeB3OEjPNRxi4QC34Xdx7I2KGYn6IpT7gxQ==", "integrity": "sha512-IjEjJOkH4FuJvHZVIW0QCDWxcG96kCq7An/KVH2NfJe6rKZU2AsHeB3OEjPNRxi4QC34Xdx7I2KGYn6IpT7gxQ==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {

View File

@@ -18,6 +18,7 @@
"@codemirror/search": "^6.7.1", "@codemirror/search": "^6.7.1",
"@codemirror/state": "^6.7.1", "@codemirror/state": "^6.7.1",
"@codemirror/view": "^6.43.6", "@codemirror/view": "^6.43.6",
"@fancyapps/ui": "^6.1.14",
"@lezer/highlight": "^1.2.3", "@lezer/highlight": "^1.2.3",
"@lucide/vue": "^1.23.0", "@lucide/vue": "^1.23.0",
"@tauri-apps/api": "^2", "@tauri-apps/api": "^2",

23
pnpm-lock.yaml generated
View File

@@ -35,6 +35,9 @@ importers:
'@codemirror/view': '@codemirror/view':
specifier: ^6.43.6 specifier: ^6.43.6
version: 6.43.6 version: 6.43.6
'@fancyapps/ui':
specifier: ^6.1.14
version: 6.1.14
'@lezer/highlight': '@lezer/highlight':
specifier: ^1.2.3 specifier: ^1.2.3
version: 1.2.3 version: 1.2.3
@@ -543,6 +546,9 @@ packages:
resolution: {integrity: sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==} resolution: {integrity: sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==}
engines: {node: ^20.19.0 || ^22.13.0 || >=24} engines: {node: ^20.19.0 || ^22.13.0 || >=24}
'@fancyapps/ui@6.1.14':
resolution: {integrity: sha512-cBV73XXHrc0qMca+PJJ6qay3xweIgsoYse13rGRstgUs0xA5WAv1tgbSzPCIOOjiLs2S2jiVFJxyszgvgyfxMg==}
'@floating-ui/core@1.7.5': '@floating-ui/core@1.7.5':
resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==} resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==}
@@ -1841,6 +1847,7 @@ packages:
glob@11.0.0: glob@11.0.0:
resolution: {integrity: sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==} resolution: {integrity: sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==}
engines: {node: 20 || >=22} engines: {node: 20 || >=22}
deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me
hasBin: true hasBin: true
glob@7.2.0: glob@7.2.0:
@@ -2813,10 +2820,6 @@ packages:
resolution: {integrity: sha512-eCPu1qRxPVkl5605OTWF8Wz40b4Mf45NY5LQmVPQ599knfs5QhASUm9GbJ5BDMDOXgrnh0wyEdvzmL//YMlw0A==} resolution: {integrity: sha512-eCPu1qRxPVkl5605OTWF8Wz40b4Mf45NY5LQmVPQ599knfs5QhASUm9GbJ5BDMDOXgrnh0wyEdvzmL//YMlw0A==}
engines: {node: '>=18'} engines: {node: '>=18'}
string-width@4.2.0:
resolution: {integrity: sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==}
engines: {node: '>=8'}
string-width@4.2.3: string-width@4.2.3:
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
engines: {node: '>=8'} engines: {node: '>=8'}
@@ -3698,6 +3701,8 @@ snapshots:
'@eslint/core': 1.2.1 '@eslint/core': 1.2.1
levn: 0.4.1 levn: 0.4.1
'@fancyapps/ui@6.1.14': {}
'@floating-ui/core@1.7.5': '@floating-ui/core@1.7.5':
dependencies: dependencies:
'@floating-ui/utils': 0.2.11 '@floating-ui/utils': 0.2.11
@@ -3749,7 +3754,7 @@ snapshots:
'@isaacs/cliui@8.0.2': '@isaacs/cliui@8.0.2':
dependencies: dependencies:
string-width: 5.1.2 string-width: 5.1.2
string-width-cjs: string-width@4.2.0 string-width-cjs: string-width@4.2.3
strip-ansi: 7.2.0 strip-ansi: 7.2.0
strip-ansi-cjs: strip-ansi@6.0.1 strip-ansi-cjs: strip-ansi@6.0.1
wrap-ansi: 8.1.0 wrap-ansi: 8.1.0
@@ -6186,12 +6191,6 @@ snapshots:
stdin-discarder@0.3.2: {} stdin-discarder@0.3.2: {}
string-width@4.2.0:
dependencies:
emoji-regex: 8.0.0
is-fullwidth-code-point: 3.0.0
strip-ansi: 6.0.1
string-width@4.2.3: string-width@4.2.3:
dependencies: dependencies:
emoji-regex: 8.0.0 emoji-regex: 8.0.0
@@ -6470,7 +6469,7 @@ snapshots:
wrap-ansi@7.0.0: wrap-ansi@7.0.0:
dependencies: dependencies:
ansi-styles: 4.3.0 ansi-styles: 4.3.0
string-width: 4.2.0 string-width: 4.2.3
strip-ansi: 6.0.1 strip-ansi: 6.0.1
wrap-ansi@8.1.0: wrap-ansi@8.1.0:

View File

@@ -21,6 +21,11 @@ interface WorkspaceEntry {
path: string; path: string;
} }
interface TreeExpansionCommand {
id: number;
expanded: boolean;
}
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
@@ -458,6 +463,7 @@ watch(showWorkspaceMenu, (open) => {
watch(() => props.folderPath, () => { watch(() => props.folderPath, () => {
closeWorkspaceMenu(); closeWorkspaceMenu();
treeExpansionCommand.value = null;
}); });
onBeforeUnmount(() => { onBeforeUnmount(() => {
@@ -535,6 +541,16 @@ function handleCreateRootFile() {
}); });
} }
const treeExpansionCommand = ref<TreeExpansionCommand | null>(null);
let treeExpansionCommandId = 0;
function setAllFoldersExpanded(expanded: boolean) {
treeExpansionCommand.value = {
id: ++treeExpansionCommandId,
expanded,
};
}
</script> </script>
@@ -600,6 +616,25 @@ function handleCreateRootFile() {
> >
<i class="ri-file-add-line"></i> <i class="ri-file-add-line"></i>
</button> </button>
<span class="tree-toolbar-separator" aria-hidden="true"></span>
<button
class="tree-toolbar-btn"
type="button"
title="全部展开"
aria-label="全部展开工作目录文件夹"
@click="setAllFoldersExpanded(true)"
>
<i class="ri-expand-vertical-line"></i>
</button>
<button
class="tree-toolbar-btn"
type="button"
title="全部折叠"
aria-label="全部折叠工作目录文件夹"
@click="setAllFoldersExpanded(false)"
>
<i class="ri-collapse-vertical-line"></i>
</button>
</div> </div>
<!-- File tree --> <!-- File tree -->
<div <div
@@ -612,6 +647,7 @@ function handleCreateRootFile() {
:entry="entry" :entry="entry"
:depth="0" :depth="0"
:currentFilePath="currentFilePath" :currentFilePath="currentFilePath"
:expansion-command="treeExpansionCommand"
@toggle-folder="(path: string) => emit('toggleFolder', path)" @toggle-folder="(path: string) => emit('toggleFolder', path)"
@open-file="(path: string) => emit('openFile', path)" @open-file="(path: string) => emit('openFile', path)"
@create-folder="(path: string) => emit('createFolder', path)" @create-folder="(path: string) => emit('createFolder', path)"
@@ -1095,6 +1131,13 @@ function handleCreateRootFile() {
color: var(--app-text); color: var(--app-text);
} }
.tree-toolbar-separator {
width: 1px;
height: 18px;
margin: 0 2px;
background: var(--app-border-strong);
}
.sidebar-search-input { .sidebar-search-input {
border: 1px solid var(--app-border-strong); border: 1px solid var(--app-border-strong);
background: var(--app-hover); background: var(--app-hover);

View File

@@ -3,9 +3,12 @@ import {
computed, computed,
nextTick, nextTick,
onBeforeUnmount, onBeforeUnmount,
onMounted,
ref, ref,
watch, watch,
} from "vue"; } from "vue";
import { Fancybox } from "@fancyapps/ui";
import "@fancyapps/ui/dist/fancybox/fancybox.css";
import { unified, type Plugin, type Transformer } from "unified"; import { unified, type Plugin, type Transformer } from "unified";
import remarkParse from "remark-parse"; import remarkParse from "remark-parse";
import remarkGfm from "remark-gfm"; import remarkGfm from "remark-gfm";
@@ -270,6 +273,7 @@ const isEditingEmptyDocument = computed(
); );
let modeScrollRestoreFrame: number | null = null; let modeScrollRestoreFrame: number | null = null;
let renderRequestId = 0; let renderRequestId = 0;
const previewImageSelector = ".markdown-preview:not(.markdown-mixed) img";
interface ScrollSnapshot { interface ScrollSnapshot {
ratio: number; ratio: number;
@@ -599,7 +603,17 @@ watch(activeDocumentMode, (mode, oldMode) => {
restoreScrollAfterModeChange(scrollSnapshot); restoreScrollAfterModeChange(scrollSnapshot);
}); });
onMounted(() => {
Fancybox.bind(scrollRef.value ?? null, previewImageSelector, {
groupAll: true,
placeFocusBack: true,
theme: "auto",
});
});
onBeforeUnmount(() => { onBeforeUnmount(() => {
Fancybox.unbind(scrollRef.value ?? null, previewImageSelector);
if (modeScrollRestoreFrame !== null) { if (modeScrollRestoreFrame !== null) {
cancelAnimationFrame(modeScrollRestoreFrame); cancelAnimationFrame(modeScrollRestoreFrame);
} }
@@ -1189,6 +1203,10 @@ defineExpose({
margin: 0.5em 0; margin: 0.5em 0;
} }
.markdown-preview:not(.markdown-mixed) img {
cursor: zoom-in;
}
/* ── Mixed mode block editing ────────────────────────────────────── */ /* ── Mixed mode block editing ────────────────────────────────────── */
.markdown-block { .markdown-block {

View File

@@ -142,11 +142,9 @@ const syncTargetDescription = computed(() => {
}); });
const syncFileDetails = computed(() => { const syncFileDetails = computed(() => {
const files = syncFeedback.value?.result?.files || []; const files = syncFeedback.value?.result?.files || [];
return [...files].sort((left, right) => { return files
if (left.action === "skip" && right.action !== "skip") return 1; .filter((file) => file.action !== "skip")
if (left.action !== "skip" && right.action === "skip") return -1; .sort((left, right) => left.path.localeCompare(right.path));
return left.path.localeCompare(right.path);
});
}); });
const menuItems: Array<{ const menuItems: Array<{
@@ -875,7 +873,7 @@ onBeforeUnmount(() => {
<div class="settings-sync-file-section"> <div class="settings-sync-file-section">
<div class="settings-sync-file-heading"> <div class="settings-sync-file-heading">
<strong>文件明细</strong> <strong>变更文件</strong>
<span>{{ syncFileDetails.length }} 个文件</span> <span>{{ syncFileDetails.length }} 个文件</span>
</div> </div>
<div v-if="syncFileDetails.length" class="settings-sync-file-list"> <div v-if="syncFileDetails.length" class="settings-sync-file-list">
@@ -887,7 +885,7 @@ onBeforeUnmount(() => {
<span class="settings-sync-file-size">{{ formatFileSize(file.size) }}</span> <span class="settings-sync-file-size">{{ formatFileSize(file.size) }}</span>
</div> </div>
</div> </div>
<p v-else class="settings-sync-file-empty">工作目录与远端都没有文件</p> <p v-else class="settings-sync-file-empty">本次同步没有文件变更</p>
</div> </div>
</template> </template>
</section> </section>

View File

@@ -15,12 +15,18 @@ interface DirEntry {
children?: DirEntry[]; children?: DirEntry[];
} }
interface TreeExpansionCommand {
id: number;
expanded: boolean;
}
const TREE_CONTEXT_MENU_CLOSE_EVENT = "yurou-tree-context-menu-close"; const TREE_CONTEXT_MENU_CLOSE_EVENT = "yurou-tree-context-menu-close";
const props = defineProps<{ const props = defineProps<{
entry: DirEntry; entry: DirEntry;
depth: number; depth: number;
currentFilePath?: string | null; currentFilePath?: string | null;
expansionCommand?: TreeExpansionCommand | null;
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
@@ -33,6 +39,7 @@ const emit = defineEmits<{
}>(); }>();
const expanded = ref(true); const expanded = ref(true);
const handledExpansionCommandId = ref(0);
const contextMenuOpen = ref(false); const contextMenuOpen = ref(false);
const contextMenuPoint = ref<{ x: number; y: number } | null>(null); const contextMenuPoint = ref<{ x: number; y: number } | null>(null);
const contextMenuStyle = computed(() => { const contextMenuStyle = computed(() => {
@@ -76,12 +83,35 @@ function onContextMenuKeydown(event: KeyboardEvent) {
watch( watch(
() => props.entry.children, () => props.entry.children,
(children, oldChildren) => { (children, oldChildren) => {
if (children !== undefined && oldChildren === undefined) { if (
children !== undefined &&
oldChildren === undefined &&
props.expansionCommand?.expanded !== false
) {
expanded.value = true; expanded.value = true;
} }
}, },
); );
watch(
() => props.expansionCommand,
(command) => {
if (
!command ||
!props.entry.is_dir ||
command.id <= handledExpansionCommandId.value
) return;
handledExpansionCommandId.value = command.id;
expanded.value = command.expanded;
if (command.expanded && props.entry.children === undefined) {
emit("toggleFolder", props.entry.path);
}
},
{ immediate: true },
);
const isActive = computed(() => { const isActive = computed(() => {
return props.currentFilePath != null && props.currentFilePath === props.entry.path; return props.currentFilePath != null && props.currentFilePath === props.entry.path;
}); });
@@ -307,6 +337,7 @@ function onContextMenu(event: MouseEvent) {
:entry="child" :entry="child"
:depth="depth + 1" :depth="depth + 1"
:currentFilePath="currentFilePath" :currentFilePath="currentFilePath"
:expansion-command="expansionCommand"
@toggle-folder="(path: string) => emit('toggleFolder', path)" @toggle-folder="(path: string) => emit('toggleFolder', path)"
@open-file="(path: string) => emit('openFile', path)" @open-file="(path: string) => emit('openFile', path)"
@create-folder="(path: string) => emit('createFolder', path)" @create-folder="(path: string) => emit('createFolder', path)"