Compare commits

...
18 Commits
Author SHA1 Message Date
电摇小子andClaude Opus 4.7 a740a50e79 fix: 使用 pnpm/action-setup@v4 安装 pnpm
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-01 11:04:19 +08:00
电摇小子andClaude Opus 4.7 ffe797b3e4 fix: 使用 corepack 启用 pnpm 替代 npm install -g
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-01 11:01:37 +08:00
电摇小子andClaude Opus 4.7 9cd6061c0f fix: 使用 pnpm@7 匹配项目要求
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-01 10:39:23 +08:00
电摇小子andClaude Opus 4.7 67d86f7834 fix: 修复 release workflow - 改用 npm 安装 pnpm 和 artifacts 上传
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-01 10:38:03 +08:00
电摇小子andClaude Opus 4.7 a6138fd62b chore: 添加 GitHub Actions 自动构建 release workflow
- 推送 v* 标签时自动在 macOS 多架构构建 .dmg
- 自动创建 GitHub Release 并附加构建产物
- 升级版本至 v1.1.0

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-01 10:20:32 +08:00
电摇小子andClaude Opus 4.7 d1763757be docs: 更新 README,添加多模型服务支持说明
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-01 10:12:57 +08:00
电摇小子andClaude Opus 4.7 a4dfa542d1 feat: 支持多模型服务配置 (Key/Base URL/模型名)
- 后端 ai:chat 支持 baseURL 参数,可配置任意 OpenAI 兼容 API
- 前端 UI 增加 Base URL 输入框和模型选择下拉 (DeepSeek/GPT-4o/Claude/Moonshot)
- 配置通过 localStorage 持久化,支持从 .env 文件读取默认値
- 统一错误提示,移除 DeepSeek 硬编码引用

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-01 10:11:25 +08:00
电摇小子 9fb1d14c41 doc: 更新文档 2026-04-08 11:01:03 +08:00
电摇小子 eb60ce4984 chore: 提升版本更新README 2026-01-14 16:28:37 +08:00
电摇小子 061086f0ad docs: 更新README 2026-01-04 16:08:37 +08:00
qingmaoandGitHub d1cb6277f9 Merge pull request #2 from Wxw-Gu/fix-issue-1
Fixes #1: 修复个人聊天界面和导出csv文件不显示名称问题
2026-01-04 15:57:10 +08:00
电摇小子 02baed9bb6 fix: 修复个人聊天界面和导出csv文件不显示名称问题,fix #1 2026-01-04 15:53:26 +08:00
电摇小子 55b792b23c chore: 提升版本 2025-12-31 09:54:59 +08:00
电摇小子 7c3fa728f7 feat: 优化 2025-12-31 09:50:46 +08:00
电摇小子 755d11f71c feat: 增加聊天窗口头像显示切换功能并更新示例图片 2025-12-30 18:06:30 +08:00
电摇小子 eeded5fc46 chore: 移除 LICENSE 2025-12-30 17:29:53 +08:00
电摇小子 ac14864655 docs: 更新README 2025-12-29 10:22:58 +08:00
电摇小子 80316b1db7 docs: 更新README 2025-12-19 17:52:03 +08:00
19 changed files with 249 additions and 161 deletions
+3 -1
View File
@@ -1,8 +1,10 @@
# WeChat Database Key (Optional, can be entered in UI)
VITE_DB_KEY=
# DeepSeek API Key (Optional, can be entered in UI)
# AI API Configuration (Optional, can be entered in UI)
VITE_DEEPSEEK_API_KEY=
VITE_AI_BASE_URL=https://api.deepseek.com
VITE_AI_MODEL=deepseek-chat
# Message types to filter out (comma separated)
VITE_FILTER_MSG_TYPES=分享消息,图片,表情包,视频
+59
View File
@@ -0,0 +1,59 @@
name: Build & Release
on:
push:
tags:
- 'v*'
jobs:
build:
runs-on: macos-latest
strategy:
matrix:
platform: [x64, arm64]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: '7'
- name: Install Dependencies
run: pnpm install
- name: Build
run: pnpm build
- name: Build DMG
run: pnpm run build:mac:${{ matrix.platform }}
- name: Upload DMG
uses: actions/upload-artifact@v4
with:
name: dmg-${{ matrix.platform }}
path: out/mac-*/WechatExplorer-*.dmg
release:
needs: build
runs-on: ubuntu-latest
steps:
- name: Download artifacts
uses: actions/download-artifact@v4
with:
pattern: dmg-*
path: artifacts
- name: Create Release
uses: softprops/action-gh-release@v2
with:
files: artifacts/**/*
generate_release_notes: true
+1
View File
@@ -5,3 +5,4 @@ out
.DS_Store
.eslintcache
*.log*
.omc
+1 -1
View File
@@ -1,6 +1,6 @@
{
"[typescript]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
-21
View File
@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2024 WechatExplorer Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+21 -63
View File
@@ -5,88 +5,46 @@ MAC系统 获取微信聊天记录 AI一键生成群聊总结
## 📸 预览
<div style="display: flex; justify-content: space-between;">
<img src="./public/WX20251215-155324.png" alt="预览图1" width="48%" />
<img src="./public/WX20251215-155524.png" alt="预览图2" width="48%" />
</div>
<img src="./public/example1.png" alt="预览图1" />
<img src="./public/example2.png" alt="预览图2" />
<img src="./public/example3.png" alt="预览图3" />
## [点击这里下载](https://github.com/Wxw-Gu/WechatExplorer/releases)
## 📦 安装说明
1. 下载下方的 `xxx.dmg` 文件。
2. 打开 DMG 并将应用拖动到 **Applications** (应用程序) 文件夹。
3. 如果遇到“无法打开,因为开发者无法验证”的提示,请前往:
`系统设置 -> 隐私与安全性 -> 仍要打开`
## ✨ 功能特性
- **聊天记录查看**: 浏览微信好友和群聊的聊天记录。
- **全局搜索**: 快速搜索聊天内容。
- **AI 智能总结**: 集成 DeepSeek AI,一键总结群聊精华内容,生成话题报告。
- **AI 智能总结**: 支持多模型服务配置(DeepSeek/GPT-4o/Claude/Moonshot,一键总结群聊精华内容,生成话题报告。
- **图片生成**: 将 AI 总结的内容生成精美图片,方便分享。
- **数据导出**: 支持导出聊天记录为 CSV 文件(今日、昨日、近7天或全部)。
- **安全隐私**: 所有数据仅在本地处理,AI 功能需自行配置 API Key。
## 🛠️ 技术栈
- **Electron**: 跨平台桌面应用框架
- **React**: 用于构建用户界面
- **TypeScript**: 提供类型安全
- **Vite**: 极速构建工具
- **Better-SQLite3**: 高性能 SQLite 数据库操作
- **OpenAI SDK**: 用于调用 DeepSeek API
## 🚀 快速开始
### 前置要求
### 使用前置要求
- 微信<=4.0 需要获取自己微信本地数据库的密码, 获取方式参考: [Mac 导出微信聊天记录](https://blog.vcvit.me/2024/08/02/mac-export-wechat-chat-records/)
- 如果无法获取本地数据库密码 则无法使用当前项目
- **微信版本**: 推荐使用微信 **4.0 以下**版本(4.0以上版本不支持数据库解密)
- 微信 <= 4.0: 可正常使用,获取数据库密码方式参考:[Mac 导出微信聊天记录](https://blog.vcvit.me/2024/08/02/mac-export-wechat-chat-records/)
- 微信 >= 4.0: 如需使用,推荐使用 [WeFlow](https://github.com/hicccc77/WeFlow)
- 如无法获取本地数据库密码,则无法使用当前项目
- Node.js (推荐 v16+)
- pnpm (推荐) 或 npm
- pnpm@7
- 解密后的微信数据库文件 (`.db`) 和对应的密钥
- [DeepSeek API Key](https://www.deepseek.com/) (用于 AI 总结功能)
### 安装
```bash
# 克隆项目
git clone https://github.com/your-username/WechatExplorer.git
# 进入目录
cd WechatExplorer
# 安装依赖
pnpm install
```
### 配置
复制 `.env.example` 文件为 `.env`,并填写相关配置(可选,也可以在应用内设置):
```bash
cp .env.example .env
```
### 开发模式运行
```bash
pnpm dev
```
### 打包构建
```bash
# Windows
pnpm build:win
# macOS
pnpm build:mac
# Linux
pnpm build:linux
```
- AI API Key(支持 OpenAI 兼容 API,可选 DeepSeek/GPT/Claude/Moonshot 等)
## ⚠️ 免责声明
本项目仅供学习和研究使用。请勿用于非法用途。开发者不对使用本项目造成的任何后果负责。请遵守相关法律法规和微信使用协议。
## 📄 许可证
[MIT](./LICENSE) License
## 🔗 参考
- [WechatMessageExplorer](https://github.com/svcvit/WechatMessageExplorer)
- [WeFlow](https://github.com/hicccc77/WeFlow)
+16
View File
@@ -0,0 +1,16 @@
#!/bin/bash
# 获取当前版本号(从 package.json 读取)
VERSION=$(jq -r '.version' package.json)
# 格式化为新的 tag(在版本号前加上 "v"
TAG="v${VERSION}"
# 输出当前生成的 tag
echo "生成的 tag: $TAG"
# 创建并推送 tag 到远程
git tag $TAG
git push origin $TAG
echo "Tag $TAG 已经推送到远程仓库!"
+20 -3
View File
@@ -1,8 +1,14 @@
{
"name": "wechatexplorer",
"version": "1.0.0",
"version": "1.1.0",
"description": "mac 版本获取微信聊天记录, AI群聊总结助手",
"keywords": ["wechat", "chat", "mac微信", "微信聊天记录", "AI群聊总结助手"],
"keywords": [
"wechat",
"chat",
"mac微信",
"微信聊天记录",
"AI群聊总结助手"
],
"author": "Qingmao",
"main": "./out/main/index.js",
"scripts": {
@@ -17,7 +23,9 @@
"postinstall": "electron-builder install-app-deps",
"build:unpack": "npm run build && electron-builder --dir",
"build:win": "npm run build && electron-builder --win",
"build:mac": "electron-vite build && electron-builder --mac",
"build:mac:x64": "electron-vite build && electron-builder --mac --x64",
"build:mac:arm64": "electron-vite build && electron-builder --mac --arm64",
"release:mac": "electron-vite build && electron-builder --mac --x64 --arm64 --publish always",
"build:linux": "electron-vite build && electron-builder --linux"
},
"dependencies": {
@@ -55,5 +63,14 @@
"electron",
"esbuild"
]
},
"build": {
"artifactName": "${productName}-${version}-${os}-${arch}.${ext}",
"mac": {
"target": [
"dmg",
"zip"
]
}
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 463 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 378 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 319 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 700 KiB

+9
View File
@@ -0,0 +1,9 @@
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_DEEPSEEK_API_KEY: string
}
interface ImportMeta {
readonly env: ImportMetaEnv
}
+21 -21
View File
@@ -6,6 +6,17 @@ import { WechatDb, Contact, WechatMessage } from './wechat-db'
let wechatDb: WechatDb | null = null
const MSG_TYPE_DICT: Record<number, string> = {
1: '普通文本',
3: '图片',
34: '语音',
43: '视频',
47: '表情包',
48: '位置',
49: '分享消息',
10000: '系统消息'
}
function createWindow(): void {
// 创建浏览器窗口
const mainWindow = new BrowserWindow({
@@ -117,17 +128,6 @@ app.whenReady().then(() => {
const groupMembers = wechatDb.getAllGroupMembers()
return rawMessages.map((msg: WechatMessage) => {
const typeDict: Record<number, string> = {
1: '普通文本',
3: '图片',
34: '语音',
43: '视频',
47: '表情包',
48: '位置',
49: '分享消息',
10000: '系统消息'
}
const msgType = parseInt(msg.messageType)
const createTime = parseInt(msg.msgCreateTime)
const date = new Date(createTime * 1000)
@@ -161,14 +161,12 @@ app.whenReady().then(() => {
return {
id: msg.mesLocalID || Math.random().toString(),
from: msg.mesDes === '1' ? 'user' : 'assistant', // 1 通常是接收到的,0 是发送的?需要验证。Swift 说:[1: "user", 0: "assistant"]
type: typeDict[msgType] || msg.messageType,
from: msg.mesDes === 1 ? 'user' : 'assistant', // 1 通常是接收到的,0 是发送的?需要验证。Swift 说:[1: "user", 0: "assistant"]
type: MSG_TYPE_DICT[msgType] || msg.messageType,
datetime: date.toLocaleString('zh-CN', { hour12: false }),
content: content,
isSender: msg.mesDes === '0',
img: img,
name: name
// 实际上让我们坚持我们观察到的:1=用户(其他人), 0=助手(我)
}
})
})
@@ -183,22 +181,24 @@ app.whenReady().then(() => {
async (
_,
messages: { role: string; content: string }[],
options?: { apiKey?: string; model?: string }
options?: { apiKey?: string; model?: string; baseURL?: string }
) => {
// @ts-ignore: vite env
const apiKey = options?.apiKey || import.meta.env.VITE_DEEPSEEK_API_KEY
const model = options?.model || 'deepseek-chat'
const model = options?.model || import.meta.env.VITE_AI_MODEL || 'deepseek-chat'
const baseURL =
options?.baseURL || import.meta.env.VITE_AI_BASE_URL || 'https://api.deepseek.com'
if (!apiKey) {
return { success: false, error: '未配置 DeepSeek API Key' }
return { success: false, error: '未配置 API Key' }
}
// 动态导入以避免如果未安装或初始类型缺失的问题
const { OpenAI } = await import('openai')
const openai = new OpenAI({
baseURL: 'https://api.deepseek.com',
apiKey: apiKey
baseURL,
apiKey
})
try {
const completion = await openai.chat.completions.create({
@@ -208,7 +208,7 @@ app.whenReady().then(() => {
})
return { success: true, data: completion.choices[0].message.content }
} catch (error: unknown) {
console.error('DeepSeek API Error:', error)
console.error('AI API Error:', error)
const errorMessage = error instanceof Error ? error.message : 'Unknown error'
return { success: false, error: errorMessage }
}
+5 -25
View File
@@ -13,7 +13,7 @@ export interface UserContact {
export interface WechatMessage {
mesLocalID: string
mesDes: string
mesDes: number
messageType: string
msgCreateTime: string
msgContent: string
@@ -62,7 +62,6 @@ export class WechatDb {
}
private connectDb(dbPath: string): Database | null {
// 1. 路径预检查
const targetPath = path.resolve(dbPath)
if (!fs.existsSync(targetPath)) {
console.error(`❌ 错误:数据库文件不存在于路径:${targetPath}`)
@@ -70,45 +69,26 @@ export class WechatDb {
}
try {
// console.log(`🔌 正在尝试连接至:${path.basename(targetPath)}`)
// console.log('路径是', targetPath)
// 2. 初始化数据库句柄
// 关键:使用 better-sqlite3-multiple-ciphers 的构造函数
// verbose 选项有助于调试 Pragma 的执行情况
const db = new DatabaseConstructor(targetPath, {
// verbose: console.log
})
// 此时 db 实例已创建,但因为未提供密钥,任何数据读取都会失败。
// 必须立即配置加密参数。
// 3. 配置加密方案 (Cipher Scheme)
// 必须显式切换到 'sqlcipher',因为该库默认可能使用 'chacha20'
// 配置加密方案 (Cipher Scheme)
db.pragma("cipher='sqlcipher'")
// 4. 配置页大小 (Page Size)
// macOS 版 WeChat 使用 1024 字节。这必须在设置密钥前(或紧随其后)生效。
db.pragma('cipher_page_size = 1024')
// 5. 配置兼容性与算法细节
// 显式指定 SQLCipher 3 的行为
db.pragma('legacy=3')
db.pragma('kdf_iter = 64000')
db.pragma('cipher_hmac_algorithm = HMAC_SHA1')
db.pragma('cipher_kdf_algorithm = PBKDF2_HMAC_SHA1')
// 6. 应用密钥
// 使用正确的 hex 语法: key = "x'HEXSTRING'"
const processedKey = this.processRawKey(this.rawKey)
// console.log(`🔑 应用密钥 (前6位): ${processedKey.substring(0, 6)}...`)
db.pragma(`key = "x'${processedKey}'"`)
// 7. 验证连接
// 如果参数错误,这里将抛出 "file is not a database" 或 "HMAC check failed"
// 验证连接
db.prepare('SELECT count(*) as count FROM sqlite_master').get()
// console.log(`✅ 连接成功!当前数据库 ${path.basename(targetPath)} 包含 ${row.count} 张表。`)
return db
} catch (error) {
console.error(`❌ 连接失败:`, error)
@@ -241,7 +221,7 @@ export class WechatDb {
public getGroupMember(wxid: string): GroupMemberInfo | null {
if (!this.correctUserId) return null
// 1. 检查缓存
// 检查缓存
if (this.groupMemberCache.has(wxid)) {
return this.groupMemberCache.get(wxid) || null
}
@@ -261,7 +241,7 @@ export class WechatDb {
db.close()
// 2. 写入缓存
// 写入缓存
this.groupMemberCache.set(wxid, result || null)
return result || null
+90 -24
View File
@@ -28,6 +28,7 @@ const systemPrompt = `你是一个中文的群聊总结的助手,你可以为
2. 使用中文冒号
3. 无需大标题
4. 开始给出本群讨论风格的整体评价,例如活跃、太水、太黄、太暴力、话题不集中、无聊诸如此类
5. 每个话题详细写出参与者
最后总结下今日最活跃的前五个发言者`
@@ -38,9 +39,11 @@ const ChatWindow: React.FC<ChatWindowProps> = ({
onRefresh,
onRefreshData
}) => {
const isGroupChat = contact?.type === 'group' || contact?.m_nsUsrName?.endsWith('@chatroom')
const messagesEndRef = useRef<HTMLDivElement>(null)
const imageContainerRef = useRef<HTMLDivElement>(null)
const [generatedImage, setGeneratedImage] = useState<string | null>(null)
const [showAvatar, setShowAvatar] = useState(false)
const [colWidths, setColWidths] = useState([150, 100, 180, 400])
const [resizingColIndex, setResizingColIndex] = useState<number | null>(null)
@@ -49,11 +52,16 @@ const ChatWindow: React.FC<ChatWindowProps> = ({
// AI Settings
const [showSettingsModal, setShowSettingsModal] = useState(false)
const [apiKey, setApiKey] = useState(() => localStorage.getItem('deepseek_api_key') || '')
const [model, setModel] = useState('deepseek-chat')
const [apiKey, setApiKey] = useState(() => localStorage.getItem('ai_api_key') || '')
const [baseURL, setBaseURL] = useState(
() => localStorage.getItem('ai_base_url') || 'https://api.deepseek.com'
)
const [model, setModel] = useState(() => localStorage.getItem('ai_model') || 'deepseek-chat')
const handleSaveSettings = (): void => {
localStorage.setItem('deepseek_api_key', apiKey)
localStorage.setItem('ai_api_key', apiKey)
localStorage.setItem('ai_base_url', baseURL)
localStorage.setItem('ai_model', model)
setShowSettingsModal(false)
AIChat()
}
@@ -113,11 +121,16 @@ const ChatWindow: React.FC<ChatWindowProps> = ({
// 昨天
const startOfYesterday = startOfDay - 86400000
return parsed >= startOfYesterday && parsed < startOfDay
} else {
} else if (days === 7) {
// 过去 7 天
const startOf7DaysAgo = startOfDay - 7 * 86400000
return parsed >= startOf7DaysAgo
} else if (days === 30) {
// 过去 30 天
const startOf30DaysAgo = startOfDay - 30 * 86400000
return parsed >= startOf30DaysAgo
}
return true
})
}
@@ -125,7 +138,15 @@ const ChatWindow: React.FC<ChatWindowProps> = ({
const csvContent = [
headers.join(','),
...filtered.map((m) => {
const content = m.content.replace(/"/g, '""').replace(/\n/g, ' ')
let prefix = ''
if (isGroupChat) {
prefix = m.name ? `${m.name}: ` : ''
} else {
const name = m.from === 'user' ? contact?.m_nsNickName || '未知' : '我'
prefix = `${name}: `
}
const fullContent = `${prefix}${m.content}`
const content = fullContent.replace(/"/g, '""').replace(/\n/g, ' ')
return `"${m.from}","${m.type}","${m.datetime}","${content}"`
})
].join('\n')
@@ -148,16 +169,12 @@ const ChatWindow: React.FC<ChatWindowProps> = ({
alert('当前没有消息可供总结')
return
}
const filteredMessages = messages.filter((msg) => {
delete msg.img
// @ts-ignore 暂时去除
delete msg.from
// @ts-ignore 暂时去除
delete msg.id
// @ts-ignore 暂时去除
delete msg.isSender
return !'分享消息,图片,表情包,视频'.split(',').includes(msg.type)
})
const filteredMessages = messages
.filter((msg) => !'分享消息,图片,表情包,视频'.split(',').includes(msg.type))
.map((msg) => {
const { img, id, isSender, ...rest } = msg
return rest
})
const recentMessages = filteredMessages
.map((msg) => {
return `${msg.datetime} ${msg.from}: ${msg.content}`
@@ -174,7 +191,7 @@ const ChatWindow: React.FC<ChatWindowProps> = ({
{ role: 'system', content: systemPrompt },
{ role: 'user', content: prompt }
],
{ apiKey, model }
{ apiKey, model, baseURL }
)
if (result.success && result.data) {
@@ -292,14 +309,34 @@ const ChatWindow: React.FC<ChatWindowProps> = ({
{msg.datetime}
</td>
<td style={{ wordBreak: 'break-all', display: 'flex', alignItems: 'center' }}>
<div style={{ display: 'flex', flexDirection: 'column', marginRight: 12 }}>
<span>{msg.name}</span>
{msg?.img && (
<img style={{ width: '50px', height: '50px' }} src={msg?.img}></img>
{showAvatar && msg?.img && (
<img style={{ width: '40px', height: '40px' }} src={msg?.img}></img>
)}
<div
style={{
justifyContent: 'center',
alignItems: 'center',
display: !showAvatar ? 'flex' : 'block'
}}
>
{(msg.name || contact.m_nsNickName) && (
<div style={{ display: 'flex', fontSize: 18 }}>
{isGroupChat ? msg.name : msg.from === 'user' ? contact.m_nsNickName : '我'}
<span>{isGroupChat ? (msg.name ? ':' : '') : ':'} </span>
</div>
)}
<div
style={{
fontSize: 18,
background: '#fff',
margin: 4,
padding: 4,
borderRadius: '4px'
}}
>
{msg.content}
</div>
</div>
{/* <span style={{ marginRight: '24px' }}>:</span> */}
<div>{msg.content}</div>
</td>
</tr>
))}
@@ -325,6 +362,17 @@ const ChatWindow: React.FC<ChatWindowProps> = ({
</div>
<div className="chat-toolbar">
<label
style={{ marginRight: '10px', display: 'flex', alignItems: 'center', cursor: 'pointer' }}
>
<input
type="checkbox"
checked={showAvatar}
onChange={(e) => setShowAvatar(e.target.checked)}
style={{ marginRight: '5px' }}
/>
</label>
<button className="toolbar-btn" onClick={onRefresh}>
🔄
</button>
@@ -343,6 +391,9 @@ const ChatWindow: React.FC<ChatWindowProps> = ({
<button className="toolbar-btn" onClick={() => handleExport(7)}>
📅 7
</button>
<button className="toolbar-btn" onClick={() => handleExport(30)}>
📅 30
</button>
<button className="toolbar-btn" onClick={() => setShowSettingsModal(true)}>
🤖 AI总结群聊
</button>
@@ -436,22 +487,37 @@ const ChatWindow: React.FC<ChatWindowProps> = ({
<div className="modal-content" onClick={(e) => e.stopPropagation()}>
<h3>AI </h3>
<div className="form-group" style={{ marginBottom: '15px' }}>
<label style={{ display: 'block', marginBottom: '5px' }}>:</label>
<label style={{ display: 'block', marginBottom: '5px' }}>:</label>
<select
value={model}
onChange={(e) => setModel(e.target.value)}
style={{ width: '100%', padding: '8px' }}
>
<option value="deepseek-chat">DeepSeek Chat</option>
<option value="gpt-4o">GPT-4o</option>
<option value="gpt-4o-mini">GPT-4o Mini</option>
<option value="gpt-4-turbo">GPT-4 Turbo</option>
<option value="claude-3-5-sonnet-20240620">Claude 3.5 Sonnet</option>
<option value="moonshot-v1-8k">Moonshot V1</option>
</select>
</div>
<div className="form-group" style={{ marginBottom: '15px' }}>
<label style={{ display: 'block', marginBottom: '5px' }}>Base URL:</label>
<input
type="text"
value={baseURL}
onChange={(e) => setBaseURL(e.target.value)}
placeholder="https://api.deepseek.com"
style={{ width: '95%', padding: '8px' }}
/>
</div>
<div className="form-group" style={{ marginBottom: '20px' }}>
<label style={{ display: 'block', marginBottom: '5px' }}>API Key:</label>
<input
type="password"
value={apiKey}
onChange={(e) => setApiKey(e.target.value)}
placeholder="Enter your DeepSeek API Key"
placeholder="Enter your API Key"
style={{ width: '95%', padding: '8px' }}
/>
</div>
+2 -1
View File
@@ -1,4 +1,5 @@
{
"files": [],
"references": [{ "path": "./tsconfig.node.json" }, { "path": "./tsconfig.web.json" }]
"references": [{ "path": "./tsconfig.node.json" }, { "path": "./tsconfig.web.json" }],
"include": ["src", "vite-env.d.ts"]
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"extends": "@electron-toolkit/tsconfig/tsconfig.node.json",
"include": ["electron.vite.config.*", "src/main/**/*", "src/preload/**/*", "src/shared/**/*"],
"include": ["electron.vite.config.*", "src/main/**/*", "src/preload/**/*", "src/shared/**/*", "src/env.d.ts"],
"compilerOptions": {
"composite": true,
"types": ["electron-vite/node"]