diff --git a/src/main/services/image-decryption-status-service.ts b/src/main/services/image-decryption-status-service.ts
index 75f92bd..a3ab925 100644
--- a/src/main/services/image-decryption-status-service.ts
+++ b/src/main/services/image-decryption-status-service.ts
@@ -17,7 +17,9 @@ import { isWechatRunning } from './wechat-process-status'
export async function inspectImageDecryptionStatus(
config: ImageKeyConfigResult
): Promise {
- const accountRoot = chat.getCurrentAccountRoot() || config.resourceRoot
+ // 状态面板的"图片资源目录"始终等于当前识别到的微信账号根目录;
+ // 仅在微信未连接时回退到上次配置中的 resourceRoot,避免空白。
+ const accountRoot = chat.getCurrentAccountRoot() || config.resourceRoot || ''
const imageDirectoryFound = hasImageDirectory(accountRoot)
const stickerCacheFound =
fs.existsSync(path.join(accountRoot, 'cache')) ||
@@ -65,7 +67,10 @@ export function testImageDecryption(
.reverse()
.find((message) => message.contentData?.type === 'image')
if (!imageMessage || imageMessage.contentData?.type !== 'image') {
- return failure('NO_IMAGE_MESSAGE', '所选聊天最近没有可测试的图片消息')
+ return failure(
+ 'NO_IMAGE_MESSAGE',
+ '所选聊天最近 300 条消息内没有可测试的图片,请换一个含图片的会话'
+ )
}
const service = new ImageDecryptService(
@@ -74,26 +79,51 @@ export function testImageDecryption(
chat.getChatDb()?.getWcdb4Client()
)
const image = imageMessage.contentData
- let filePath = service.findImageFile(image.md5, image.datName, { allowThumbnail: false })
+ // 测试时优先使用用户在下方"图片资源目录"输入框填写的目录;
+ // 找不到再退回默认 accountDir。
+ const testAccountDir = normalized.resourceRoot || undefined
+ let filePath = service.findImageFile(image.md5, image.datName, {
+ allowThumbnail: false,
+ accountDir: testAccountDir
+ })
if (!filePath)
- filePath = service.findImageFile(image.md5, image.datName, { allowThumbnail: true })
+ filePath = service.findImageFile(image.md5, image.datName, {
+ allowThumbnail: true,
+ accountDir: testAccountDir
+ })
if (!filePath) return failure('FILE_NOT_FOUND', '图片文件不存在')
const data = service.decryptImageToBase64(filePath)
if (!data) {
+ // 三步联动:解密失败 → fileFound/decrypted/readable 都为 false。
return {
- ...failure('DECRYPT_FAILED', '无法解析媒体文件'),
- fileFound: true
+ success: false,
+ code: 'DECRYPT_FAILED',
+ error: '无法解析媒体文件',
+ fileFound: false,
+ decrypted: false,
+ readable: false
}
}
const readable = data.startsWith('data:image/')
+ if (!readable) {
+ // 三步联动:解密成功但字节流不可读 → 前一步打勾(确实找到了 dat),
+ // 但 decrypted/readable 全为 false,让 UI 表达"找到但解析失败"。
+ return {
+ success: false,
+ code: 'DECRYPT_FAILED',
+ error: '图片解密结果不可读取',
+ fileFound: true,
+ decrypted: false,
+ readable: false,
+ isThumbnail: service.isThumbnailFile(filePath)
+ }
+ }
return {
- success: readable,
- code: readable ? undefined : 'DECRYPT_FAILED',
- error: readable ? undefined : '图片解密结果不可读取',
+ success: true,
fileFound: true,
decrypted: true,
- readable,
+ readable: true,
isThumbnail: service.isThumbnailFile(filePath)
}
} catch {
diff --git a/src/main/services/image-key-config-service.ts b/src/main/services/image-key-config-service.ts
index a7bc547..3c54b42 100644
--- a/src/main/services/image-key-config-service.ts
+++ b/src/main/services/image-key-config-service.ts
@@ -92,9 +92,10 @@ export class ImageKeyConfigService {
if (!result.success || !result.entry) {
return { ...this.getEmptyConfig(), error: result.error || '图片密钥保存失败' }
}
+ // 注意:normalized.resourceRoot 不再写回 imageKeyRoot。
+ // 下方的"图片资源目录"输入框仅用于本次手动测试,不再污染状态面板上方显示。
saveSettings({
...settings,
- imageKeyRoot: normalized.resourceRoot,
imageXorKey: '',
imageAesKey: '',
imageKeyFallbackDisabled: false
@@ -106,7 +107,9 @@ export class ImageKeyConfigService {
encryptionAvailable: true,
source: 'secure-storage',
accountId: context.accountId,
- resourceRoot: normalized.resourceRoot,
+ // 返回的 resourceRoot 始终是识别到的默认目录,与状态面板一致;
+ // 不返回 normalized.resourceRoot,避免把用户输入的测试目录当成状态写回。
+ resourceRoot: context.resourceRoot,
xorKey: result.entry.xorKey,
aesKey: result.entry.aesKey,
updatedAt: result.entry.updatedAt
diff --git a/src/renderer/src/assets/main.css b/src/renderer/src/assets/main.css
index 1d62bb4..3efe764 100644
--- a/src/renderer/src/assets/main.css
+++ b/src/renderer/src/assets/main.css
@@ -5791,6 +5791,59 @@ body {
width: 100%;
margin: 2px 0 0;
}
+.image-step-list {
+ list-style: none;
+ margin: 14px 0 0;
+ padding: 12px 14px;
+ border-radius: 8px;
+ border: 1px solid #e5ecea;
+ background: #f7faf9;
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ font-size: 12px;
+}
+.image-step {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+.image-step-icon {
+ width: 18px;
+ height: 18px;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ border-radius: 50%;
+ font-size: 12px;
+ font-weight: 600;
+}
+.image-step-ok .image-step-icon {
+ background: #2e765d;
+ color: #fff;
+}
+.image-step-fail .image-step-icon {
+ background: #a84444;
+ color: #fff;
+}
+.image-step-fail span:last-child {
+ color: #a84444;
+}
+.image-step-pending .image-step-icon {
+ background: #d6dde0;
+ color: #6a7378;
+}
+.image-step-pending span:last-child {
+ color: #98a1a4;
+}
+.image-step-skipped .image-step-icon {
+ background: transparent;
+ color: #b8c0c4;
+}
+.image-step-skipped span:last-child {
+ color: #b8c0c4;
+ text-decoration: line-through;
+}
.image-inline-error {
margin: 14px 0 0;
color: #a84444;
@@ -5829,6 +5882,12 @@ body {
border-radius: 7px;
background: #f1f4f3;
}
+.image-auto-scope-hint {
+ display: inline-block;
+ margin-top: 4px;
+ color: #7d8c8a;
+ font-size: 11px;
+}
.image-auto-unavailable {
padding-top: 20px;
padding-bottom: 20px;
diff --git a/src/renderer/src/features/settings/image-decryption/AutoDetectImageKeySection.tsx b/src/renderer/src/features/settings/image-decryption/AutoDetectImageKeySection.tsx
index c89b7bd..0cc50dd 100644
--- a/src/renderer/src/features/settings/image-decryption/AutoDetectImageKeySection.tsx
+++ b/src/renderer/src/features/settings/image-decryption/AutoDetectImageKeySection.tsx
@@ -45,6 +45,10 @@ export function AutoDetectImageKeySection({
{state.status.platform === 'darwin'
? '扫描本机微信缓存并通过图片模板验证候选密钥。'
: '扫描微信进程内存并通过本地图片模板验证候选密钥。'}
+
+
+ 仅支持 WeChat 4.0,V3 及以下无法解析
+