diff --git a/.env.example b/.env.example
index 5436001..cd56eb5 100644
--- a/.env.example
+++ b/.env.example
@@ -6,9 +6,12 @@ VITE_DB_KEY=
VITE_AUTO_LOGIN=false
# AI API Configuration (Optional, can be entered in UI)
+# 注意:发布版本不再自动读取以下环境变量。
+# 如果你只是本地开发想用默认值,可以在自己机器的 .env.local 里填,
+# 然后在「设置 → AI 模型」里手动完成"添加供应商"流程。
VITE_DEEPSEEK_API_KEY=
VITE_AI_BASE_URL=https://api.deepseek.com
-VITE_AI_MODEL=deepseek-v4-flash
+VITE_AI_MODEL=deepseek-chat
# Message types to filter out (comma separated). Empty means show all message types.
VITE_FILTER_MSG_TYPES=
diff --git a/README.md b/README.md
index 5832147..85009e6 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@ macOS / Windows 微信聊天记录查看,AI 一键生成群聊总结。
在微信 4.0 数据库解析、解密思路上,项目参考了 [WeFlow](https://github.com/hicccc77/WeFlow) 等开源项目的实现方式;此项目围绕我自己的使用场景做的定制化工具,重点放在本地聊天记录查看、群聊总结和个人工作流集成上。
-> 当前版本:`v2.1.4`。macOS 支持相对稳定;Windows 可能会遇到性能 卡顿问题, 仍在持续兼容不同微信版本与本地目录结构。
+> macOS 支持相对稳定;Windows 已初步支持 但因聊天记录大/机械硬盘等问题 会有所卡顿,仍在持续兼容不同微信版本与本地目录结构。
## ✨ 功能特性
diff --git a/package.json b/package.json
index ca4022e..a9fed0e 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "wechatexplorer",
- "version": "2.1.4",
+ "version": "2.1.5",
"description": "macOS / Windows 微信聊天记录查看与 AI 群聊总结助手",
"keywords": [
"wechat",
diff --git a/src/main/image-decrypt-service.ts b/src/main/image-decrypt-service.ts
index 8ed1aac..b1cab03 100644
--- a/src/main/image-decrypt-service.ts
+++ b/src/main/image-decrypt-service.ts
@@ -10,8 +10,6 @@ const imageDecryptLog = (...args: unknown[]): void => {
}
export class ImageDecryptService {
- private readonly defaultV1AesKey = 'cfcd208495d565ef'
-
private xorKey: number = 0
private aesKey: string = ''
private wcdb4Client: Wcdb4Client | null = null
@@ -80,9 +78,11 @@ export class ImageDecryptService {
findImageFile(
md5?: string,
imageDatName?: string,
- options?: { allowThumbnail?: boolean }
+ options?: { allowThumbnail?: boolean; accountDir?: string }
): string | null {
- const accountDir = this.getAccountDir()
+ // 测试场景下可显式指定根目录;不传则维持原 getAccountDir() 行为
+ const accountDir =
+ options?.accountDir && existsSync(options.accountDir) ? options.accountDir : this.getAccountDir()
if (!accountDir) return null
const allowThumbnail = options?.allowThumbnail !== false
@@ -273,12 +273,9 @@ export class ImageDecryptService {
)
let decrypted: Buffer
- if (version === 1) {
- imageDecryptLog('[ImageDecrypt] using V1 (default AES key)')
- const key = Buffer.from(this.defaultV1AesKey, 'ascii')
- decrypted = this.decryptDatV4(datPath, key)
- } else if (version === 2) {
- imageDecryptLog('[ImageDecrypt] using V2 (user AES key)')
+ if (version === 2) {
+ // WeChat 4.0 标准 dat 头: 07 08 56 32 08 07
+ imageDecryptLog('[ImageDecrypt] using WeChat 4.0 (user AES key)')
if (!this.aesKey) {
imageDecryptLog('[ImageDecrypt] no AES key configured')
return null
@@ -286,7 +283,8 @@ export class ImageDecryptService {
const key = Buffer.from(this.aesKey, 'ascii').slice(0, 16)
decrypted = this.decryptDatV4(datPath, key)
} else {
- imageDecryptLog('[ImageDecrypt] unsupported dat version:', version)
+ // 仅支持 WeChat 4.0:版本不匹配直接返回 null,不做 V3/老版本兜底。
+ imageDecryptLog('[ImageDecrypt] unsupported dat version (WeChat 4.0 only):', version)
return null
}
@@ -356,7 +354,8 @@ export class ImageDecryptService {
}
/**
- * 检测 DAT 文件版本
+ * 检测 DAT 文件版本(仅识别 WeChat 4.0 头 V2)。
+ * 老 V1 头(V3 及以下)直接返回 0,由调用方走"不支持"分支。
*/
private getDatVersion(inputPath: string): number {
const bytes = readFileSync(inputPath)
@@ -365,9 +364,6 @@ export class ImageDecryptService {
}
const signature = bytes.subarray(0, 6)
- if (this.compareBytes(signature, Buffer.from([0x07, 0x08, 0x56, 0x31, 0x08, 0x07]))) {
- return 1
- }
if (this.compareBytes(signature, Buffer.from([0x07, 0x08, 0x56, 0x32, 0x08, 0x07]))) {
return 2
}
diff --git a/src/main/index.ts b/src/main/index.ts
index 2e572f2..74a44d3 100644
--- a/src/main/index.ts
+++ b/src/main/index.ts
@@ -297,7 +297,12 @@ app.whenReady().then(async () => {
const nextWechatDb = await WechatDb.create(key, settings.dbRoot)
const resolvedRoot = nextWechatDb.getWcdb4Client().getAccountRoot()
if (resolvedRoot && resolvedRoot !== settings.dbRoot) {
- saveSettings({ ...settings, dbRoot: resolvedRoot })
+ // 同步更新 imageKeyRoot,避免自动获取图片密钥时扫描到错误目录
+ saveSettings({
+ ...settings,
+ dbRoot: resolvedRoot,
+ imageKeyRoot: resolvedRoot
+ })
}
chat.setChatDb(nextWechatDb)
const wcdb4Client = nextWechatDb.getWcdb4Client()
@@ -411,7 +416,13 @@ app.whenReady().then(async () => {
ipcMain.handle('key:autoGetImageKey', async (event, options?: { save?: boolean }) => {
const settings = loadSettings()
const self = chat.getSelfAccountInfo()
- const accountRoot = settings.imageKeyRoot || self?.accountRoot || settings.dbRoot
+ // 优先级:chat 真实识别到的根 → self.accountRoot → settings.imageKeyRoot → settings.dbRoot
+ // 必须先看 chat.getCurrentAccountRoot(),否则 settings 缓存漂移会导致扫错目录。
+ const accountRoot =
+ chat.getCurrentAccountRoot() ||
+ self?.accountRoot ||
+ settings.imageKeyRoot ||
+ settings.dbRoot
const wxid = self?.wxid
const onStatus = (message: string): void => {
if (!event.sender.isDestroyed()) event.sender.send('key:imageKeyStatus', { message })
@@ -788,6 +799,11 @@ app.whenReady().then(async () => {
ipcMain.handle('db:reopenWithRoot', (_, accountRoot: string) => {
const ok = chat.reopenWithRoot(accountRoot)
if (!ok) return { success: false, error: '数据库未初始化或重新打开失败' }
+ // 同步 imageKeyRoot,避免自动获取扫描到旧目录
+ const settings = loadSettings()
+ if (accountRoot && accountRoot !== settings.imageKeyRoot) {
+ saveSettings({ ...settings, imageKeyRoot: accountRoot })
+ }
const info = chat.getSelfAccountInfo()
return { success: true, info }
})
diff --git a/src/main/key-service-win.ts b/src/main/key-service-win.ts
index e5bd6b4..33b917d 100644
--- a/src/main/key-service-win.ts
+++ b/src/main/key-service-win.ts
@@ -889,7 +889,8 @@ export class KeyService {
const dirName = normalized.split(/[\\/]/).pop() ?? ''
if (dirName.startsWith('wxid_')) pushUnique(dirName)
- const marker = normalized.match(/[\\/]xwechat_files/i) || normalized.match(/[\\/]WeChat Files/i)
+ // 仅支持 WeChat 4.0:路径识别只匹配 xwechat_files
+ const marker = normalized.match(/[\\/]xwechat_files/i)
if (marker) {
const root = normalized.slice(0, marker.index! + marker[0].length)
try {
@@ -934,15 +935,49 @@ export class KeyService {
onProgress?.('正在查找模板文件...')
let result = await this._findTemplateData(userDir, 32)
let { ciphertext, xorKey } = result
-
+ const firstDiag = (this as { _imageTemplateDiag?: {
+ userDir: string; totalTFiles: number; v2Count: number; nonV2Count: number
+ } })._imageTemplateDiag
+
// 如果找不到密钥,尝试扫描更多文件
if (ciphertext && xorKey === null) {
onProgress?.('未找到有效密钥,尝试扫描更多文件...')
result = await this._findTemplateData(userDir, 100)
xorKey = result.xorKey
}
-
- if (!ciphertext) return { success: false, error: '未找到 V2 模板文件,请先在微信中查看几张图片' }
+
+ if (!ciphertext) {
+ // 用诊断信息给具体提示
+ const diag = (this as { _imageTemplateDiag?: {
+ userDir: string; totalTFiles: number; v2Count: number; nonV2Count: number
+ } })._imageTemplateDiag || firstDiag
+ if (!diag || diag.totalTFiles === 0) {
+ return {
+ success: false,
+ error:
+ '在账号目录下未找到任何 _t.dat 图片文件。\n' +
+ `扫描路径:${diag?.userDir || userDir || '(空)'}\n` +
+ '原因:微信没在本地生成缩略图。\n' +
+ '请让用户在微信里打开任意聊天的图片大图(等"原图"按钮可点击),然后再试。'
+ }
+ }
+ if (diag.v2Count === 0 && diag.nonV2Count > 0) {
+ return {
+ success: false,
+ error:
+ `找到 ${diag.totalTFiles} 个 _t.dat,但都不是 V2 头(可能图片尚未解密到本地,或微信版本不同)。\n` +
+ `扫描路径:${diag.userDir}\n` +
+ '请让用户在微信里打开 2-3 张不同的图片大图,等"原图"按钮可点击后再试。'
+ }
+ }
+ return {
+ success: false,
+ error:
+ `找到 ${diag.totalTFiles} 个 _t.dat,其中 ${diag.v2Count} 个是 V2 头,但没有长度 ≥ 0x1F 的有效模板。\n` +
+ `扫描路径:${diag.userDir}\n` +
+ '请在微信中查看更多图片后再试。'
+ }
+ }
if (xorKey === null) return { success: false, error: '未能从模板文件中计算出有效的 XOR 密钥,请确保在微信中查看了多张不同的图片' }
onProgress?.(`XOR 密钥: 0x${xorKey.toString(16).padStart(2, '0')},正在查找微信进程...`)
@@ -1005,6 +1040,8 @@ export class KeyService {
let ciphertext: Buffer | null = null
const tailCounts: Record = {}
+ let v2Count = 0
+ let nonV2Count = 0
for (const f of files.slice(0, 32)) {
try {
@@ -1013,8 +1050,11 @@ export class KeyService {
// 统计末尾两字节用于 XOR 密钥
if (data.subarray(0, 6).equals(V2_MAGIC) && data.length >= 2) {
+ v2Count++
const key = `${data[data.length - 2]}_${data[data.length - 1]}`
tailCounts[key] = (tailCounts[key] ?? 0) + 1
+ } else {
+ nonV2Count++
}
// 提取密文(取第一个有效的)
@@ -1031,6 +1071,15 @@ export class KeyService {
if (count > maxCount) { maxCount = count; const [x, y] = key.split('_').map(Number); const k = x ^ 0xFF; if (k === (y ^ 0xD9)) xorKey = k }
}
+ // 诊断信息:远程排查时让 UI 直接告诉用户搜到了什么
+ const diag = {
+ userDir,
+ totalTFiles: files.length,
+ v2Count,
+ nonV2Count
+ }
+ ;(this as { _imageTemplateDiag?: unknown })._imageTemplateDiag = diag
+
return { ciphertext, xorKey }
}
diff --git a/src/main/services/ai-provider-service.ts b/src/main/services/ai-provider-service.ts
index 5accd30..3335dd9 100644
--- a/src/main/services/ai-provider-service.ts
+++ b/src/main/services/ai-provider-service.ts
@@ -41,7 +41,6 @@ export class AIProviderService {
constructor(private readonly keyStore = new AIProviderKeyStore()) {}
list(): AIProviderListResult {
- this.ensureEnvironmentMigration()
try {
const data = this.readMetadata()
return {
@@ -339,15 +338,10 @@ export class AIProviderService {
}
private ensureEnvironmentMigration(): void {
- const data = this.readMetadata()
- if (data.providers.length) return
- const apiKey = String(import.meta.env.VITE_DEEPSEEK_API_KEY || '').trim()
- if (!apiKey) return
- this.migrateLegacy({
- apiKey,
- baseUrl: String(import.meta.env.VITE_AI_BASE_URL || ''),
- model: String(import.meta.env.VITE_AI_MODEL || '')
- })
+ // 已禁用:内置环境变量 Key 自动迁移策略。
+ // 安全要求:发布给最终用户的版本不应携带任何内置 API Key,
+ // 必须由用户自己在 UI 里手动配置(或者通过自己的 .env.local 注入)。
+ // 保留此方法作为占位,方便后续重新评估。
}
private toSummary(
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/main/services/settings-store.ts b/src/main/services/settings-store.ts
index 5d6a61e..fc135c5 100644
--- a/src/main/services/settings-store.ts
+++ b/src/main/services/settings-store.ts
@@ -4,6 +4,22 @@ import path from 'path'
import os from 'os'
import { discoverWindowsDbRoots } from '../windows-db-root-discovery'
+/**
+ * 把 V3 时代的 "...\\Documents\\WeChat Files" 路径重定向到
+ * "...\\Documents\\xwechat_files"(V4)。如果 xwechat_files 不存在则保留原值。
+ * 仅支持 WeChat 4.0:自动纠正用户机器上残留的旧路径。
+ */
+function redirectLegacyWeChatFilesToXwechat(candidate: string): string {
+ if (!candidate) return candidate
+ const normalized = candidate.replace(/[\\/]+$/, '')
+ const lowered = normalized.toLowerCase()
+ const legacyMarker = `${path.sep}wechat files`
+ if (!lowered.endsWith(legacyMarker)) return candidate
+ const redirected = `${normalized.slice(0, -legacyMarker.length)}${path.sep}xwechat_files`
+ if (fs.existsSync(redirected)) return redirected
+ return candidate
+}
+
export interface AppSettings {
dbRoot: string
apiEnabled: boolean
@@ -25,16 +41,17 @@ function getDefaultDbRoot(): string {
function getDefaultDbRootCandidates(home: string): string[] {
if (process.platform !== 'win32') {
+ // macOS 仅支持 WeChat 4.0 路径(xwechat_files)
return [
path.join(home, 'Library/Containers/com.tencent.xinWeChat/Data/Documents/xwechat_files')
]
}
+ // 仅支持 WeChat 4.0:剔除 V3 时代的 "WeChat Files" 目录,
+ // 只认 xwechat_files(含 Documents\ 和 AppData\Roaming\Tencent\ 两种合法位置)。
const candidates = [
...getWeflowDbPathCandidates(home),
- path.join(home, 'Documents', 'WeChat Files'),
path.join(home, 'Documents', 'xwechat_files'),
- path.join(home, 'WeChat Files'),
path.join(os.homedir(), 'AppData', 'Roaming', 'Tencent', 'xwechat_files')
]
@@ -123,9 +140,19 @@ export function loadSettings(): AppSettings {
if (process.platform === 'win32' && !isUsableDbRoot(cache.dbRoot)) {
cache.dbRoot = getDefaultDbRoot()
}
- if (!cache.imageKeyRoot) {
+ // 同步:imageKeyRoot 必须跟随 dbRoot 更新,
+ // 否则自动获取会扫错目录(旧 bug:状态面板显示 D 盘,自动获取扫 C 盘)。
+ if (!cache.imageKeyRoot || !isUsableDbRoot(cache.imageKeyRoot)) {
cache.imageKeyRoot = cache.dbRoot
}
+ // V4-only 兜底:如果 imageKeyRoot 指向旧的 "WeChat Files"(V3 路径),
+ // 重定向到同一父目录下的 xwechat_files(V4)。
+ if (cache.imageKeyRoot) {
+ cache.imageKeyRoot = redirectLegacyWeChatFilesToXwechat(cache.imageKeyRoot)
+ }
+ if (cache.dbRoot) {
+ cache.dbRoot = redirectLegacyWeChatFilesToXwechat(cache.dbRoot)
+ }
return cache
}
} catch (error) {
diff --git a/src/main/wcdb4-client.ts b/src/main/wcdb4-client.ts
index dcf065f..e10504d 100644
--- a/src/main/wcdb4-client.ts
+++ b/src/main/wcdb4-client.ts
@@ -311,11 +311,10 @@ export class Wcdb4Client {
private static getDefaultRootCandidates(): string[] {
const home = os.homedir()
if (process.platform === 'win32') {
+ // 仅支持 WeChat 4.0:只认 xwechat_files,V3 时代的 "WeChat Files" 不再加入候选
const candidates = [
...Wcdb4Client.getWeflowDbPathCandidates(home),
- path.join(home, 'Documents', 'WeChat Files'),
path.join(home, 'Documents', 'xwechat_files'),
- path.join(home, 'WeChat Files'),
path.join(home, 'AppData', 'Roaming', 'Tencent', 'xwechat_files')
]
candidates.push(...discoverWindowsDbRoots())
diff --git a/src/main/windows-db-root-discovery.ts b/src/main/windows-db-root-discovery.ts
index aa74fb8..5da052e 100644
--- a/src/main/windows-db-root-discovery.ts
+++ b/src/main/windows-db-root-discovery.ts
@@ -1,7 +1,8 @@
import fs from 'fs-extra'
import path from 'path'
-const DB_ROOT_NAMES = new Set(['xwechat_files', 'wechat files'])
+// 仅支持 WeChat 4.0:只扫描 xwechat_files;旧 V3 时代的 "WeChat Files" 不再纳入候选
+const DB_ROOT_NAMES = new Set(['xwechat_files'])
const SKIPPED_DIRECTORY_NAMES = new Set([
'$recycle.bin',
'system volume information',
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 及以下无法解析
+