🔌 アダプター開発ガイド
ForcedSkin アダプターは 宣言型 JSON formula ( forcedskin-adapter-formula/v1 )として作成され、対象 hostname と着色する selector layer を記述します。サーバーは JSON のみを保存し、拡張機能は 固定インタープリター を同梱してペイントルールを適用します — 任意の JavaScript は実行しません。
仕組み
コアエンジンは CSS 変数 + インラインオーバーレイでベースラインの再配色を行います。
hostname が一致すると、アダプターは priority の昇順で実行されます — 数値が小さいほど先に実行されます(サイトアダプターは 100 を推奨)。
各アダプターは公開された engineApi ヘルパーで対象ノードを調整します。半透明オーバーレイ/プレイヤー領域は Best practices の注意に従ってスキップしてください。
ランタイムは DOM の変化を監視し、スロットル付きでアダプターを再適用します — MutationObserver のボイラープレートは不要です。
formula は GET /api/pub/extension-adapters から取得され、各レコードの JSON blob はローカルにキャッシュされます。ポータルの内容を更新すると、ユーザーがアダプターを更新するかブラウザを再起動すれば反映されます — 再インストールは不要です。
最小例
{
"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"]
}
]
}アダプター formula forcedskin-adapter-formula/v1
提出する JSON はサーバー検証に合格する必要があります — ルートスキーマは次のとおりです:
| フィールド | 型 | 必須 | 説明 |
|---|---|---|---|
| schema | string | 必須 | リテラルバージョン文字列 forcedskin-adapter-formula/v1 |
| id | string | 必須 | 論理アダプター id — 通常はサイトのコードネーム(例: bilibili) |
| priority | number | 任意 | 実行順(昇順)。サイト固有アダプターは通常 100 |
| match.hostname | Rule[] | 必須 | hostname ルールオブジェクトの配列(下表参照) |
| layers | Layer[] | 必須 | 順序付きペイント layer — kind は layer 表に記載 |
match.hostname ルール
| 演算子 | 意味 |
|---|---|
| equals | hostname が value と等しい(大文字小文字を区別しない) |
| suffixDomain | hostname が value と等しい、または .value で終わる(example.com と *.example.com の両方をカバー) |
パレットキー (richText cssVars / color)
| フィールド | 説明 |
|---|---|
| background | ページ背景 |
| foreground | 主要テキスト |
| surface | カード / パネル塗り |
| surfaceMuted | 控えめなコンテナ / hover 塗り |
| border | ボーダー色 |
| muted | 二次テキスト |
| primary500 | 主要強調(リンク)— テーマ primary.500 からマップ |
| primary700 | より深い primary 状態 — primary.700 または 800 からマップ |
推奨 layer kind
要素名だけでなく意味的に考えてください。リポジトリ内のサンプル home/server/seeds/bilibili-adapter.formula.json で完全な解説を参照してください。
| Layer kind | 用途 | 典型的なマッピング | markApplied |
|---|---|---|---|
| surface | パネル、リスト行、ナビシェル | background→surface, color→foreground, border→border | bg + text + border |
| accent | アクティブタブ / 現在のリスト行 | background+border→primary700, color→background(コントラスト用) | bg + text + border |
| canvas | ラスター背景を持つヒーロースラブ | background-image を除去、background→palette background | 通常は background のみ |
| richText | ブランド web component を公開しているサイト | 必要な CSS 変数 + テキスト色マッピングを出力 | text(場合により + background) |
| svgRecolor | インライン SVG グリフ | デフォルト fill/stroke→currentColor; 固定色用に fill/stroke パレットキーを任意指定 | クリーンアップ干渉を避けるための任意マーキング |
リスクの高い領域をスキップ: エンジンは media、canvas、iframe、heavy blend/backdrop layer、マスク/オーバーレイを示す class を持つノードを既に無視します — 半透明プレイヤー UI 向けにこれらのガードレールを拡張してください。
ホスト側オプトアウト: サブツリー(例: プレビュー)に 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/layer/host ルールは審査で却下されます — 提出前に本ガイドの最小サンプルと seed を確認してください。
surface で skipOverlayLike を有効化
コアエンジンの安全策と一致し、半透明 HUD/動画スタックが単色塗りつぶしに潰されないようにします。
JavaScript を送らない
code フィールドは JSON のみです。拡張機能は new Function による文字列 eval は行いません。
提出チェックリスト
🔐 提出前にサインインしてください。
📋 表示名、カンマ区切りドメイン、JSON をアダプター code テキストエリアに貼り付けてください。
⏳ スタッフが安全性と selector スコープを検証するまで pending 状態になります。
✅ 承認されたアダプターは、アダプターを同期するすべての拡張機能ユーザーに配信されます。
❌ 過度に広い selector や無効な JSON はフィードバック付きで却下 — 修正して再提出してください。
対象ドメイン形式
siteDomain は人間向けのリストです。実際のマッチングは match.hostname で行われます:
[
{ "op": "equals", "value": "bilibili.com" },
{ "op": "suffixDomain", "value": "bilibili.com" }
]準備はできましたか?アダプターギャラリーを開いて JSON を提出しましょう。
🔌 アダプターを投稿