Repository navigation
fix: 复制图片改由本机服务端同步写剪贴板(复制后立刻可粘) - #67
Merged
Merged
Conversation
浏览器 navigator.clipboard.write() 写图片是异步投递:resolve 时系统剪贴板里 还没有位图,实测要 100~300ms 后才读得到(大图更久,偶发整次丢弃,页面失活还 会被挂起)。于是「已复制」的提示比「能粘」早一截——用户点完切到微信立刻 Ctrl+V 拿到的是空的,多按几次偶尔成功。 改为由本机后端同步写:POST /api/clipboard/image -> core/clipboard.py 用 Win32 SetClipboardData(CF_DIB),接口返回即代表别的程序已经能粘到(实测 275ms 往返)。 CF_DIB 自己拼字节而不是存 BMP 再切头:40 字节 BITMAPINFOHEADER + 24 位 BGR + 自下而上 + 每行 4 字节对齐,三条写错都不报错、只是图坏掉,因此进单测。
- imageCopy:一条门面收口两条路。有本机绝对路径先走 POST /api/clipboard/image, 没有路径(外链 / blob / data URL)或后端不可用(非 Windows / 旧服务端)再退回 浏览器 API,功能不因后端缺席而消失 - ZoomModal:反馈从「已复制 / 失败」两态改为「复制中… → 已复制 / 失败」,进行中 禁用两颗复制按钮(连点会并发写剪贴板,最后落地哪张不确定) - 新增 imageCopy.test.ts 锁路径决策;WorkflowModals.test.tsx 补「先复制中、后已复制 且进行中禁用」回归
- ARCHITECTURE 7.1 加 POST /api/clipboard/image;8.3 记复制的落地口径; 9.4 新增第 14 条(「已复制」比「能粘」早:现象 / 实测数据 / 规范 / 判据) - core/README 加 clipboard.py 节(导出、依赖、改后必测、两条边界) - frontend/README 更新 imageCopy.ts 与 WorkflowModals 条目、api.ts 依赖 - tests/README 登记两个新测试文件 + imageCopy.test.ts,计数 482 / 1066 - 新增决策记录 2026-11-01-clipboard-write-by-server(含被否方案)
zlZayn
added a commit
that referenced
this pull request
Nov 3, 2026
只写 24 位 CF_DIB 时 alpha 被整条丢掉:透明背景的 PNG 粘出来透明区是一片黑 (透明像素 RGB 通常是 0,转 RGB 只是把 alpha 扔掉)。浏览器旧的复制路径本来就 同时提供 PNG 与 DIB,所以这属于 #67 引入的功能退化。 - 剪贴板同时写两种格式:CF_DIB(24 位,给微信/画图/老软件)+ 注册格式 PNG (源是 PNG 用原字节、其它转 PNG,alpha 完整,给 Photoshop/Office/浏览器) - DIB 的透明像素改为合成到白底(不再是丢弃后留原 RGB 的黑块) - 单测:透明合成白底、PNG 载荷原字节直传且 alpha 完好、非 PNG 源转 PNG 实测(写剪贴板后回读):格式含 PNG;PNG 透明区 alpha=0、不透明区 255; DIB 透明区为白。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
问题
预览大图里点「复制图片」,界面立刻显示「已复制图片」,切到微信
Ctrl+V什么都没出来;多按几次偶尔成功;停在页面上等 1 秒左右再粘就稳定。真机实测(有头 Chrome、
127.0.0.1安全上下文、复刻copyImageToClipboard全链路):navigator.clipboard.write()resolveimage=NONE(还没有位图)write()成功返回,剪贴板仍是上一张(整次静默丢弃)也就是说:浏览器写图片是异步投递,Promise 的 resolve 只代表「请求已受理」。所以「时间」不是关键变量,写有没有真的提交才是——这也解释了「切走再等两秒也不行」(页面失活时提交被挂起)。
改法
分两层,一改一兜底。
① 根因:复制的可信来源改成后端。 应用本来就在本机跑,让 Python 直接写剪贴板:
core/clipboard.py:Win32SetClipboardData(CF_DIB),同步——返回即别的程序已能粘到(实测 275 ms 往返;16 MB 大图 529 ms)POST /api/clipboard/image:文件不存在 404,写不了(非 Windows / 被别的程序占着)503,前端据此静默退回浏览器路径BITMAPINFOHEADER+ 24 位 BGR + 自下而上 + 每行 4 字节对齐。这三条写错都不报错、只是图坏掉,因此全部进单测OpenClipboard被别的程序(剪贴板管理器 / IM)占着时返回 0(err 5)→ 短暂重试,不当失败② UI:不再拿
write()的 resolve 当成功。复制中…→已复制/失败,落地后才说「已复制」验证
482 passed;前端1066 passed;tsc --noEmit/eslint/ruff check/ruff format --check全绿verify_canvas.py:105/105 PASS(隔离实例)image=1254x1254(不再是NONE);真实 UI 里点「生成历史 → 双击图片 → 复制图片」→POST /api/clipboard/image -> 200,剪贴板当场换成新图python scripts/check_docs.py通过(链接 541 处 + 计数一致)影响与边界
pillow运行时依赖(uv.lock已更新,CI 用uv sync --locked).agents/notes/2026-11-01-clipboard-write-by-server.md提交拆分
feat(clipboard)后端能力 + 两个测试文件fix(preview)前端接线 + 三态反馈 + 前端测试docs契约(7.1)、防错条(9.4 第 14 条)、子 README、仪表盘计数、决策记录