写一个扩展功能不难。难的是把日常那一堆需求塞进一个壳子,还别互相踩。
翻译要弹窗、划词,新标签页想看天气汇率,书签得写到自己的服务。GitHub 偶尔打包下个目录,V2EX 想自动签到,长列表懒得点下一页。代理、扩展开关、站点 Cookie 又不想再装四五套。每样单独装一个,图标能挤满工具栏,权限重复,设置散得到处都是。从零写倒也能写,就是 Service Worker 说停就停、右键菜单互相覆盖、content script 把宿主页样式搞脏,这些坑得反复踩。
formant 冲着这件事做。Chrome Manifest V3,原生 HTML / CSS / JS,后台走 Oolaf mono 的 Client API。常用能力已经串进去了,空壳演示按钮那种先别指望。

设置用一套 option.html 挂多个面板。总开关在 #features,细节进对应锚点。工具栏图标始终打开 formant popup,不把入口让给某个子模块。后来功能加多了,popup 里也能进书签、站点数据、扩展管理;二级页顶栏统一成返回翻译加当前标题,操作放下面工具条,免得小窗口里再叠一套顶栏。
popup 顶栏有工具箱,点开 tools.html。工具箱关掉、识图还开着,图标仍在,列表里只剩识图。代理那一行下面有 oolafOffice 快捷条。翻译仍是主界面。popup 固定高度 600px,默认三张引擎卡不再出纵向滚动条。


密钥不进扩展仓库。本机只存 mono 地址、一把 Client API Key;GPT、天气这些放 Admin 的 Formant 配置组。DeepLX 的 Key 写在「翻译」面板,留在本机 formantDeeplx,不进仓库,也不经 mono。翻译失败链路写死了:Google 直连不行走 mono Google。这条再失败,自动、Google 模式改走 DeepLX。「全部」则 Google、GPT、DeepLX 三路一起跑。百度已经拿掉。词库打分用合并后的结果,不拿一份静态表当唯一真相。
Google 直连现在走 translate-pa.googleapis.com/v1/translate(gtx)。这条挂了,仍退回 mono Google。
划词面板、GitHub 下载进度、AutoPagerize 状态条、手势轨迹,尽量都进 Shadow DOM。页面通信走 chrome.runtime / chrome.tabs,密钥不塞进 content script。Service Worker 按随时会被杀掉来写,重要状态落 chrome.storage。
代码按模块拆:翻译、词库、New Tab、书签、GitZip、V2EX、自动翻页、代理、扩展管理、站点数据、鼠标手势,各有 bridge、option 面板。新能力往面板上挂,不为单项另开独立设置页。自有源码单文件卡在 500 行附近,background、popup 不无限胀。
翻译、词库
入口就那些:popup 输入框、划词浮标、右键「用 formant 翻译」。模式有自动 / Google / GPT / 全部。本地打分看脚本、词库、句式,简单的走 Google,绕的可以并行 GPT。原文译文都能读。默认模式、目标语言存在 chrome.storage.local,popup、划词共用。popup 打开会自动聚焦输入框。
结果卡上可以复制译文。工具栏那个小窗一切标签就没了,这是 Chrome 对 default_popup 失焦即毁的限制。顶栏能再开一份 popup.html,切标签还在。上次原文只在小窗开着、又切过浏览器标签时才带回来;自己点外面关掉,再打开,输入框是空的。关浏览器后快照也清掉。某一路结果缺了,只标这一路失败,不再把 Cannot read properties of undefined (reading 'ok') 写进每张卡。

内置 en / zh / ja / ko / tr 俚语包,只读,能停用。自定义包在本机,JSON 导入可以新建、合并、替换。管理在 option.html#lexicon。

X / Twitter 时间线另有一条翻译,默认开。匹配 x.com / pro.x.com / twitter.com,可见 tweetText 串行翻,Shadow DOM 展示。只走 translateGooglePa,不打 mono / GPT / DeepLX。译文颜色、字号在 option.html#x-translate,默认 #8c8c8c / 14px。

Discord 聊天也有手动翻译,默认开。discord.com(含 ptb、canary)频道页右下角一个按钮,点一次只翻当前频道已经加载的这一页。换频道算新的一页;不点,上滚加载出来的更早消息不会自己翻。译文走 Google 直连,不打 mono。颜色、字号共用 #x-translate。记录只在当次页面内存里,刷新就没了。
输入框右侧还有一枚翻译图标。选 Google(translate-pa),也能选 GPT。GPT 走 mono,吃这把 Client API Key。译文直接写进输入框,不会自动发出去。框正上方贴一条细栏:翻译中转圈,失败出红字,能撤回。写入走 Slate 的粘贴,不直接改 DOM,不然 Backspace 删不掉,字还会叠在占位符上。输入被改过、发出去、切频道、按 Esc,细栏自己收掉。

elitepvpers 默认也开着。只翻帖子标题、楼层正文:列表标题、帖子页标题、每楼标题、post_message。导航、用户名、签名不动。译文仍是 Google 直连,长楼分段,样式共用 #x-translate。关掉开关,译文马上卸掉。
New Tab
覆盖浏览器新标签页,布局能推到 mono。第一行四张卡:天气、假期、汇率、热点英语。天气大概十分钟单独刷一次,别的卡片不动。热点英语词数可配,单词上方标美式 IPA,释义、谐音默认马赛克,点一下揭开。能单词语,也能整卡连读。已会的剔掉,会补新的上来。天气的逐日预报在周几上面写了月-日,比如 10-08。
假期卡平时写着「今天 M 月 D 日 周 X」。赶上假期,这行藏起来,左边改成「当前是某假期」,大号天数改成距下一个假期,下面带上那个假期的日期。右边固定接下来 3 个。假期内,右边是再往后的 3 个;平时右边包含左边正在倒计时的那个。
假期、汇率各自有缓存时间戳。数据超过 24 小时,卡底会出琥珀色「N 天前更新」。打开新标签页时,天气超过 30 分钟、假期跨天、汇率超 24 小时才补拉。后台 10 分钟 alarm 仍只刷天气,假期只在跨天时顺带查。右侧假期条数不够填时,缓存也当过期,打开新标签页会再拉。
换电脑打开空主页会自动拉云端。现在也不只看「空」:本机 revision 落后云端,打开 New Tab 就会对账拉取。点拉取时,本机落后改用云端布局,不再按图标数量误判,旧电脑也盖不掉云端。空布局去拉,仍不会把云端已有的盖掉。壁纸、遮罩、城市在 #newtab,连 mono 仍在 #sync。
快捷图标先请求各站 {origin}/favicon.ico。裂了再回退 Google s2/favicons,Google 地址不写回保存字段。成功拉到的图标以 data URL 缓存在 newtabCacheFavicons,按 origin / 自定义 src;未命中先文字占位再换图。14 天 stale-while-revalidate。换一把 Client API Key 会清掉这坨缓存。编辑图标链接后,默认 favicon 不再写入 src,避免保存完变成文字占位。
添加、编辑链接 dialog 请求 mono 时会出半透明 loading,期间锁表单、按钮,Esc、点遮罩也关不掉,免得连点。
汇率按 1 USD 换本机所选币种。刷新旁边可以改显示条数(1 到 5)、换币种、拖拽排序,输入国家、货币名也能搜。越南盾这类原先没有的法币补上了,美元自己不在列表里。上游没给有效汇率就显示「—」。列表上方能输入金额,按报价折成人民币,列表里没选人民币也会带上 CNY。这套偏好只留在这台电脑的 formantExchange,不进 mono,换 Key 也不清。

热点英语右上角能翻到生词本,3D 翻面。停在哪一面记在 formantVocabFace,刷新后还停在那里。划词、popup 译完,只记单个英文单词。句子、中文、别的语言不记。it、he、to 这类功能词不记,asdf、qwerty、连续三个重复字母这种乱串也不记。翻译框会等这轮输入停住大约 1 秒,失焦也行,再记框里最后那个词;先停在 he 再写成 hello,会用 hello 换掉 he。划词仍是译完就记,带首尾标点也记这个词。
释义改走有道英汉,带词性,每个词性最多 3 个义项,挤在一行,悬停看全文。词典没有就退回 Google 译文。音标用有道美式,不用 Google 附带的拼音。发音只读一遍。读中文释义时去掉词性,只读前两个义项。热点英语的喇叭也改成一遍。
单词列宽锁死,释义左边对齐。长释义只在生词本这张卡里截断,不会把旁边的天气、假期、汇率挤窄,翻面也不会错位。没有生词时,卡片中间写「暂无生词」。「查看更多」的弹窗,开闭动画照着链接编辑那只做:淡入、往上挪一点。Esc、点遮罩会把关闭动画走完。


书签、图片
书签变更会防抖上传,也能手动推上去。按 Client API Key 隔离,全站一个池那种没有。空文件夹列表不会覆盖云端。换一把 Key,本机 New Tab、词卡、手势缓存会清掉,免得串数据。

图片右键可以另存 PNG / JPG,转码在扩展里做,要 downloads 权限。

Telegram Web(web.telegram.org / webk / webz)上,已能看见的图片、视频会补 Shadow DOM 保存按钮,默认开。视频在页面里分片拉,用 <a download> 落盘,整段文件不塞进 Service Worker。没嵌 TG Downloader,也不接它的云端站。
Gemini 生成图下载默认去掉右下角水印。只处理 gemini.google.com / business.gemini.google 上、googleusercontent 的 gg / rd 成图。先抬到原图尺寸,再按右下角蒙版反混合。关掉开关,下载回到原图。不新要权限。
GitZip、V2EX、自动翻页
GitHub 公开仓库、目录、文件页可以勾选打包。单文件、目录 ZIP、多选、整仓都行。只走公开 API,不存 GitHub PAT,不接 OAuth。匿名接口有速率上限,大目录、狂点可能被限,面板会提示。选择方式、主题在 #gitzip。关掉总开关,页面注入、菜单一起停,popup 还在。

V2EX Plus 是上游 GPLv3 模块改过来的:自动签到、未读角标、楼中楼、用户卡、sov2ex。设置字段还用 chrome.storage.sync.options,对上游兼容。关掉就停 alarm、badge、DNR、页面增强,工具栏点击行为不动。带这个模块一起分发,整体按 GPLv3 处理。
主题帖默认先拉齐评论分页再组楼中楼:最多 100 页、4 路并发 fetch,解析后只导入回复单元格,按楼层一次性插入。楼主回复背景默认关(opReplyBg)。

AutoPagerize 滚到底会接下页。内置一大坨站点 XPath,Google / Bing 有优先覆盖,也能配排除列表。本域带 cookie 拉,跨域才走 background。HTML 解析后先去掉脚本再插入,避免宿主页跑远端代码。linux.do 已硬排除,翻页不进这个站。

linux.do、freefq、huavod
linux.do 有一套列表换皮,默认开。只动 https://linux.do/ 非帖子页的 .topic-list:formant token,行高大约 48px,欢迎横幅藏掉,分类 Tab 收紧。手法参考 linuxdo-lark-ui(MIT),没整份拷油猴,品牌、侧栏、详情结构不改。列表未读、新帖左侧有 accent 竖条,标题加粗;底栏分类常显,tag 悬停再出。
列表页点帖子 /t/ 链接改 window.open 新标签,拦截 capture,避免 Discourse SPA 在当前页跳。Ctrl / Cmd / Shift / Alt 仍交浏览器。顶栏搜索、全页 /search 里的帖子链一样处理。关总开关后恢复原行为。
详情页按阅读向收了一圈控件:topic-map、楼层轴、楼主徽章、分享书签举报那排、相关话题、标语条。点赞、回复常显。
freefq.com / www.freefq.com(含 /m/)默认开导出。解除禁复制,挂 Shadow DOM「导出 TXT」。PC 取 td#text,移动端取 div.wz,UTF-8(BOM)经 chrome.downloads 落盘。不走 mono,不新要权限。页尾脚本爱绑 onmousedown / onselectstart / oncontextmenu,扩展会清掉,短时轮询防再绑。
huavod.com 控制台解锁默认开。动态 DNR 拦页面拉的 disable-devtool 脚本。MAIN world 在 document_start 挂起 DisableDevtool.isSuspend;没有用户手势时吞掉 history.back、history.go(-n),开 F12 不至于被踢回上一页。
这两项开关在 #features,概览文案也写了。
bbs.hupu.com 默认一套简洁版式,不走 mono,不新要权限。顶栏改白底两层导航,App 推广、游戏中心藏掉。帖子改成一张白卡,回帖行距收紧。表情按真实尺寸分档:小的收成 20px 高,贴纸统一高度,大图限制最大边。样式在 document_start 就挂上,打开帖子不再先闪一下红顶栏。关掉开关再刷新,回到站点原样。
帖子详情可以翻上一帖、下一帖,标题下一条,底部再一条。顺序按板块列表:上一帖是列表里更靠上的,下一帖是更靠下的。直接打开详情 URL 也会自己去板块列表里找邻居,最多翻 10 页。找不到,下一帖就变成回板块。
工具箱
popup 顶栏的工具箱打开 tools.html,默认开。左侧六个工具,外加识图:二维码生成、识别,时间戳(秒、毫秒、时区、FILETIME、世界时间表),JSON 美化(格式化、压缩、多页,最多 20 页,文本着色、树视图),JSON 对比,图片 Base64,编码转换(Unicode、URL、Base64、Hex、JWT、MD5、SHA、Gzip 这些)。都在本机跑,不上传,不走 mono,无新权限。列表能拖拽排序,顺序存在 formantToolsOrder。页内数据刷新就没,不写 chrome.storage。
识图收进这个列表,不再单开一页。左右布局:拖拽、选文件、粘贴、URL,右边出中文、English、JSON。模型在 option.html#image-prompt,OpenAI 兼容,存在 formantImagePrompt。按画面分区写成长提示,可见文字原样引用,圈选、打码、水印都算进画面。送出去前长边大约压到 1600。Key 不进仓库。

鼠标手势
后来加的,默认关着。本机要是还开着 smartUp,两边会抢右键,只开一个就行。开了之后,普通网页按住右键划,路径收成 L / R / U / D。默认左退右进,上下刷新,右下关当前标签,上到顶,下到底。
设置页点某个动作的「绘制」,画板里按住左键划一下松手就绑上。网页上仍是右键。位移太小放行系统菜单,输入框、可编辑区域不触发。轨迹 Shadow DOM。一条路径不能绑两个动作。
映射能推到 mono,按 Client API Key 隔离,规则按 New Tab 那套来。「刷新」把云端拉下来,旁边那个按钮把本机推上去。空映射不会覆盖云端已有的。总开关只存在本机,不进云端。chrome://、商店页、PDF 注入不进去。
代理、扩展、站点数据
代理走 chrome.proxy.settings:直连、系统代理、PAC(URL / 本地脚本)、最多五条固定服务器。默认偏系统代理,固定代理端口默认 7890。细配在 #proxy,popup 顶上能快捷切。不做账号密码认证,也没整包塞第三方代理扩展。

扩展管理用 chrome.management 列出本机扩展,能搜、能开关,formant 自己不出现。完整面板在 #extensions,popup 顶栏也有二级入口。不做分组、锁定、别名那一套。

站点数据在 popup 里读当前 http / https 页的 Cookie、localStorage、sessionStorage。进去、切 Tab 会自动拉。能复制、单条删、整页清,都要确认。Cookie 经 background 转发。不搞跨站批量写。

JSON Prettify 是 DevTools 面板加内容脚本查看器。后来整页查看器、DevTools 面板也补了 JS / CSS 美化:打开 .js / .css 纯文本页(min.js 那种)自动缩进,JSON 仍走树视图。高亮自研,没嵌 prettier。面板名是 formant,JSON / JS / CSS 响应一块收。工具栏可以下载美化后的内容,文件名取当前页面、请求路径。

网页右键可以整页滚动截屏,也可以只截可见区域,拼成 PNG。两条都是顶层菜单,点了就截。按需注入,逐屏 captureVisibleTab,第一屏之后藏掉 fixed / sticky,免得顶栏重复。高度锁在开始时的 scrollHeight,最多 80 屏。关开关后菜单消失。不新要权限。失败会在页角提示。
扩展里带了 Word、Excel、PPT,目录 oolafoffice/。上游是 VibeOffice,界面上的入口字样已经拿掉,NOTICE 仍记着出处。popup 快捷条、网页右键「formant oolafOffice」都能开起始页。本机读写 docx / xlsx / pptx,不走 mono。关掉 features.office.enabled,快捷条、右键一起藏。
中文稿子上补过一轮版式:审批表竖排停在合并格里,双栏里的大图按栏宽缩小,公文行距按网格算,宽表居中时不再只往右溢。Excel 里 Wingdings 空复选框不再画成英镑,双击选区边框会进编辑,不会飞到表的最末格。PPT 读取东亚字体。Ctrl 加滚轮走套件自己的缩放。F5、Ctrl+S 这类也留给套件,复制粘贴仍交浏览器。

各模块有总开关。关了停运行时,配置尽量留着。权限按能力要:alarms、bookmarks、contextMenus、cookies、downloads、management、proxy、scripting、storage 这些;GitHub、V2EX、Imgur 的 host 按需列。HLS 另要 webNavigation。右键菜单按自己的 ID 增删,不 removeAll() 把别人的菜单清掉。

另外几块页面上的小东西,开关都在 #features。
HLS 默认开。主框架打开以 .m3u8 结尾的地址,会改到扩展播放页。地址栏打出 m3u8 这个关键字,也会打开播放页。用的是 hls.js 1.6.10。倍速、旋转、左右键进度(5 秒,按住 Shift 为 30 秒),能把当前列表存成 MP4。倍速、旋转、音量不写入 storage,关掉再开回到 1 倍、0°。
Gmail 只在 mail.google.com 顶栏,问号、设置左边,显示当前登录邮箱,点一下复制。别的域名不注入。
Axure 用 file:// 打开本地发布的 HTML 时,在各框架之间转发页面已有的消息。没有事件节点的文件不挂监听。浏览器得允许扩展访问文件 URL。
Vue 在 DevTools 里多一个「Vue」面板。Vue 3、Vue 2 都能看组件树、props / data,Pinia、Vuex 的当前状态也能看,可以高亮组件。当前标签检测到 Vue,工具栏 formant 图标右上角加一枚绿色 V。
接到 mono
扩展侧就两样:环境(dev 连本地,prod 连 www.oolaf.top)、一把 Client API Key。设置页顶栏会显示 Key 指纹,不展示全文。New Tab 布局、热点英语、书签、手势映射都按这把 Key 隔离。
option.html#sync 能把本机钥匙导出成 JSON,也能把 JSON 贴进文本框再导入。里面有连接地址、Client API Key、识图的 Base URL / API Key / Model、Imgur Client ID、DeepLX Key。文件版本是 2,旧的 version 1 导不进去。缺的面板不覆盖。换 Key 仍会清掉本机 New Tab、手势缓存。
服务端大概是这些:formant.translate.* 翻译通道,formant.weather.* / formant.exchange.* 天气汇率,formant.vocab.model_key / card_size 热点英语,formant.newtab.default_home 默认主页。密钥只放 Admin,不进扩展仓库。
本地加载
Chrome 打开 chrome://extensions,打开开发者模式
「加载已解压的扩展程序」,选中仓库里的 formant/ 目录
打开 option.html#sync,填环境、Client API Key
到 #features 打开要用的模块,再到对应面板细调
版本号看 formant/manifest.json 的 version。要改某一块,从对应 bridge / option 面板顺着读就行。
github 地址:https://github.com/dirkhe1051931999/formant
确认评论邮箱
邀请制评论