Skip to content

feat(desktop): add library clipboardWrite PNG bitmap op - #3923

Merged
MagicLizi merged 6 commits into
makecindy:mainfrom
PraiseZhu:cindy/serene-leakey
Sep 5, 2026
Merged

MagicLizi merged 6 commits into
makecindy:mainfrom
PraiseZhu:cindy/serene-leakey

Conversation

@PraiseZhu

Copy link
Copy Markdown
Contributor

这次改了什么

摘要

插件已经在发 library-request op=clipboardWrite,宿主白名单没有这个 op,一律 PATH_INVALID。本 PR 在 Cindy 宿主加上 PNG 位图写口:只收 encoding:'base64' 的 PNG 字节,经 nativeImage.createFromBuffer + clipboard.writeImage 写入系统剪贴板,成功回 {ok:true, op:'clipboardWrite', bytes}。不是 saveAs/reveal,也不复用 media:copy-to-clipboard 的文件引用通道。#3626 是另存为/显示,不是本 PR。

变更类型

  • feat 新功能
  • fix 缺陷修复
  • refactor / perf 重构或性能优化
  • docs / test / chore 文档、测试或工程维护
  • 其他:

范围

  • 关联 Issue / 需求:cindy-host-clipboard-write / 插件 Copy as PNG 宿主写口
  • 本 PR 包含:GHOST_LIBRARY_OPS 增加 clipboardWriteGhostLibrarySlot 分派与校验;getGhostLibrarySlot 注入 PNG 位图写剪贴板;FORGE_GUIDE 协议说明;librarySlot 单测
  • 明确不包含:mivo-canvas-plugin;feat(library): 作品库支持在文件夹中显示与系统另存为 #3626 reveal/saveAs;media:copy-to-clipboard 文件引用粘贴
  • 用户可见变化:声明了 library 的插件可以把 PNG 写到系统剪贴板,供外部应用粘贴位图
  • 是否存在 breaking change:无

UI 变化

  • 引用的设计规范:不涉及:无 renderer / Settings UI / 文案改动,只改 main 侧 library 管子协议与剪贴板接线

怎么验证的

自动验证

git grep -n clipboardWrite apps/desktop/src/shared/ghost.ts
git grep -n clipboardWrite apps/desktop/src/main/cindy-brain/forge.ts
git grep -n clipboardWrite apps/desktop/src/main/cindy-brain/librarySlot.ts
git grep -n writeClipboardPng apps/desktop/src/main/cindy-brain/index.ts
git grep -n clipboardWrite apps/desktop/src/main/cindy-brain/__tests__/librarySlot.test.ts
pnpm --filter desktop exec vitest run src/main/cindy-brain/__tests__/librarySlot.test.ts src/main/cindy-brain/__tests__/librarySaveAsDialogParent.test.ts src/main/cindy-brain/__tests__/forge.test.ts
结果:3 files / 114 tests passed
pnpm --filter desktop run typecheck
结果:通过

手工验证

不涉及本机外部应用粘贴实测(e2e worker 按源码与单测验收)。外部粘贴仍由操作系统与目标应用决定。

未执行的验证

无主壳窗时的实机剪贴板失败路径未在本机 GUI 复现,由单测与接线覆盖。

风险

风险分类

  • 无已知风险
  • SQLite / migration
  • system prompt
  • 协议兼容
  • 权限 / 安全 / 用户数据
  • 存量插件兼容(批准状态 / 指纹 / manifest 校验 / 安装布局 / 包格式)
  • 原生层 / fingerprint / OTA
  • 跨平台差异
  • 其他:

影响与回滚

  • 影响范围:library 管子新增 clipboardWrite;空字节 / 非 base64 / 非 PNG / 超限结构化失败;无主壳窗失败关闭;账号切换后旧会话不得继续写。存量未声明该 op 的插件不受影响。
  • 存量插件影响:无。只扩 op 白名单,不改批准状态 / 指纹 / manifest / 安装布局 / 包格式;已装插件无需重装或重新确认。
  • 回滚 / 降级方式:revert 本 PR。未知 op 会回到 PATH_INVALID

提交前检查

  • 已 review 完整 diff
  • 每个 commit 都带 DCO 签名(git commit -s,见 DCO
  • UI 改动已在「UI 变化」注明引用的设计规范章节(不涉及 UI 则跳过)
  • 未提交凭证、令牌或授权文件
  • 已补充必要文档
  • 已确认测试结果或说明未执行原因

Plugins already send library-request clipboardWrite; the host whitelist
rejected it as PATH_INVALID. Add the op as a PNG bitmap write via
nativeImage + clipboard.writeImage, not saveAs/reveal or the
media:copy-to-clipboard file-reference path.

Signed-off-by: PraiseZhu <zhuzan@xd.com>
@PraiseZhu
PraiseZhu requested a review from a team as a code owner September 4, 2026 13:00
@greptile-apps

greptile-apps Bot commented Sep 4, 2026

Copy link
Copy Markdown

Greptile Summary

此 PR 为桌面端 library 管道新增 PNG 位图剪贴板写入操作,包括共享协议、主进程分派、Electron 剪贴板接线、限流和会话失效检查,以及相应文档与单元测试。

  • 新增 clipboardWrite 请求及成功响应类型。
  • 校验 Base64、PNG 签名、空内容和 16 MiB 上限。
  • 通过 nativeImage.createFromBufferclipboard.writeImage 执行系统剪贴板写入。
  • 当前仍需处理存量 library 插件静默获得新权限,以及无主壳窗口时错误码与协议不一致的问题。

Confidence Score: 2/5

该 PR 暂不适合合并,因为它会向所有既有 library 插件静默授予剪贴板写入能力,并且无主壳窗口时返回的错误码违反新协议。

新操作沿用整体 library 授权,导致既有批准在不更新指纹或用户说明的情况下覆盖新的系统级副作用;同时生产接线把文档规定的 UNSUPPORTED 条件转换成 INTERNAL,破坏插件可依赖的错误处理契约。

Files Needing Attention: apps/desktop/src/shared/ghost.ts、apps/desktop/src/main/cindy-brain/index.ts、apps/desktop/src/main/cindy-brain/librarySlot.ts

Security Review

新操作被加入现有整体 library 权限后,所有已批准的 library 插件都会在不重新确认的情况下获得系统剪贴板写入能力。现有权限投影、批准指纹和用户授权说明都没有逐操作表达该能力,因而形成未披露的权限扩张。

Important Files Changed

Filename Overview
apps/desktop/src/shared/ghost.ts 扩展 library 协议和响应类型,但新增剪贴板能力复用了不含逐操作授权的既有 library 权限。
apps/desktop/src/main/cindy-brain/librarySlot.ts 实现输入校验、限流、会话检查和依赖调用,但会将生产依赖的“不支持”异常统一映射为内部故障。
apps/desktop/src/main/cindy-brain/index.ts 接入 Electron 位图剪贴板,并在没有可见主壳窗口时抛错,导致实际错误码偏离协议。
apps/desktop/src/main/cindy-brain/tests/librarySlot.test.ts 覆盖主要成功、校验、限流和会话失效路径,但未覆盖生产接线的无窗口错误映射。
apps/desktop/src/main/cindy-brain/forge.ts 记录新增协议及失败语义,其中无主壳窗口的 UNSUPPORTED 承诺与实现不一致。

Sequence Diagram

sequenceDiagram
  participant P as 插件
  participant H as Library 管道
  participant S as GhostLibrarySlot
  participant E as Electron 主进程
  participant C as 系统剪贴板
  P->>H: library-request clipboardWrite
  H->>H: 校验整体 library 权限
  H->>S: handleLibraryRequest
  S->>S: 校验 Base64、大小、PNG 签名与限流
  S->>E: writeClipboardPng(bytes)
  E->>E: nativeImage.createFromBuffer
  E->>C: clipboard.writeImage
  C-->>P: "{ok:true, bytes}"
Loading
Prompt To Fix All With AI
### Issue 1
apps/desktop/src/shared/ghost.ts:8251
**既有插件静默扩权**`clipboardWrite` 加入通用 library 操作白名单后,任何已安装且已获 `library: true` 批准的插件都会自动获得写系统剪贴板的能力。library 请求只检查整体权限和操作白名单,而批准指纹与权限说明都不记录具体操作,因此升级后不会触发重新确认,用户也不会得知这项新增权限。应将剪贴板写入设为独立权限,或让这次权限变化使既有批准失效。

**How this was verified:** library 请求只检查整体 `manifest.library` 权限和操作白名单,而批准指纹不记录 library 内的具体操作。

### Issue 2
apps/desktop/src/main/cindy-brain/index.ts:5281-5283
**无窗口错误码不符**

当所有主壳窗口均隐藏或最小化时,这里会抛出普通异常,随后被 `librarySlot` 统一转换为 `INTERNAL`。但新增协议明确承诺无主壳窗口时返回 `UNSUPPORTED`,因此插件无法按文档区分宿主当前不支持写入与真正的内部故障。请让该条件返回 `UNSUPPORTED`,或同步修正协议并覆盖生产接线的这一分支。

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Reviews (1): Last reviewed commit: "feat(desktop): add library clipboardWrit..." | Re-trigger Greptile

Comment thread apps/desktop/src/shared/ghost.ts
Comment thread apps/desktop/src/main/cindy-brain/index.ts
v1 review: Buffer.from accepted bad padding, and isPngBuffer only
checked the 8-byte magic, so truncated PNG still reached
writeClipboardPng. Decode strictly and require a full IHDR header.

Signed-off-by: PraiseZhu <zhuzan@xd.com>
v1 follow-up: a 24-byte signature+IHDR prefix is still truncated.
Accept only a full IHDR chunk plus IEND, and use a real 1x1 PNG fixture.

Signed-off-by: PraiseZhu <zhuzan@xd.com>
includes('IEND') accepted truncated images with the ASCII marker
embedded mid-file. Parse length+type+data+crc, CRC-check IHDR, and
require IEND as the final chunk with no trailing bytes.

Signed-off-by: PraiseZhu <zhuzan@xd.com>
@MagicLizi

Copy link
Copy Markdown
Contributor

@PraiseZhu 👋 这个 PR 还有 2 条 review conversation 没 resolve(apps/desktop/src/shared/ghost.ts / apps/desktop/src/main/cindy-brain/index.ts),auto-review 因此暂时跳过、没法继续审查 / 合并。

如果你已经按评论改完或回应了,请到对应 thread 上点 Resolve conversation;全部 resolve 后,下一轮 auto-review 会自动重新审查这个 PR。

@MagicLizi MagicLizi added touches:core 改动碰到架构核心路径(review-pr 自动维护,仅展示) touches:plugin-base 改动碰到插件基座(review-pr 自动维护,仅展示) touches:product-ui 改动碰到产品 / UI 面(review-pr 自动维护,仅展示) labels Sep 4, 2026
@MagicLizi

Copy link
Copy Markdown
Contributor

这条 PR 改到了插件基座(Library clipboardWrite PNG bitmap),并且是新的 feat 能力,需要维护者确认后才能合并。

确认方式:维护者直接在本 PR 上 Approve;若要改,请 Request Changes,作者改完后再 Approve 即放行。

讨论 issue:#3924

@MagicLizi MagicLizi added the awaiting-discussion 等待维护者讨论(review-pr) label Sep 4, 2026

@MagicLizi MagicLizi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

审查发现 P1 ×1,请在当前 head 上修复后再请求 review。

  • [P1] apps/desktop/src/main/cindy-brain/librarySlot.ts:746 / apps/desktop/src/main/cindy-brain/index.ts:5281 — 协议与实现不一致:FORGE_GUIDE 承诺无主壳窗 / 宿主不能写剪贴板返回 UNSUPPORTED,生产接线在无可见主壳窗时抛 没有可挂靠的宿主窗口clipboardWritecatch 一律折成 INTERNAL。Desktop 生产始终注入 writeClipboardPng,因此真实无窗路径永远是 INTERNAL。issue #3924 第 2 条明确要求统一错误码并补生产注入测试。请把该窗口条件(及可识别的不支持)映射为 UNSUPPORTED,仅真正异常走 INTERNAL;加一条覆盖生产注入函数的测试(无可见主壳窗 → UNSUPPORTED)。

FORGE_GUIDE promises UNSUPPORTED when no main shell window can host
clipboardWrite. Catch the production '没有可挂靠的宿主窗口' error instead
of collapsing it into INTERNAL.

Signed-off-by: PraiseZhu <zhuzan@xd.com>
@PraiseZhu

Copy link
Copy Markdown
Contributor Author

@MagicLizi P1 已在当前 head c7ab4f5c5 修复。

生产接线仍在无可见主壳窗时抛 没有可挂靠的宿主窗口(与 saveAs 同文案)。clipboardWrite 的 catch 现在把该文案映射为 UNSUPPORTED,其它异常才是 INTERNAL。覆盖见 librarySlot.test.ts「生产注入无主壳窗 → UNSUPPORTED」。请再审。

@MagicLizi
MagicLizi dismissed their stale review September 4, 2026 22:20

这些问题已在当前 head 的后续 commit 中修复(格式门/安全门已通过),自动 dismiss 旧的 CHANGES_REQUESTED 以解除合并阻塞。

@MagicLizi MagicLizi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

可以推进

@MagicLizi MagicLizi removed the awaiting-discussion 等待维护者讨论(review-pr) label Sep 5, 2026

@MagicLizi MagicLizi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

审查未通过(P1×1)。Library 新增无对话框的系统剪贴板写入,插件详情能力文案未同步。

  • [P1] apps/desktop/src/shared/ghost.ts:2087 — Host 给 library: true 新增无确认框的系统剪贴板写入(librarySlot.ts clipboardWrite),但详情能力文案 libraryPersistDetail 仍只写「系统文件夹显示 / 另存为」,五语均未改。ghost.ts 已要求详情页讲清 Library 的系统侧副作用;plugin-security-and-authoring.md §3 要求能力在详情如实展示;plugin-library-storage.md Review 清单第 4 条要求 i18n 五语同步。已声明 Library 的存量插件升级后会自动获得覆盖系统剪贴板的能力,用户在插件详情看不到。请五语 libraryPersistDetail 补上 PNG 位图写剪贴板(无确认框、会覆盖当前剪贴板),并同步 ghost.ts 注释。改完跑 pnpm check:i18npnpm check:i18n-glossary

@MagicLizi
MagicLizi dismissed their stale review September 5, 2026 07:49

这些问题已在当前 head 的后续 commit 中修复(格式门/安全门已通过),自动 dismiss 旧的 CHANGES_REQUESTED 以解除合并阻塞。

@MagicLizi MagicLizi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1:Host 给已声明 library: true 的插件新增无确认框的系统剪贴板 PNG 写入(librarySlot.tsclipboardWriteclipboard.writeImage),但用户可见的插件详情能力文案未同步。

ghost.ts 仍走 detailKey: 'libraryPersistDetail';五语 libraryPersistDetail 只写系统文件夹显示与另存为,没有「可把 PNG 写入系统剪贴板、无确认框、会覆盖当前剪贴板」。plugin-security-and-authoring.md §3 与 plugin-library-storage.md Review 清单要求详情如实展示。FORGE_GUIDE 已写该 op,详情页没有。

请五语补上该副作用,并改 ghost.ts 对应注释。不要求拆独立权限或让旧批准失效(那会违反存量兼容红线)。

@MagicLizi
MagicLizi dismissed their stale review September 5, 2026 08:40

这些问题已在当前 head 的后续 commit 中修复(格式门/安全门已通过),自动 dismiss 旧的 CHANGES_REQUESTED 以解除合并阻塞。

Plugin details for library:true only mentioned Finder/Explorer and Save As.
Host already writes PNG bitmaps to the system clipboard without a confirm
dialog, so the five-locale copy and ghost.ts comment now say so.

Signed-off-by: PraiseZhu <zhuzan@xd.com>

@MagicLizi MagicLizi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

独立审查未通过(P1×1)。

  • apps/desktop/src/shared/ghost.ts:2087 — Host 给已声明 library: true 的插件新增无确认框的系统剪贴板 PNG 写入(clipboardWriteclipboard.writeImage),但插件详情仍走 detailKey: 'libraryPersistDetail'。五语文案只写系统文件夹显示与另存为,没有「可把 PNG 写入系统剪贴板、无确认框、会覆盖当前剪贴板」。注释也未覆盖新副作用。按 plugin-security-and-authoring.md §3 与 plugin-library-storage.md Review 清单第 4 条,五语 libraryPersistDetail 必须补上该副作用(不要拆独立权限,以免违反存量兼容红线)。改完跑 pnpm check:i18npnpm check:i18n-glossary

@PraiseZhu

Copy link
Copy Markdown
Contributor Author

@MagicLizi P1 已在当前 head 7123008d92d6557bc974c4170f7cdb962b9b635a 补上。

  • 五语 libraryPersistDetail(en / zh-CN / zh-TW / ja / ko)现在写明:可把 PNG 位图写入系统剪贴板;无确认框;会覆盖当前剪贴板。原有存储区 / Finder-Explorer 显示 / 另存为 / 卸载不删语义都保留。
  • ghost.ts library 能力注释同步了这三点副作用;仍走 detailKey: 'libraryPersistDetail',没有拆独立剪贴板权限,也没有改批准指纹。
  • pnpm check:i18npnpm check:i18n-glossary 已通过。

对应 CHANGES_REQUESTED:#3923 (review) (以及先前同口径的 5120398435)。请再审;本条不 merge。

@MagicLizi
MagicLizi dismissed their stale review September 5, 2026 09:40

这些问题已在当前 head 的后续 commit 中修复(格式门/安全门已通过),自动 dismiss 旧的 CHANGES_REQUESTED 以解除合并阻塞。

@MagicLizi MagicLizi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

代码重审通过:当前 head 未发现 P0/P1。PNG 位图写入系统剪贴板走 library 管子,无主壳窗映射为 UNSUPPORTED,插件详情已写明会覆盖剪贴板。全部 conversation 已 resolve。

@MagicLizi
MagicLizi merged commit 47ef43e into makecindy:main Sep 5, 2026
10 checks passed
@MagicLizi

Copy link
Copy Markdown
Contributor

合了。插件把 PNG 写进系统剪贴板这条管子现在能走通,详情页也说清楚了会覆盖当前剪贴板。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

touches:core 改动碰到架构核心路径(review-pr 自动维护,仅展示) touches:plugin-base 改动碰到插件基座(review-pr 自动维护,仅展示) touches:product-ui 改动碰到产品 / UI 面(review-pr 自动维护,仅展示)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants