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

设置用一套 option.html 挂多个面板。总开关在 #features,细节进对应锚点。工具栏图标始终打开 formant popup,不把入口让给某个子模块。后来功能加多了,popup 里也能进书签、站点数据、扩展管理;二级页顶栏统一成返回翻译加当前标题,操作放下面工具条,免得小窗口里再叠一套顶栏。


密钥不进扩展仓库。本机只存 mono 地址、一把 Client API Key;GPT、百度、天气这些放 Admin 的 Formant 配置组。翻译失败链路写死了:Google 直连不行走 mono Google,再不行百度。词库打分用合并后的结果,不拿一份静态表当唯一真相。
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 打开会自动聚焦输入框。

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

X / Twitter 时间线另有一条翻译,默认开。匹配 x.com / pro.x.com / twitter.com,可见 tweetText 串行翻,Shadow DOM 展示。只走 translateGooglePa,不打 mono / GPT / 百度。译文颜色、字号在 option.html#x-translate,默认 #8c8c8c / 14px。
[图片占位:X 时间线译文]
New Tab
覆盖浏览器新标签页,布局能同步到 mono。第一行四张卡:天气、假期、汇率、热点英语。天气大概十分钟单独刷一次,别的卡片不动。热点英语词数可配,单词上方标美式 IPA,释义、谐音默认马赛克,点一下揭开。能单词语,也能整卡连读。已会的剔掉,会补新的上来。
假期卡常驻「今天 M月D日 周X」。假期、汇率各自有缓存时间戳。数据超过 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、点遮罩也关不掉,免得连点。

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

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

Telegram Web(web.telegram.org / webk / webz)上,已能看见的图片、视频会补 Shadow DOM 保存按钮,默认开。视频在页面里分片拉,用 <a download> 落盘,整段文件不塞进 Service Worker。没嵌 TG Downloader,也不接它的云端站。
[图片占位:Telegram 保存按钮]
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、楼层轴、楼主徽章、分享书签举报那排、相关话题、标语条。点赞、回复常显。
[图片占位:linux.do 列表换皮]
freefq.com / www.freefq.com(含 /m/)默认开导出。解除禁复制,挂 Shadow DOM「导出 TXT」。PC 取 td#text,移动端取 div.wz,UTF-8(BOM)经 chrome.downloads 落盘。不走 mono,不新要权限。页尾脚本爱绑 onmousedown / onselectstart / oncontextmenu,扩展会清掉,短时轮询防再绑。
[图片占位:freefq 导出 TXT]
huavod.com 控制台解锁默认开。动态 DNR 拦页面拉的 disable-devtool 脚本。MAIN world 在 document_start 挂起 DisableDevtool.isSuspend;没有用户手势时吞掉 history.back、history.go(-n),开 F12 不至于被踢回上一页。
[图片占位:huavod 控制台]
这两项开关在 #features,概览文案也写了。
鼠标手势
后来加的,默认关着。本机要是还开着 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 响应一块收。

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

接到 mono
扩展侧就两样:环境(dev 连本地,prod 连 www.oolaf.top)、一把 Client API Key。设置页顶栏会显示 Key 指纹,不展示全文。New Tab 布局、热点英语、书签、手势映射都按这把 Key 隔离。
服务端大概是这些: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 面板顺着读就行。
确认评论邮箱
邀请制评论