Адаптеры сайтовРуководство по разработке

🔌 Руководство по разработке адаптеров

Адаптеры ForcedSkin пишутся как декларативные JSON-формулы ( forcedskin-adapter-formula/v1 ), описывающие целевые hostname и слои селекторов для окраски. Сервер хранит только JSON; расширение поставляет фиксированный интерпретатор , который применяет правила покраски — никогда произвольный JavaScript.

Как это работает

Базовый движок начинает с наложений CSS-переменных и inline-стилей для исходной перекраски.

Когда hostname совпадает, адаптеры выполняются по возрастанию приоритета — чем меньше число, тем раньше запускается интерпретатор (рекомендуемые адаптеры сайтов используют 100).

Каждый адаптер подстраивает целевые узлы через открытые helpers engineApi; пропускайте прозрачные оверлеи / стеки плеера согласно рекомендациям.

Среда выполнения уже отслеживает изменения DOM и повторно применяет адаптеры с троттлингом — MutationObserver писать не нужно.

Формулы приходят из GET /api/pub/extension-adapters ; JSON каждого записи кэшируется локально. Обновление копии на портале вступает в силу, когда пользователи обновят адаптеры или снова откроют браузер — переустановка не нужна.

Минимальный пример

{
  "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[]ОбязательноУпорядоченные слои покраски — типы описаны в таблице слоёв

Правила match.hostname

ОператорЗначение
equalsHostname равен value (без учёта регистра)
suffixDomainHostname равен value или оканчивается на .value (покрывает example.com и *.example.com)

Ключи палитры (richText cssVars / color)

ПолеОписание
backgroundФон страницы
foregroundОсновной текст
surfaceЗаливка карточки / панели
surfaceMutedПриглушённые контейнеры / заливка hover
borderЦвет рамки
mutedВторостепенный текст
primary500Основной акцент (ссылки) из theme primary.500
primary700Более глубокое состояние primary из primary.700 или 800

Рекомендуемые типы слоёв

Думайте семантически — не только по именам элементов. См. закоммиченный пример home/server/seeds/bilibili-adapter.formula.json для полного разбора.

Тип слояНазначениеТипичное соответствиеmarkApplied
surfaceПанели, строки списков, оболочки навигацииbackground→surface, color→foreground, border→borderbg + текст + рамка
accentАктивные вкладки / текущие строки спискаbackground+border→primary700, color→background для контрастаbg + текст + рамка
canvasHero-блоки с растровым фономУбрать background-image, background→palette backgroundОбычно только фон
richTextСайты с фирменными web componentsВыдать нужные CSS-переменные + текстовое сопоставление цветатекст (иногда + фон)
svgRecolorВстроенные SVG-глифыfill/stroke по умолчанию→currentColor; необязательные ключи палитры fill/stroke для фиксированного цветаНеобязательная пометка, чтобы не мешать очистке

Пропускайте рискованные области: Движок уже игнорирует media, canvas, iframe, тяжёлые blend/backdrop-слои и узлы, чьи классы намекают на маски или оверлеи — расширьте эти ограничения для полупрозрачного хрома плееров.

Отказ хоста: Пометьте поддеревья (напр. превью) с помощью 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 должен строго валидироваться

Некорректные схема / слои / правила хоста отклоняются при проверке — опирайтесь на минимальный пример и seeds этого руководства перед отправкой.

Включайте skipOverlayLike на surface

Совпадает с защитами ядра, чтобы полупрозрачные HUD / видеостеки не сплющивались в сплошную заливку.

Никогда не отправляйте JavaScript

Поле code — только JSON; расширение больше не вычисляет строки через new Function.

Чеклист отправки

🔐 Войдите перед отправкой заявки.

📋 Укажите отображаемое имя, домены через запятую и вставьте JSON в поле кода адаптера.

⏳ Сервер проверяет JSON-схему и правила безопасности; JavaScript и другой рискованный код отклоняются.

✅ Корректные формулы публикуются автоматически и доходят до пользователей расширения, которые синхронизируют адаптеры.

❌ Слишком широкие селекторы или некорректный JSON возвращают отзыв — исправьте и отправьте снова.

Формат целевого домена

siteDomain — человекочитаемый список; фактическое сопоставление идёт по match.hostname, например:

[
  { "op": "equals", "value": "bilibili.com" },
  { "op": "suffixDomain", "value": "bilibili.com" }
]

Готовы? Откройте галерею адаптеров и отправьте JSON.

🔌 Отправить адаптер