- AI 应用
- 桌面应用
- 移动开发
- 知识管理
【免费下载链接】note-gen
Capture first. Organize later. A local-first Markdown app that turns scattered records into clear notes with AI.
本篇文章以 NoteGen 仓库内 PATCH.md 为骨架,完整讲解该项目如何通过 vendoring(本地镜像)+[patch.crates-io]机制,把上游tauri-plugin-clipboard2.1.11 中image依赖的默认特性全部关闭,只保留 PNG、JPEG、GIF、WebP、BMP、ICO 六种剪贴板图片格式,从而剪掉 AVIF/AV1、EXR、TIFF 等用不到的编解码器,同时保证剪贴板命令、权限标识符和前端 JavaScript API 完全不变。读完本文,你将掌握 Cargo patch 的落地姿势、特性叠加(additive features)的坑,以及 NoteGen 剪贴板监听链路(从 Rust 命令到前端 React 组件)的完整证据链。
背景:NoteGen 为什么需要裁剪剪贴板图片格式
NoteGen 是一款本地优先的 Markdown 笔记应用,主打 "Capture first. Organize later."——随手复制的内容要能被快速捕获进笔记。在桌面端,这一能力依赖剪贴板监听:用户在任意软件里复制文本或截图,NoteGen 自动把内容接入聊天流。
前端实现位于 clipboard-listener.tsx:它通过hasImage()/hasText()探测剪贴板内容类型,图片走readImageBase64(),随后用writeFile把图片落盘到 AppData 下的clipboard/目录,再以insert()插入一条type: 'clipboard'的系统消息;文本则去重后同样插入。这正是插件中read_image_base64、read_text、clear等命令的调用方。
而这一切命令,都由上游插件tauri-plugin-clipboard(CrossCopy 维护、MIT 协议)提供。该插件在官方 Tauri API 只支持纯文本的基础上,扩展出 HTML、RTF、文件与图片的读写,以及剪贴板变更监听。但上游插件对图片处理依赖的imagecrate 默认开启了全部编解码特性,导致 NoteGen 的依赖树里混进大量用不到的格式支持。于是项目决定在src-tauri/vendor/下 vendoring 该插件,做一次"精准瘦身"。
裁剪的核心:vendor 目录里的 Cargo.toml 特性改动
项目把上游tauri-plugin-clipboard2.1.11 完整镜像到 src-tauri/vendor/tauri-plugin-clipboard/,包含src/(lib.rs、commands.rs、desktop.rs、mobile.rs 等)、permissions/(自动生成的权限定义)、Cargo.toml、README.md与LICENSE。
据 PATCH.md 所述,与 crates.io 发布版相比,唯一的功能性改动就在 Cargo.toml:image依赖关闭默认特性,只启用 PNG、JPEG、GIF、WebP、BMP、ICO。对照 vendor 版 Cargo.toml 可以验证:
[dependencies.image] version = "0.25.1" default-features = false features = ["png", "jpeg", "gif", "webp", "bmp", "ico"]这一行声明的效果是:移除上游插件对 AVIF/AV1、EXR、TIFF 以及其他未使用编解码器的依赖请求。注意措辞是"移除请求"而非"从系统里删除"——因为 Cargo 特性是加性的,最终生效的特性集取决于整个依赖图中所有节点声明特性的并集,这一点下文会展开。
改动的收益是实实在在的:imagecrate 每开启一个格式特性,都会引入对应的原生编解码依赖(如 AVIF 的 ravif/avif-serialize、EXR 的 zune-exr、TIFF 的 zune-tiff 等),这些都会拉长编译时间、增大二进制体积。NoteGen 剪贴板场景只需要常见位图格式,裁剪后image的依赖面显著收窄。
落地机制:根 Cargo.toml 的[patch.crates-io]
光在 vendor 目录里改 Cargo.toml 还不够,必须让整个构建真正用上这份本地源码。NoteGen 的做法是在根 src-tauri/Cargo.toml 里通过 Cargo 的 patch 机制把 crates.io 上的同名 crate 替换为本地路径:
[patch.crates-io] # Upstream enables every image codec; keep clipboard formats used by NoteGen. tauri-plugin-clipboard = { path = "vendor/tauri-plugin-clipboard" }配合桌面端平台依赖声明(src-tauri/Cargo.toml):
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies] tauri-plugin-clipboard = "2"要点说明:
[patch.crates-io]只接受相同包名的替换:被 patch 的 crate 版本号会被忽略,实际使用本地 path 的源码;但版本约束("2")仍用于解析依赖关系,因此 vendor 目录内保留version = "2.1.11"以维持语义版本匹配。- patch 只影响直接/间接依赖的解析结果,不影响
tauri-plugin-clipboard-api(npm 前端包)的版本对齐要求——前端与 Rust 端仍需保持 API 兼容。 - 若某天上游插件支持配置图片格式,可以直接删掉这段 patch,恢复从 crates.io 拉取。
插件随后在 src-tauri/src/main.rs 注册进 Tauri 构建器:
tauri::Builder::default() .plugin(tauri_plugin_clipboard::init()) .plugin(tauri_plugin_clipboard_manager::init()) // ...加性特性的深坑:为什么"关闭默认特性"不等于"只有这些格式"
Cargo 的 feature 是加性(additive)的:同一 crate 出现在依赖图中多次时,各节点声明的 features 会取并集。也就是说,default-features = false只能保证本项目这一条边不开默认特性;只要依赖图里还有其他 crate 对image声明了default(或某个具体格式特性),全部编解码器都会被重新拉回来。
因此 PATCH.md 特别强调:其他依赖也必须避免启用image/default。维护者在升级上游插件或新增依赖时,需要用cargo tree检查image节点的实际特性集合,例如:
cargo tree -i image --edges features在 src-tauri 目录下执行,确认输出中没有任何路径向image传入default特性。
平台特例:macOS 上的 TIFF 必须保留
这里有一个刻意的例外:在 macOS 上,clipboard-rs与arboard仍然会为原生剪贴板互操作启用 TIFF。这是因为 macOS 的NSPasteboard在复制图片时通常会携带 TIFF 表示,剪贴板后端需要 TIFF 编解码才能完成与系统剪贴板的读写转换。因此这个平台需求要原样保留,不要试图把 TIFF 从 macOS 的依赖路径上也剪掉,否则会导致 macOS 上剪贴板图片读写失败。
功能不变性验证:命令、权限标识符与 JS API 一个不少
裁剪只动了image依赖,没有触碰插件的对外契约。这一点可以从仓库内三处交叉验证:
1. Rust 命令列表原样保留
vendor 版 lib.rs 的init()中,invoke_handler注册的命令与上游完全一致,共 25 个:
- 探测:
has_text、has_image、has_html、has_rtf、has_files、available_types - 读取:
read_text、read_files、read_files_uris、read_html、read_image_base64、read_image_binary、read_rtf - 写入:
write_text、write_html、write_html_and_text、write_rtf、write_image_binary、write_image_base64、write_files_uris、write_files - 监听:
start_monitor、stop_monitor、is_monitor_running - 其他:
clear
2. 权限标识符未变
permissions/目录下的自动生成权限定义保持不变,例如 read_image_base64.toml 仍提供clipboard:allow-read-image-base64与clipboard:deny-read-image-base64两个标识符;完整的权限清单见 reference.md。这意味着应用原有的 capability 配置(如桌面端 default.json)无需任何改动。
3. JavaScript API 行为一致
前端继续通过tauri-plugin-clipboard-api调用,无需改动。以图片读取为例,前端拿到的是 base64 字符串:
import { hasImage, readImageBase64, readText, clear } from "tauri-plugin-clipboard-api"; const hasImageRes = await hasImage(); if (hasImageRes) { const image = await readImageBase64(); const uint8Array = Uint8Array.from(atob(image), c => c.charCodeAt(0)); // 写入 AppData/clipboard/<uuid>.png await clear(); }这与 clipboard-listener.tsx 的实际实现一致。
源码级原理:图片在 Rust 侧是如何被读写的
理解裁剪影响,最好再看一眼桌面端实现 desktop.rs:
- 读取图片:
read_image_binary()通过clipboard_rs的get_image()拿到系统剪贴板图片,再调用to_png()统一编码为 PNG 字节流;read_image_base64()在此基础上做标准 base64 编码。因此读取路径只需要 PNG 编码能力即可。 - 写入图片:
write_image_binary()通过RustImageData::from_bytes()解析传入的原始字节。这里的解码就依赖image开启的格式特性——这就是裁剪后保留 JPEG/GIF/WebP/BMP/ICO 解码的原因:用户可以复制任意这几种格式的图片字节并写入剪贴板。 - 类型探测:
available_types()返回text / html / rtf / image / files五元布尔结构(见 desktop.rs),has_image()等命令在此基础上逐个封装。 - 文件 URI 平台差异:
write_files_uris()在 Linux/macOS 要求file://前缀、Windows 则禁止该前缀(commands.rs),与图片格式裁剪无耦合。
从该结构可以推断:裁剪后image仍然完整覆盖"读取统一输出 PNG + 写入兼容常见位图"的两种能力需求,功能面没有缺口。
升级维护守则:更新上游时该做什么
PATCH.md 最后给出两条维护纪律,值得任何使用 vendor + patch 方案的项目借鉴:
- 更新上游插件时,必须保留这一依赖改动:每次把新的上游版本同步进
src-tauri/vendor/tauri-plugin-clipboard/后,都要重新检查[dependencies.image]是否仍是default-features = false且 features 为["png", "jpeg", "gif", "webp", "bmp", "ico"]。上游发布新版本时可能会重新引入默认特性或改依赖名,diff 时要特别盯住这一节。 - 上游支持配置图片格式后,可以移除 patch:如果未来
tauri-plugin-clipboard官方支持通过 feature 开关配置图片编解码器,届时直接删除根 Cargo.toml 的[patch.crates-io]段与 vendor 目录,改用官方特性即可,无需再维护本地镜像。
小结
NoteGen 通过"vendor 镜像 +[patch.crates-io]+ 精确特性白名单"三件套,在不改动任何命令、权限与前端 API 的前提下,把tauri-plugin-clipboard的image依赖从"全格式"收敛为"六种常用位图格式",并显式记录了两条红线:其他依赖不得开启image/default,macOS 平台的 TIFF 互操作能力必须保留。这套做法同时给出了可执行的验证路径(cargo tree -i image --edges features)和明确的升级回退策略,是 Tauri 桌面应用中裁剪第三方插件依赖面的一个完整、可复现的工程样例。
相关文件索引:
- 裁剪说明:PATCH.md
- 改动本体:vendor Cargo.toml
- Patch 挂载点:根 Cargo.toml
- 插件注册:main.rs
- 前端调用方:clipboard-listener.tsx
- Rust 命令集:lib.rs、commands.rs
- 桌面端实现:desktop.rs
- 权限清单:reference.md
- AI 应用
- 桌面应用
- 移动开发
- 知识管理
【免费下载链接】note-gen
Capture first. Organize later. A local-first Markdown app that turns scattered records into clear notes with AI.
相关推荐
FontForge 的 X 选择与剪贴板:PRIMARY/CLIPBOARD 机制与剪贴板数据格式深度解析
FontForge 的 X 选择与剪贴板:PRIMARY/CLIPBOARD 机制与剪贴板数据格式深度解析 本篇技术指南聚焦 FontForge 在 X Win
桌面应用图形学如何快速找到历史剪贴板中的特定内容:Clipboard剪贴板内容搜索终极指南
如何快速找到历史剪贴板中的特定内容:Clipboard剪贴板内容搜索终极指南 Clipboard是一款智能剪贴板管理工具,能够帮助用户高效管理和搜索历史剪贴板内
CLI开发工具Tauri剪贴板插件:HTML读取功能深度解析
Tauri剪贴板插件:HTML读取功能深度解析 你是否在开发桌面应用时遇到过剪贴板HTML内容读取的兼容性问题?Tauri的clipboard manager插
桌面应用跨平台移动开发
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考