ForcedSkinForcedSkin
Adapter situsPanduan pengembangan

🔌 Panduan pengembangan adapter

Adapter ForcedSkin ditulis sebagai formula JSON deklaratif ( forcedskin-adapter-formula/v1 ) yang menjelaskan hostname target dan lapisan selektor yang diwarnai. Server hanya menyimpan JSON; ekstensi membawa interpreter tetap yang menerapkan aturan pewarnaan — tidak pernah JavaScript sembarang.

Cara kerja

Mesin inti mulai dengan overlay variabel CSS + gaya inline untuk pewarnaan dasar.

Saat hostname cocok, adapter berjalan menaik menurut prioritas — semakin kecil angkanya, semakin dulu interpreter dijalankan (adapter situs yang disarankan memakai 100).

Setiap adapter menyesuaikan node target lewat helper engineApi yang diekspos; lewati overlay transparan / tumpukan pemutar sesuai catatan praktik terbaik.

Runtime sudah mengamati delta DOM dan menerapkan ulang adapter secara tertahan — Anda tidak perlu boilerplate MutationObserver.

Formula datang dari GET /api/pub/extension-adapters ; blob JSON setiap catatan di-cache secara lokal. Memperbarui salinan di portal berlaku setelah pengguna menyegarkan adapter atau membuka ulang peramban — tanpa instal ulang.

Contoh minimal

{
  "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 adapter forcedskin-adapter-formula/v1

JSON yang dikirim harus lolos validasi server — skema akar seperti ini:

FieldTipeWajibDeskripsi
schemastringWajibString versi literal forcedskin-adapter-formula/v1
idstringWajibId adapter logis — biasanya nama kode situs, mis. bilibili
prioritynumberOpsionalUrutan eksekusi (menaik). Adapter khusus situs biasanya memakai 100
match.hostnameRule[]WajibArray objek aturan hostname (lihat tabel di bawah)
layersLayer[]WajibLapisan pewarnaan berurutan — jenis didokumentasikan di tabel lapisan

Aturan match.hostname

OperatorArti
equalsHostname sama dengan value (tidak peka huruf besar/kecil)
suffixDomainHostname sama dengan value atau diakhiri .value (mencakup example.com dan *.example.com)

Kunci palet (richText cssVars / color)

FieldDeskripsi
backgroundLatar halaman
foregroundTeks utama
surfaceIsian kartu / panel
surfaceMutedKontainer redup / isian hover
borderWarna tepi
mutedTeks sekunder
primary500Penekanan utama (tautan) dipetakan dari theme primary.500
primary700Keadaan primary lebih dalam dipetakan dari primary.700 atau 800

Jenis lapisan yang disarankan

Pikir secara semantik — bukan hanya nama elemen. Lihat contoh yang sudah di-commit home/server/seeds/bilibili-adapter.formula.json untuk panduan lengkap.

Jenis lapisanKegunaanPemetaan khasmarkApplied
surfacePanel, baris daftar, cangkang navigasibackground→surface, color→foreground, border→borderbg + teks + tepi
accentTab aktif / baris daftar saat inibackground+border→primary700, color→background untuk kontrasbg + teks + tepi
canvasLempeng hero dengan latar rasterHapus background-image, background→palette backgroundBiasanya hanya latar
richTextSitus yang mengekspos web component bermerekKeluarkan variabel CSS yang diperlukan + pemetaan warna teksteks (kadang + latar)
svgRecolorGlif SVG inlinefill/stroke default→currentColor; kunci palet fill/stroke opsional untuk warna tetapPenandaan opsional agar tidak mengganggu pembersihan

Lewati wilayah berisiko: Mesin sudah mengabaikan media, canvas, iframe, lapisan blend/backdrop berat, dan node yang kelasnya mengisyaratkan mask atau overlay — perluas pagar itu untuk chrome pemutar tembus pandang.

Opt-out host: Tandai subtree (mis. pratinjau) dengan data-gts-ignore agar turunannya melewati pewarnaan global.

Kutipan contoh lengkap

{
  "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"]
    }
  ]
}

Praktik terbaik

JSON harus valid secara ketat

Skema/lapisan/aturan host yang tidak valid ditolak saat tinjauan — lipat contoh minimal dan seed panduan ini sebelum mengirim.

Aktifkan skipOverlayLike pada surface

Selaras dengan pengamanan mesin agar HUD tembus / tumpukan video tidak diratakan menjadi isian padat.

Jangan kirim JavaScript

Field code hanya JSON; ekstensi tidak lagi mengevaluasi string lewat new Function.

Daftar periksa pengiriman

🔐 Masuk sebelum mengirim.

📋 Berikan nama tampilan, domain dipisah koma, dan tempel JSON ke textarea kode adapter.

⏳ Server memvalidasi skema JSON dan aturan keamanan; JavaScript atau kode berisiko lainnya ditolak.

✅ Formula valid dipublikasikan otomatis dan didistribusikan ke pengguna ekstensi yang menyinkronkan adapter.

❌ Selektor terlalu luas atau JSON tidak valid akan dikembalikan — perbaiki dan kirim ulang.

Format domain target

siteDomain adalah daftar yang ditampilkan; pencocokan sebenarnya memakai match.hostname seperti:

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

Siap? Buka galeri adapter dan kirim JSON Anda.

🔌 Kirim adapter