02 · 方案设计:post_process 钩子 + 智能粘贴
02 · 方案设计:post_process 钩子 + 智能粘贴
目标行为
SenseVoice 转写完成
↓ stdin(完整文本,含中英混排)
voxpaste 工具
├─ 1. 整体写入剪贴板(wl-copy)
├─ 2. hyprctl activewindow -j 解析焦点窗口 → class / XWayland 判定
├─ 3. 按应用族选择粘贴和弦:
│ GUI 应用 → Ctrl+V
│ foot / kitty → Shift+Insert(终端直通粘贴)
│ alacritty/ghostty → Ctrl+Shift+V
│ 其他终端 → Shift+Insert → Ctrl+V
├─ 4. wtype 发送和弦(修饰键+单键,rime 不拦截"组合键",只拦字母)
└─ 5. stdout 返回空串
↓
voxtype 无内容可打(fallback_on_empty = false)
核心洞察:被 rime 劫持的是「字母键击」。Ctrl+V 是修饰键+键的组合,rime 组词逻辑不消费它;应用收到后执行的是自己的粘贴动作——文本作为整体插入,IME 全程不接触内容。
接线方式
~/.config/voxtype/config.toml:
[output.post_process]
command = "~/.local/bin/voxpaste"
timeout_ms = 3000
fallback_on_empty = false # 关键:空输出不回退原文,否则会双重输入
command收到转写文本于 stdin,stdout 输出交给 voxtype 继续处理- 我们在脚本内完成全部注入后返回空串 → voxtype 的打字环节变成 no-op
fallback_on_empty = false必须显式设置:若为 true,空输出可能回退为原文本导致 voxtype 又打一遍(待实测确认语义)
失败语义
| 环节失败 | 行为 |
|---|---|
| wl-copy 不可用 | 返回原文(让 voxtype 打字兜底)+ 桌面通知说明 |
| hyprctl 解析失败 | 按 GUI 默认和弦 Ctrl+V 尝试 |
| 和弦发送失败 | 文本已在剪贴板 → notify-send 提示手动 Ctrl+V |
| 超时(3s) | voxtype 侧按超时策略处理;工具内所有外部调用自带短超时 |
实现要点
- 语言/形态:Go 静态编译单二进制,部署到
~/.local/bin/voxpaste,chezmoi 纳管 - 环境健壮性:daemon 环境通常已有
WAYLAND_DISPLAY,但保留缺失时的/run/user/<uid>回退 - 剪贴板竞争:wl-copy 后 sleep ~150ms 再发和弦(合成器/剪贴板管理器同步延迟)
- XWayland 判定:
hyprctl activewindow -j的输出含xwayland字段;此类窗口需 xsel 写 X11 CLIPBOARD + xdotool 发和弦(P2) - 空输入:stdin 为空直接透传空串,不做任何动作
- 日志:写
~/.local/state/voxpaste/log,便于排查「没贴上」类问题
测试计划
| 用例 | 期望 |
|---|---|
| foot 中说「今天讨论 docker 部署」 | 「今天讨论 docker 部署」顺序正确 |
| Chromium 输入框混排 | 同上 |
| kitty 中混排 | Shift+Insert 路径生效 |
| fcitx5 中文模式 + 英文模式各测一轮 | 结果一致 |
| 剪贴板原有内容 | 被覆盖(可接受,后续可考虑恢复) |
明确不做
- 不修改 voxtype 本体或其配置结构
- 不做通用剪贴板管理(交给 cliphist)
- 不尝试恢复用户剪贴板旧内容(复杂度高、收益低)