用一句话概括这个项目:把「打开豆包 → 粘贴内容 → 打提示词 → 发送」压缩成一次 URL 访问。兼容 Raycast、Alfred 和浏览器书签。这篇文章复盘从需求到开源的完整过程,重点讲讲适配豆包新版输入框时踩的那个大坑。

一、起点:一条 URL 的需求

平时最常用的 AI 调用方式是什么?对很多人来说是「打开豆包 → 粘贴文本 → 手动打字提示词 → 发送」,至少四步。高频使用的时候,这个四步流程的成本就被放大了。

于是需求一句话就讲完了:「浏览器打开一个链接,豆包页面自己把提示词填好、发出去。」这样我就能在 Raycast、Alfred、书签里一键唤起,像用快捷键一样用 AI。

二、URL 协议设计:type + content

协议越简单越好,只留两个参数:

  • type:提示词类型(translate / polish / summarize / explain),不传默认 translate

  • content:要处理的内容,URL 编码后传入

示例:

https://www.doubao.com/chat/?type=translate&content=Hello%20world

模板里的 {content} 占位符会被替换成传入内容。比如 translate 的提示词是「请将以下英文翻译成中文,仅输出译文:{content}」。内置四个模板开箱即用,也能在页面上可视化增删改。

三、脚本怎么工作

一个油猴脚本,只匹配 doubao.com/chat*,核心就三步:

  1. 抢 URL:在 document-start 就捕获原始 query 参数。因为豆包是 SPA,加载后会重定向到 /chat/{id} 把 query 清掉,晚一步参数就没了。

  2. 写输入框:等输入框出现,把组装好的提示词写进去。

  3. 点发送:发送成功后清理 URL 参数,避免刷新重复发送。

四、最大的坑:豆包新版输入框

这是整个项目最有意思的部分。豆包旧版是 textarea,用原生 value setter + input 事件就能写入。但新版换成了 TipTap / ProseMirror 富文本编辑器,规则完全变了:

  • 必须聚焦后用 document.execCommand('insertText') 写入,触发真实的 beforeinput / input 事件,TipTap 才能同步内部 state

  • TipTap 的 MutationObserver 是异步把 DOM 同步到 state 的,写完后要等 state 真正同步、发送按钮变为可用,再点击

  • 发送是否成功,用「输入框是否被清空」来验证,而不是想当然

发送做了三级回退:点击发送按钮 → 直接调用 React fiber 里的 onSubmit → 模拟 Enter 键。实测新版 UI 下,点击发送按钮最可靠。

教训:网页自动化最怕「看起来成功了」。用页面自身的状态(state 同步、输入框清空)做验收,而不是只管操作。

五、配置面板:不写代码也能加模板

右下角一个 ⚙ 悬浮按钮,打开配置弹窗:

  • 模板管理:新增 / 编辑 / 删除 / 重置,键名只允许字母数字下划线

  • 运行设置:自动发送、发送后清理 URL、显示配置按钮、发送延迟

所有配置存油猴本地存储,改完即时生效,还实时生成每个模板的可复制调用 URL。

六、开源上线

  • GitHub 仓库:README 写好安装、用法、Raycast / Alfred 集成示例

  • 介绍网站:单文件 index.html,浅色主题,配了一个「URL → 自动对话」的循环演示动画

  • 自动部署:GitHub Actions + Pages,push 到 main 自动发布

七、成果

这个项目的价值不在技术复杂度,而在于「一次 URL 访问」这个交互范式带来的减负。AI 工具应该像快捷键一样,出现在你手指最顺的地方。