适配器开发指南

🔌 适配器开发指南

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,根对象字段如下(服务端会校验):

字段类型必填说明
schemastring必填固定为 forcedskin-adapter-formula/v1
idstring必填适配器逻辑 ID,建议与站点英文名一致,如 bilibili
prioritynumber可选执行顺序:数值越小越早;站点适配建议 100
match.hostnameRule[]必填hostname 匹配规则数组(见下表)
layersLayer[]必填着色分层列表,kind 含义见「着色分层」表

match.hostname 规则

op含义
equalshostname 与 value 完全相同(忽略大小写)
suffixDomainhostname 等于 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→borderbg + text + border(三项都打)
accent选中 Tab、当前列表项background+border→primary700,color→background(保证对比)bg + text + border
canvas带大图背景的整块容器移除 background-image,background→background通常仅 bg
richText站内富文本自定义元素写入站点要求的 CSS 变量 + color→foregroundtext(或按需加 bg)
svgRecolorSVG 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" }
]

准备好了?前往适配器页面提交你的适配器!

🔌 前往提交适配器