fix(chat): support image paste from clipboard on Linux/Tauri - #248
fix(chat): support image paste from clipboard on Linux/Tauri#248tingyuxuan123 wants to merge 1 commit into
Conversation
在 Linux (X11/Wayland) + Tauri webview 环境下,微信等截图工具 将图片数据写入系统剪贴板后,浏览器的 DataTransfer API 无法 正确暴露图片数据:clipboardData.files 为空,且 DataTransferItem.getAsFile() 返回 null。 当同步 paste 事件无法提取文件时,降级使用异步 Clipboard API (navigator.clipboard.read()) 读取原始图片 blob。
xintaofei
left a comment
There was a problem hiding this comment.
自动化评审小结
核心思路是对的:同步 paste 事件拿不到文件时,降级用异步 Clipboard API 读取图片 blob;对 Linux/Tauri WebKitGTK 下 DataTransfer.files 为空、getAsFile() 返回 null 的根因判断也准确。👍
不过当前实现有两处会影响所有用户(含非 Linux、含浏览器/服务器模式)的回归,建议修订后再合并:
- 🔴 降级触发条件过宽:
filesFromClipboard()对纯文本粘贴也返回[](message-input.tsx:230),于是每一次普通文本粘贴都会落入降级分支并执行navigator.clipboard.read()——一次完整、权限/隐私敏感的全剪贴板读取。MessageInput 桌面与浏览器模式共用,浏览器下这会带来无谓开销甚至权限弹窗,而该 bug 仅存在于 Tauri WebKitGTK webview。 - 🔴 破坏既有「文本优先」设计:
filesFromClipboard()(:226-235)对 text+image 混合剪贴板(电子表格单元格等)刻意返回[]以保留文本粘贴;降级路径忽略了这点,会在所有平台把这类内容劫持成图片附件。 - 🟡
await之后的event.preventDefault()是空操作:异步读取 resolve 时浏览器默认 paste 已执行,preventDefault 不再生效。 - 🟡 缺少回归测试(项目用 vitest):建议覆盖「纯截图降级成功 / 纯文本不触发降级 / 混合内容保持文本优先」。
行内已给出具体定位与一个可直接套用的修改建议(需从 @/lib/transport/detect 引入 detectEnvironment)。整体评价:方向正确,收敛触发条件 + 补测试后即可达到生产可用级别。
| // Fallback for Linux/Tauri webview where DataTransfer API doesn't | ||
| // expose screenshot images (e.g. WeChat screenshot). The async | ||
| // Clipboard API can read raw image blobs when the sync paste event | ||
| // yields nothing. | ||
| if (navigator.clipboard?.read) { |
There was a problem hiding this comment.
🔴 降级被过度触发 + 破坏「文本优先」设计
filesFromClipboard() 对纯文本也返回 [](见 :230,检测到 text/plain 即 return []),所以每一次普通文本粘贴都会进到这里执行 navigator.clipboard.read()——一次完整、权限/隐私敏感的全剪贴板读取。MessageInput 在桌面与浏览器/服务器模式共用,浏览器模式下这是无谓开销、部分浏览器还会弹权限;而本 bug 只存在于 Tauri WebKitGTK webview。
此外这段忽略了 filesFromClipboard()(:226-235)刻意的「文本优先」设计:复制电子表格单元格这类 text+image 混合内容时原本返回 [] 是为了让文本正常粘贴,这里会在所有平台把它劫持成图片附件;又因为是 await 之后才 preventDefault(),文本其实已经粘进输入框,最终是「文本 + 图片」双重动作。
建议把降级限定到 Tauri,并在同步剪贴板已有文本时直接返回(下方 suggestion,需额外 import { detectEnvironment } from "@/lib/transport/detect";若确认 web 端 Linux 也受影响可再放宽该判断):
| // Fallback for Linux/Tauri webview where DataTransfer API doesn't | |
| // expose screenshot images (e.g. WeChat screenshot). The async | |
| // Clipboard API can read raw image blobs when the sync paste event | |
| // yields nothing. | |
| if (navigator.clipboard?.read) { | |
| // Fallback for Linux/Tauri webview where DataTransfer API doesn't | |
| // expose screenshot images (e.g. WeChat screenshot). The async | |
| // Clipboard API can read raw image blobs when the sync paste event | |
| // yields nothing. Gate to Tauri and bail when the clipboard already | |
| // carries text, to preserve filesFromClipboard()'s text-first | |
| // behaviour and avoid a full clipboard read on every plain-text paste. | |
| if (detectEnvironment() !== "tauri") return | |
| if (event.clipboardData?.getData("text/plain").trim()) return | |
| if (navigator.clipboard?.read) { |
| } | ||
| } | ||
| if (imageFiles.length > 0) { | ||
| event.preventDefault() |
There was a problem hiding this comment.
🟡 这里的 event.preventDefault() 实际是空操作:它在 await navigator.clipboard.read() 之后执行,此时浏览器默认 paste 行为已经发生。对纯截图(剪贴板无文本)无害,因为没有要阻止的默认行为;但在「文本优先」被绕过的混合内容场景下,文本已经粘进输入框,preventDefault 无法回收。属于上面问题 1/2 修复后会自然消解的点,这里一并标注。
|
0.15.1版本已支持 |
问题
在 Linux 桌面版(Tauri + WebKitGTK webview)中,使用微信等截图工具复制的图片粘贴到聊天输入框无反应。
根因
Linux (X11/Wayland) 下,截图工具将图片写入系统剪贴板后,浏览器的
DataTransferAPI 无法暴露图片数据:clipboardData.files为空DataTransferItem.getAsFile()返回null修复
当同步粘贴事件无法提取文件时,降级使用异步 Clipboard API (
navigator.clipboard.read()) 读取原始图片 blob。测试