🔌 适配器开发指南
ForcedSkin 适配器基于 声明式 JSON 公式 ( forcedskin-adapter-formula/v1 )描述匹配的域名与分层选择器。服务端与数据库仅存储 JSON;扩展内置 固定解释器 应用着色规则,不执行任意 JavaScript。
工作原理
主引擎先对页面做全局配色覆盖(CSS 变量 + inline)。
命中域名后,按适配器 priority 数值升序执行内置步骤(数值越小越早;站点适配建议写 100)。
适配器通过 engineApi 精准修正元素;播放器叠层、半透明 HUD 等需在最佳实践中额外跳过。
引擎已对 DOM 变更增量改写并节流回调适配器,无需手写 MutationObserver。
公式由服务端下发:扩展从 GET /api/pub/extension-adapters 拉取每条适配器的 JSON(code 字段),缓存后由解释器解析;后台或种子更新后,用户同步或重启浏览器即可生效,无需重装扩展。
最小示例
{
"schema": "forcedskin-adapter-formula/v1",
"id": "example-site",
"priority": 100,
"match": {
"hostname": [
{ "op": "suffixDomain", "value": "example.com" }
]
},
"layers": [
{
"kind": "surface",
"skipOverlayLike": true,
"selectors": [".site-navbar", ".site-header"]
}
]
}适配器公式 forcedskin-adapter-formula/v1
提交的「适配器代码」须为合法 JSON,根对象字段如下(服务端会校验):
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| schema | string | 必填 | 固定为 forcedskin-adapter-formula/v1 |
| id | string | 必填 | 适配器逻辑 ID,建议与站点英文名一致,如 bilibili |
| priority | number | 可选 | 执行顺序:数值越小越早;站点适配建议 100 |
| match.hostname | Rule[] | 必填 | hostname 匹配规则数组(见下表) |
| layers | Layer[] | 必填 | 着色分层列表,kind 含义见「着色分层」表 |
match.hostname 规则
| op | 含义 |
|---|---|
| equals | hostname 与 value 完全相同(忽略大小写) |
| suffixDomain | hostname 等于 value 或以 .value 为后缀(覆盖 example.com 与 *.example.com) |
palette 键名(richText.cssVars / color)
| 字段 | 说明 |
|---|---|
| background | 页面主背景色 |
| foreground | 主文字颜色 |
| surface | 卡片/面板背景 |
| surfaceMuted | 次级容器/悬浮背景 |
| border | 边框颜色 |
| muted | 次要文字颜色 |
| primary500 | 主色强调(链接等),由主题 JSON 的 primary.500 映射 |
| primary700 | 主色更深状态,由 primary.700 / 800 映射 |
推荐的「着色分层」
按语义分层而非只看标签名;完整范例见仓库文件 home/server/seeds/bilibili-adapter.formula.json 。
| 分层 kind | 用途 | 典型样式映射 | markApplied |
|---|---|---|---|
| surface | 面板、列表行、导航条容器 | background→surface,color→foreground,border→border | bg + text + border(三项都打) |
| accent | 选中 Tab、当前列表项 | background+border→primary700,color→background(保证对比) | bg + text + border |
| canvas | 带大图背景的整块容器 | 移除 background-image,background→background | 通常仅 bg |
| richText | 站内富文本自定义元素 | 写入站点要求的 CSS 变量 + color→foreground | text(或按需加 bg) |
| svgRecolor | SVG path 等矢量图标 | 默认 fill/stroke→currentColor;可填 fill/stroke 调色板键直接着色 | 可不标记(避免干扰引擎清除逻辑) |
不应着色: 引擎已跳过 IMG / VIDEO / CANVAS / IFRAME、复杂背景图、混合模式、backdrop-filter、以及 class 含 mask/overlay 等的节点;适配器内应对半透明遮罩、播放器叠层做额外 skip。
站点隔离: 宿主页面可将预览区等加上 data-gts-ignore ,其子孙不参与全局改写。
完整示例(节选)
{
"schema": "forcedskin-adapter-formula/v1",
"id": "bilibili",
"priority": 100,
"match": {
"hostname": [
{ "op": "equals", "value": "bilibili.com" },
{ "op": "suffixDomain", "value": "bilibili.com" }
]
},
"layers": [
{
"kind": "surface",
"skipOverlayLike": true,
"selectors": ["[class*='bili-']", "[class*='bpx-']"]
},
{
"kind": "accent",
"selectors": ["[class*='active']", ".bili-dyn-list-tabs__item.active"]
},
{
"kind": "canvas",
"selectors": [".message-bg", ".message-bgc"]
},
{
"kind": "richText",
"selectors": ["bili-rich-text"],
"cssVars": {
"--bili-rich-text-color": "foreground",
"--bili-rich-text-link-color": "primary500",
"--bili-rich-text-link-color-hover": "primary700"
},
"color": "foreground"
},
{
"kind": "svgRecolor",
"selectors": ["svg path", "svg rect", "svg circle"]
}
]
}最佳实践 & 注意事项
公式须通过 JSON / schema 校验
schema、layers、match.hostname 写法错误会导致拒稿;请对照本文最小示例与仓库 seeds。
surface 层建议开启 skipOverlayLike
与引擎一致,跳过半透明叠层与 pointer-events:none 等高风险节点,避免把视频蒙层涂成实色。
禁止提交 JavaScript
code 字段必须为 JSON 公式;扩展不再对服务端字符串使用 new Function 执行脚本。
提交须知
🔐 提交前需登录账号。
📋 提交时需填写展示名称、目标域名列表(逗号分隔)及完整 JSON(粘贴至「适配器代码」文本框)。
⏳ 提交后为待审核,管理员会校验合法性与安全性。
✅ 审核通过后对在扩展中启用该适配器的全体用户生效。
❌ 公式无效或选择器过宽损坏布局时将退回,请据反馈修订后重提。
目标域名格式
siteDomain 为人类可读列表;真正匹配使用 match.hostname,例如:
[
{ "op": "equals", "value": "bilibili.com" },
{ "op": "suffixDomain", "value": "bilibili.com" }
]准备好了?前往适配器页面提交你的适配器!
🔌 前往提交适配器
ForcedSkin