2b63bf7d0b2fcd61f552b2b0cc356c5809ed6316
## 图片直显 + 右键复制 以前图片消息基本只能看到 "[图片] xxx.png" 的文件卡片, 而且右键没有"复制"。 根因有两层: 1. `asImage` 从来没被设成 true —— 用"发送文件"选中的图片是当**普通文件**发的, 对方只能看到文件卡片。 2. 拖拽/粘贴的图走 `sendBuffer`, 只在 <200KB 时才带 `thumbDataUrl`, 大图没有缩略图 -> 渲染层 fallback 成文件卡片。 改法: - 缩略图随消息体走 (`body.thumbDataUrl`): **发送方生成一次, 对方直接拿来显示**, 对端不用再解码, 老版本对端也看得到图。 - 缩略图用 Electron 的 `nativeImage` 生成 (`src/main/image.ts`), 不引 sharp/jimp: 宽度上限 480, **PNG 优先 (保透明 —— 截图转 JPEG 会变黑底)**, PNG 超 60KB 才退 JPEG, 然后按"降质量 -> 缩尺寸"的阶梯找一档够小的。 阈值/编码选择/选图源抽到 `src/main/image-core.ts` (纯逻辑, 不 import electron, 可单测)。 源文件 >20MB 不生成 —— nativeImage 解码会把整张图铺成 RGBA 内存。 - 拖拽/选中的图片改走 `sendFile(asImage: true)` (有真实路径, 流式上传), 只有粘贴的图片才走 `sendBuffer` (整图 base64 进 WS 帧, 大图撑爆带宽)。 - 右键"复制图片" -> `image:copy` IPC -> `clipboard.writeImage`, 写的是**原图** (本地文件优先, 退回 dataBase64), 不是气泡里那个缩略图。 - 缩略图缺失时 (老消息/老版本对端) 渲染层退到 `image:thumb` 现场生成, 失败才显示文件卡片。点开看大图用 `image:dataUrl` 读本地原图。 ## 修未读角标不清 用户正看着某个会话, dock/任务栏角标的数字却不消。 根因: `App.vue` 的 `message:received` 里先判 `if (!savedPath) return` (等文件落地再算未读), 而**文字消息没有 savedPath** —— 正好在"清未读"之前就 return 了。 文件/图片消息因为第二帧带 savedPath 才碰巧能清掉, 所以只有文字消息暴露这个 bug。 改法: - 决策抽成纯函数 `src/renderer/src/utils/unread.ts` 的 `unreadActionForIncoming()` (`clear` / `wait` / `count`): **先判 isActive -> 清未读 + 回已读回执, 再判 savedPath**。 - 侧边栏**重复点同一个设备**时 activePeerId 不变, watch 不触发 -> 显式清一次。 - 主进程 `device:delete` 清了 unread 但忘了 `updateBadge()` -> 角标残留, 补上。 ## 验证 - npm run typecheck ✅ - npm run build ✅ - npm run selftest ✅ 76 通过 / 0 失败 (新增 I1-I8 图片 + U1-U4 角标) - npm run selftest:sync ✅ 全部通过 - npm run selftest:image ✅ 真 Electron 下验 nativeImage (新增命令) - npm run package:linux ✅ 新 deb - npm run selftest:package ✅ 4/4 真机 (CDP 驱动真实窗口) 验证过: 图片气泡内联显示 (640x420, fallback 0 个), 右键菜单含"复制图片", 点下去系统剪贴板里是 **640x420 原图** (不是缩略图)。
LocalNetMsg
局域网即时通讯工具,飞书/微信聊天风格。基于 Electron + Vue 3 + TypeScript。
功能
- 局域网自动发现 (UDP 广播)
- 1 对 1 私聊,消息持久化 (SQLite)
- 富文本消息:Markdown / 代码高亮 / @提醒 / 表情
- 图片消息:粘贴板、拖拽、本地选择
- 文件传输:任意类型,大文件流式上传
- 飞书式三栏 UI,消息气泡、时间分组、发送状态
- 系统托盘最小化,后台新消息系统级通知
- 接收文件按
设置目录/YYYY-MM/自动归档
开发
环境要求: Node.js >= 20
npm install
npm run dev # 启动 Electron + 热更新
第一次启动时:
- 默认接收目录:
~/Documents/LocalNetMsg - 默认设备名: 操作系统主机名
- 两个端口自动分配 (UDP 发现 47800 / WebSocket 47900 / 文件 47901)
要测试多设备,在同一局域网的不同机器上各跑一个实例即可。
项目结构
src/
├── main/ Electron 主进程
│ ├── index.ts 入口: 窗口、托盘、IPC
│ ├── discovery.ts UDP 局域网发现
│ ├── chat-server WebSocket 服务端
│ ├── chat-client WebSocket 客户端
│ ├── file-server HTTP 文件服务
│ ├── db.ts SQLite 持久化
│ ├── settings.ts electron-store
│ ├── notify.ts 系统通知
│ └── protocol.ts 消息协议定义
├── preload/ contextBridge 桥接
└── renderer/ Vue 3 渲染层
├── src/
│ ├── views/ ChatView, SettingsView
│ ├── components/ Sidebar, MessageList, MessageItem, MessageInput
│ ├── stores/ Pinia
│ ├── api/ IPC 桥接
│ └── utils/ markdown, format
└── index.html
配置
~/AppData/Roaming/local-net-msg/config.json (electron-store):
deviceName: 本机在网络中的显示名downloadDir: 接收文件保存目录notifications: 是否启用系统通知
端口
| 用途 | 端口 | 协议 |
|---|---|---|
| 局域网发现 | 47800 | UDP |
| 聊天 | 47900 | WebSocket |
| 文件传输 | 47901 | HTTP |
被占用时自动顺延。
Description
Languages
TypeScript
70.8%
Vue
23.6%
JavaScript
5.3%
CSS
0.2%