我把 paperclip 写出来,最初其实只是因为每天高频的 Ctrl+C / Ctrl+V 让我彻底受够了系统自带剪贴板的短视。复制完一段代码,再复制第二段,第一段就再也找不回来了;好不容易在浏览器里参考了一段配置,想粘贴到终端里,结果中途被另一条内容顶掉。这些问题说大不大,但一天发生十几次,就足以打断节奏。于是我用业余时间手搓了一个命令行剪贴板增强工具,取名叫 paperclip,核心目标就四个:保存剪贴板历史、快速检索、一键回填、敏感片段还能加密。如果你也是那种需要同时处理多段文本、经常在多个窗口之间来回搬内容的人,这篇文章应该能给你不少可以直接照抄的设计思路和实现细节。
我一直认为,剪贴板本质上就是一台超级小型的缓存服务器。系统只给你留一个格子,而我想要的是“一个常驻后台的仓库”。paperclip 到现在已经陪我跑了几十个工作日,文本历史入库超过两万条,回头看去,真正值钱的地方并不在代码本身,而在于围绕“剪贴板数据生命周期”做的那些取舍。接下来我会从设计动机、核心架构、关键实现、实际踩坑,再到打磨体验这几个维度,把这套方案完完整整拆开讲。
1. paperclip 出现的理由:剪贴板从来不是“存一下”这么简单
1.1 原生剪贴板的三个致命痛点
第一个痛点,是只能保存最后一项内容。复制一个网址,再去复制一份报错日志,网址就丢了。如果刚才那个网址是登录后台的地址,你还得重新去翻聊天记录;如果日志是刚从终端复制的,那更麻烦,原始输出早刷刷刷往上滚了。这个场景我想每个开发者和运维朋友都经历过。
第二个痛点,是剪贴板内容在关机重启之后会消失。跨天工作时,前一天晚上整理好的一批文本片段,第二天打开电脑什么都不剩。系统重启、远程桌面断开、甚至某些窗口管理器异常退出,都可能把还没来得及落盘的内容直接清掉。
第三个痛点,是内容安全。把密码、API Key、服务器地址复制到系统剪贴板之后,如果没有及时清理,它就会一直躺在那里。别人用你电脑时,随便打开一个文本框按 Ctrl+V,敏感信息就出去了。普通剪贴板工具只关注让你能多存几条,却很少帮你管理这些内容的生命周期。
1.2 为什么不自接用现成方案
市面上并不缺剪贴板工具。Windows 上有 Ditto、ClipX、Windows 10 自带的剪贴板历史;macOS 上有 Paste、CopyClip;Linux 上也有 CopyQ、clipman 这类不错的方案。但我用了一圈下来,总有几个地方不对味。
Ditto 很强,但它在 Windows 上跑得很好,跨平台时却要额外搭桥;CopyQ 跨平台支持不错,但它的图形界面、插件生态对于我这种重度终端用户来说有点重;macOS 上的 Paste 体验很好,但它不开源,也没法快速集成到我自己那套命令行工作流里。我真正想要的,是一个能装进 alias、能用管道接收内容、能用一条命令搜索调取、还能把历史加密后方便迁移的纯终端工具。市面上的选择要么太重,要么不够开放,要么同步方案被绑死在某个生态里。
于是就有了 paperclip。它不追求做一个花哨的桌面应用,而是把自己定位成“剪贴板领域的命令行瑞士军刀”。它常驻后台,但内存占用很低;它有历史库,但数据格式完全透明,随便导出一个 JSON 就能备份;它有 TUI 选择器,但一个 pick 命令就够,不会逼你打开另一个窗口。
2. paperclip 的整体设计:一条命令解决“存、找、贴、锁”
2.1 命令面设计
我在设计 CLI 时给自己定了一个规矩:所有操作都围绕剪贴板数据的生命周期展开,命令不超过两个词。最终收敛成下面这一组核心操作:
paperclip watch:启动后台监听进程,持续捕获系统剪贴板内容并写入本地历史库。paperclip hist [query]:按时间倒序列出历史片段,支持关键字过滤。paperclip pick:打开一个 TUI 选择器,选中的历史片段会自动写回系统剪贴板并输出到 stdout。paperclip pin <id>:把某条历史固定住,防止被过期策略清理。paperclip search <keyword>:直接输出命中的片段列表,配合 grep 或其他程序使用。paperclip wipe [--all]:按 ID 删除某条记录,或清空全部历史;敏感内容可以一键物理销毁。paperclip export/paperclip import:把历史库导出成加密 JSON,或者从备份中恢复。paperclip serve:启动一个仅监听本机回环地址的 Web UI,方便图形环境下用鼠标操作。
这组命令覆盖了“写入、存储、检索、回填、销毁、备份”六个环节。使用频率最高的两个命令是hist和pick,它们相当于给了 Ctrl+V 一个“历史版本选择器”。我平时在终端里会把pick绑到快捷键上,比如按 F8 就弹出最近的 20 条内容,选中后自动粘贴到当前光标处,整个过程不碰鼠标。
2.2 核心数据结构与数据库设计
paperclip 使用 SQLite 做存储。一开始有人劝我用 JSON 文件加索引,但我坚持用数据库,原因有三:并发安全和崩溃安全、增量追加方便、能直接启用 FTS5 做全文检索。
历史表的核心结构如下:
CREATE TABLE IF NOT EXISTS clip_items ( id INTEGER PRIMARY KEY AUTOINCREMENT, hash TEXT NOT NULL, content BLOB NOT NULL, encoding TEXT NOT NULL DEFAULT 'utf-8', content_type TEXT NOT NULL DEFAULT 'text/plain', app_name TEXT, tags TEXT, pinned INTEGER DEFAULT 0, ttl INTEGER, created_at DATETIME NOT NULL DEFAULT (datetime('now')), last_used_at DATETIME, use_count INTEGER DEFAULT 0 ); CREATE UNIQUE INDEX IF NOT EXISTS idx_clip_items_hash ON clip_items (hash);hash是对内容做 SHA-256 后的十六进制字符串,用来做快速去重。content用 BLOB 而不是 TEXT,是因为剪贴板里不一定只有 UTF-8 文本,Windows 上可能带 BOM,macOS 上可能出现自动补全的换行符。统一存原始字节,读取时再按encoding字段解码,最安全。
ttl字段是给临时片段用的。有些内容比如短信验证码、临时密钥,我只希望它在历史库里活十分钟。监听进程每五分钟蜕一次库,把ttl不为空且超时的记录删掉。
2.3 目录规划与环境变量
paperclip 默认把所有数据放在~/.paperclip/下,但为了便于多配置测试,也支持通过环境变量PAPERCLIP_HOME覆盖。目录内部分层如下:
~/.paperclip/ ├── paperclip.db ├── config.toml ├── logs/ │ └── paperclip.log └── exports/ └── backup_2025-06-01.jsonconfig.toml控制了监听频率、是否启用自动加密、同步目标地址、历史保留条数等参数。比如我当前的配置长这样:
[watch] interval_ms = 500 max_history = 5000 ignore_same_app = true [encrypt] enabled = true key_source = "keyring" [sync] type = "webdav" endpoint = "https://example.com/dav/" username = "paperclip"我强烈建议把密钥放进系统原生的 keyring 或系统钥匙串,而不是写在配置文件里。因为剪贴板工具本来就是用来处理高敏感内容的软件,如果密钥跟着配置走,这台机器被人拷走就等于你的剪贴板全裸奔了。
3. 核心实现:跨平台剪贴板监听、入库与候选粘贴
3.1 剪贴板读取与跨平台差异
读取系统剪贴板这项操作,看起来简单,实际涉及不少平台差异。pyperclip 这类库把跨平台读取包装成统一接口,真正执行时:
- 在 macOS 上会调用
pbpaste命令读取,写回则调用pbcopy; - 在 Windows 上通过
ctypes调用系统 API,读取 CF_UNICODETEXT 格式; - 在 Linux/X11 上会调用
xclip或xsel命令; - 在 Linux/Wayland 上则需要使用
wl-paste,否则读不到内容。
为了不依赖某个单一命令,paperclip 在底层做了一个读取器模块,按平台动态选择命令:
import os import shutil import subprocess from pathlib import Path def read_text_clipboard_linux() -> bytes: if os.environ.get("WAYLAND_DISPLAY"): return subprocess.run( ["wl-paste", "--no-newline", "--type", "text/plain"], capture_output=True, ).stdout if os.environ.get("DISPLAY"): return subprocess.run( ["xclip", "-selection", "clipboard", "-o"], capture_output=True, ).stdout raise RuntimeError("未检测到可用的剪贴板协议") def read_text_clipboard_windows() -> bytes: # 仅在 Windows 平台调用,通过 ctypes 读 CF_UNICODETEXT import ctypes user32 = ctypes.windll.user32 if not user32.OpenClipboard(None): return b"" try: if not user32.IsClipboardFormatAvailable(13): # CF_UNICODETEXT return b"" handle = user32.GetClipboardData(13) if not handle: return b"" # 通过指针读取宽字符串 ... finally: user32.CloseClipboard()这段代码里最容易忽略的一点是:Wayland 下wl-paste --type text/plain只能拿纯文本,如果你复制了富文本或图片,返回结果可能是空的。因此在设计上,所有读取函数都应该容忍空结果,不能因为拿到b""就直接清空历史或写一条空记录。
3.2 监听循环和进程驻留
监听的核心思路很简单:定时轮询剪贴板内容,发现变化就读取并入库。轮询听起来似乎不够优雅,但实测下来,500ms 一次读取对系统资源几乎没有任何影响,因为大多数情况下剪贴板内容根本没变,只需要对比一次内容哈希就继续睡了。
轮询线程的核心逻辑可以简化成下面这段:
import hashlib import threading import time def watch_loop(stop_event: threading.Event): last_hash = b"" while not stop_event.is_set(): raw = read_text_clipboard() current_hash = hashlib.sha256(raw or b"").digest() if raw and current_hash != last_hash: last_hash = current_hash handle_new_item(raw) stop_event.wait(watch_interval / 1000.0)在 Linux 上,如果使用 Wayland,wl-paste还有一个--watch模式,可以在剪贴板变化时触发指定命令。这种方式比轮询更省资源,但它存在一个让我踩了很久的坑:如果用wl-paste --watch直接启动 paperclip,那么整个进程会一直挂在 Wayland 的剪贴板事件循环里,一旦子进程退出或剪贴板内容被外部清空,很容易造成数据丢失。后面我在专门讲坑的章节里会更详细地说。
Windows 和 macOS 下暂时没有特别理想的事件通知接口,所以统一用轮询。如果你对 CPU 占用特别敏感,可以调整interval_ms到 1000ms,代价是粘贴回填时最长要等 1 秒才能被监听到,体感会明显变钝。
3.3 历史入库与去重合并策略
历史入库不能简单 inserts,因为同一个内容在短时间内可能会反复复制。比如你在对比两段代码,来回切换复制,如果每次都新增一条,历史列表会被刷屏,检索价值大打折扣。
我的去重合并策略分三步。第一步,计算内容哈希,如果发现历史库已存在相同哈希,则不再新增记录,而是把对应记录的last_used_at更新时间,use_count加一。第二步,如果内容相似但不等同,则交给“相似度合并”策略:仅对纯文本内容做行数对比,去头去尾处理后相似度超过 95% 就认为是同一片段的不同临时版本,只保留较长的一条。第三步,超过max_history上限时,按“未固定、未加密、最后使用时间最久”的优先级清理。
入库操作的伪代码如下:
def handle_new_item(raw: bytes): content_hash = hashlib.sha256(raw).hexdigest() existing = db.fetch_one( "SELECT id FROM clip_items WHERE hash = ?", (content_hash,) ) if existing: db.execute( "UPDATE clip_items SET last_used_at = datetime('now')," " use_count = use_count + 1 WHERE id = ?", (existing[0],), ) return db.execute( "INSERT INTO clip_items (hash, content, app_name, created_at)" " VALUES (?, ?, ?, datetime('now'))", (content_hash, raw, get_active_app_name()), )get_active_app_name是一个很有意思的功能点。在 Windows 上可以通过前台窗口句柄拿到进程名,Linux 上可以通过xprop或sway的 IPC 拿到 workpace 信息。记录来源应用有一个实际好处:当你搜索一段日志时,即使不记得具体文字,也会记得它是从终端复制的还是从浏览器复制的,多一个过滤维度,检索效率高很多。
3.4 TUI 选择器和快速粘贴回填
paperclip pick是用户最直接感受到效率的操作,所以这里体验一定要做好。我没有从零画 UI,而是选择相对成熟的 Textual 库来写。Textual 的布局能力接近 Web 页面,支持鼠标和键盘同时操作,在 Windows 终端、macOS 终端和 SSH 会话里都能稳定运行。
核心机制非常简单:从数据库读取最近历史,渲染成列表,支持上下方向键选择,支持/进行模糊过滤,回车或 Tab 确认后,将内容写入系统剪贴板,并由调用方负责粘贴。如果你在使用终端时选中文本会自动复制,再用pick回填,就能实现完全不用鼠标的“复制、切换窗口、粘贴、回填”闭环。
对于比较急的临时使用,我还在 setup 配置里加了一个“粘贴即复制”开关:当 paperclip 从历史中回填一段内容到剪贴板后,它会自动把这段内容备份到一个叫recent的临时表中。这样你想撤回刚才的粘贴操作,按 hotkey 就能找回上一个版本,而不是真的让 Ctrl+Z 去处理已经写进应用的文本。
4. 从原型到顺手:我在这几十天里踩过的坑
4.1 Wayland 下 wl-paste 的驻留问题
第一次把 paperclip 跑在 Linux 桌面环境时,我天真地使用了这样一段命令:
wl-paste --watch --type text/plain "paperclip capture"这条命令的含义是:当 Wayland 剪贴板内容变化时,执行一次paperclip capture。逻辑上很完美,但它直接引发了一个诡异的现象:剪贴板被读取一次之后就自动清空,好像有另一个进程一直在抢剪贴板所有权。查了半天才发现,问题出在wl-paste --watch的内部机制上。它并不是“监听变化后触发一次子命令”,而是持续霸占着 Wayland 剪贴板的selection会话,它内部多次调用wl-paste读取数据时,会把原本的选区变成自己的,导致其他应用读取到的内容为空。
这个问题我在本地折腾了两个晚上才彻底定位。最终解决方案是:不用--watch,改用自己实现的轮询来规避协议层面的所有权争夺。如果你确实想让监听更实时,可以把wl-paste的执行改造成一次性调用:无论什么事件,都只执行一次读取,读取完成马上连带退出监测进程,不要长时间占用 selection 会话。
4.2 Windows 剪贴板格式标准化
Windows 的剪贴板不是只存一段字符串,它支持多种数据格式同时存在,比如 CF_UNICODETEXT、CF_HDROP、CF_BITMAP、HTML Format。用pyperclip.paste()读到的只是其中文本格式的一份渲染结果。这带来两个问题。
第一个问题是换行符。从 Windows 应用复制的文本,换行可能是不一样的组合,有\r\n、单个\n,偶尔还会见到\r。如果直接入库,搜索时就会频繁遇到“看着像同一行但匹配不上”的情况。我做了全局的新行规整,入库前统一把\r\n和\r转成\n。
第二个问题是 NUL 字符。某些劣质应用,尤其是老式控件写的程序,往剪贴板里放文本时会带上一串\x00结尾符。直接读出来放入 SQLite,既占空间又影响哈希去重。我在入库前会先清理这些非法字节:
def normalize_text(raw: bytes) -> bytes: raw = raw.replace(b"\r\n", b"\n").replace(b"\r", b"\n") return raw.strip(b"\x00")这种细节如果不处理,平时看不出有什么毛病,但你搜索历史时匹配一个精确字符串,可能怎么查都查不到,因为隐藏的空白字符作怪。
4.3 二进制和图片内容误入文本历史
剪贴板不只是文本的搬运工,它经常承载图片、文件路径、二进制数据。paperclip 定位是文本优先,一开始我没有做内容过滤,结果就是在复制 Excel 表格时,一次次拿到一堆带制表符的单元格文本;复制本地图片时,wl-paste --type text/plain返回空,导致历史里多出一堆空记录。
后来我加了一个非常简单的内容类型判断函数:读取时先尝试按 UTF-8 解码,如果解码失败,直接忽略;如果解码成功但内容里出现大量控制字符,也忽略。只有真正的纯文本或富文本才入库。Windows 平台上,如果检测到剪贴板里有CF_BITMAP或CF_DIB,paperclip 会直接跳过文本入库,避免把图元数据这种二进制垃圾塞进历史。
这里有一个值得说的折中:图片类内容其实也有历史价值,所以我后期在content_type字段里预留了image类型,但图片内容的处理策略是“只存缩略图和哈希,不存原始大图”。毕竟把几百 MB 的截图全部塞进 SQLite 是不现实的,除非你明确开启--with-image选项。
4.4 加密存储和多设备同步的取舍
敏感剪贴板内容自然要加密,但“加密”这两个字也会让工具变得很重。最开始我想用 SQLCipher 把整个数据库加密,但这样一来搜索、历史浏览都需要额外处理,而且 SQLCipher 的编译依赖在 Windows 上并不友好。后来我改成“字段级加密 + 数据库明文索引”的方式。
具体实现是:只有content字段做对称加密,hash、created_at、content_type等索引字段保持明文。这样历史列表的查询性能几乎不受影响,用 SQLite FTS5 全文搜索时也能快速缩小候选集,最后取回具体内容时再解密。加密算法选用了 Fernet,因为它的 API 足够简单,密钥从系统 keyring 读取:
from cryptography.fernet import Fernet import keyring def get_key() -> bytes: key = keyring.get_password("paperclip", "enc_key") if key is None: key = Fernet.generate_key() keyring.set_password("paperclip", "enc_key", key.decode()) return key.encode() if isinstance(key, str) else key def encrypt_content(raw: bytes) -> bytes: return Fernet(get_key()).encrypt(raw) def decrypt_content(encrypted: bytes) -> bytes: return Fernet(get_key()).decrypt(encrypted)在同步方面,我没有选择自己搞一台服务器,而是支持 WebDAV。因为 WebDAV 对极客来说太常见了,坚果云、家里 NAS、服务器只要挂上davfs2都能提供这类服务。paperclip 每天晚上把exports/backup_*.json用 WebDAV PUT 推到配置好的目录,恢复时从远端拉回最近一份备份再本地 import。这种方案的好处是,剪贴板历史不会经过第三方的格式封锁,所有数据都是 open 格式。
我见过一些人追求全平台实时剪贴板同步,但我最终没有做全量实时同步,只做了加密备份同步。原因是剪贴板内容往往带有上下文敏感性,比如正在写的代码片段、还没发出的评论草稿,实时同步到另一台设备有时反而会泄露工作分布。加密备份同步已经能满足绝大多数“换机/丢失”场景。
5. 把 paperclip 从“能用”打磨到“好用”
5.1 性能优化:从无脑轮询到混合事件驱动
轮询模式确实简单,但它有一个隐蔽问题:哪怕剪贴板内容没变,每次轮询都要调用一次外部命令、消耗一次进程创建。在 Windows 上,如果OpenClipboard被频繁调用,某些应用会短暂进入“系统剪贴板忙”状态,导致你从 Excel 复制时第一下没反应。
解决方法是把轮询线程改成混合模式:极短时间窗口内,如果刚发生过一次成功的pick回填操作,则暂停监听 3 秒,把这段静默期留给被聚焦的应用;其他时间保持 500ms 轮询。另外,我增加了一个简单的“零拷贝判断”:监听线程只维护最近一次内容哈希,轮询时先调用轻量级命令判断剪贴板序列号是否变化,只有变化时才真正读取内容。
在 Linux 上,这个思路简单理解为:如果xclip或wl-paste返回的数据大小和哈希跟上次一致,就不需要执行 SQLite 写入操作。实测下来,CPU 占用能降低一半左右,电池环境的续航也会好一些。这些优化做起来都不难,但收益非常直接。
5.2 快速启动、开机自启和日常 alias
一个后台工具如果每次都要手动启动,它的使用频率会断崖式下降。paperclip 的watch模式设计成交互式进程,但生产环境更适合把它注册为系统服务。我在 Linux 上使用了 systemd user service:
[Unit] Description=paperclip clipboard watcher After=graphical-session.target [Service] Type=simple Environment=PAPERCLIP_HOME=%h/.paperclip ExecStart=/usr/local/bin/paperclip watch Restart=on-failure [Install] WantedBy=default.target保存到~/.config/systemd/user/paperclip.service后,执行systemctl --user enable --now paperclip就能开机自启。Windows 下用任务计划程序,登录时运行paperclip watch --minimized,带一个最小化到托盘的参数。macOS 自然是 LaunchAgent,plist 文件模板已经不陌生。
至于 terminal 下的使用,我在 shell rc 文件里加了一组别名,把最常用的检索和回填缩短到两个键:
alias ph='paperclip hist' alias pp='paperclip pick' alias ps='paperclip search'因为pick会同时把选中内容写回剪贴板并且输出到 stdout,我用一个 shell 函数封装,让它满足“选中即粘贴”的习惯:
pcp() { paperclip pick | xclip -selection clipboard }如果你不用 X11,而是 Wayland,把管道目标换成wl-copy即可。
5.3 自动化测试与回归验证
剪贴板工具最大的风险在于不同平台 API 行为不一致,所以我把测试分成三类:单元测试、模拟集成测试、快照回归测试。
单元测试覆盖的是核心去重逻辑、加密逻辑、TTL 清理逻辑。这里有一个很实用的测试思路:不直接访问真实剪贴板,而是提供一套 fake clipboard adapter,在测试里模拟一个“可设置内容、可轮询读取、可模拟读取失败”的假剪贴板。这样单元测试跑起来不用改动系统状态,也不会触发 Wayland 等平台相关的意外行为。
快照回归测试则针对历史库的 schema 变更:每次升级版本,都要用上一个版本生成的数据库文件跑一遍迁移脚本,验证老数据是否兼容。这个测试救了我一次,因为开发过程中我调整过一次content字段的类型,从TEXT改成BLOB,如果没有回归测试,老用户升级后很可能直接库损坏。
测试用例列表大致如下:
- 复制文本 A -> 历史多一条 - 再次复制文本 A -> 历史不新增,使用次数 +1 - 复制密钥内容且 ttl=60s -> sleep 61s 后消失 - 尝试用错误密钥解密 -> 抛出异常且不崩溃 - 导出加密备份 -> 用相同密钥导入 -> 内容一致 - SQLite 数据库被外部删除 -> watch 能自动重建目录6. 如果继续往下做,我会在 paperclip 里加什么
6.1 片段语义化和自动分组
目前 paperclip 的历史检索纯粹靠关键词,虽然能配合 FTS5 做全文搜索,但结果仍然是平的。下一步我想给每条记录自动打上“语义标签”:检测到内容看起来像export const,就加js、code标签;看起来像ssh -p,就加ssh标签;像 URL,就加link标签。这样搜索时除了关键词,还能用type:code、app:terminal这样的过滤语法,检索精度会高很多。
自动分组还有一个好处:在 TUI 里可以按应用或类型折叠,不会出现同样一段日志被不同应用复制十几次的重复场景。虽然已经有哈希去重,但“内容近似但不完全一致”的条目依然会占空间,语义分组后可以更聪明地合并。
6.2 局域网多设备粘贴
当我把 paperclip 装到台式机和笔记本两台设备后,最自然的诉求就是让两台机器的剪贴板打通。用中心化服务器同步我是不太想的,但局域网内可以做得更轻量:一台机器跑paperclip serve开启 HTTP 服务并广播 UDP 组播,另一台机器发现后,实时把剪贴板变化推过去。
这个方向的技术难点不大,但产品设计上需要谨慎:默认应该“推送前询问”,否则你正在笔记本上复制一段密码,台式机上马上就能拿到,这种无感同步在共享办公环境其实有风险。我计划做成“手动拉取”优先:在目标机器上输入一个组合键,主动从当前活跃设备拉取最新剪贴板,而不是由源设备主动广播。
6.3 基于规则的安全擦除
历史记录里总有一些内容真的不希望留下来,比如临时生成的密钥、一次性验证码。现在 paperclip 支持ttl自动过期,但还是太粗。我想加入一个规则引擎,比如“内容匹配password=、token=、api_key=的片段,默认 ttl=5 分钟;内容匹配来自ssh应用的片段,保留时间不超过 24 小时”。规则引擎跑起来后,可以在后台定期扫库,按规则执行加密销毁。
安全擦除还有个细节:普通 delete 只是删除 SQLite 中的引用,数据页本身可能还残留在磁盘文件里。对于高敏感内容,应该使用“清理后重写”:把这些页的位置重新写入随机数据,并执行一次VACUUM。我会在wipe --secure参数里实现这个逻辑,给想要彻底销毁的用户一个选择。
说实话,剪贴板这个领域看起来小,真正做深了发现边界远超想象。paperclip 目前已经解决了我个人的大部分痛点,我也还在继续往它的语义化和安全方向补功能。如果你正好也被系统剪贴板的短视问题困扰,不妨照着上面的思路做一版属于自己的工具。自己写的好处就是,你可以完全按照自己的使用习惯来定义“好用”,而不是被迫适应别人定义的规则。