プロジェクト名: Web System Injection
バージョン: v1.0
作成日: 2026-04-12
形態: Chrome拡張機能(Manifest V3)
既存のWebサイトに対して、ドメイン単位でカスタム機能(プラグイン)を注入できるChrome拡張機能。
開発者がプラグインをZIP形式でインポートし、自分のブラウザ上でのみ動作させる。
plugin.json — プラグイン定義ファイル(必須)main.js — メインスクリプト(必須)style.css — スタイルシート(任意)assets/ — アイコン等のリソース(任意)chrome.storage.local から該当データを完全に除去するlocation.hostname)と登録済みプラグインの対象ドメインを照合する*.example.com)* 単独指定に対応する(例: "domains": ["*"])main.js をページに注入して実行するstyle.css が存在する場合はCSSも注入するdocument_idle(DOMContentLoaded後)をデフォルトとするdocument_start / document_end / document_idle)WSI.addButton(options) — ページにフローティングボタンを追加WSI.addPanel(options) — ページにサイドパネルを追加WSI.storage.get(key) / WSI.storage.set(key, value) — プラグイン固有のストレージWSI.log(message) — デバッグログ出力WSI.onPageLoad(callback) — ページ遷移時のフック{
"id": "example-plugin",
"name": "Example Plugin",
"version": "1.0.0",
"description": "プラグインの説明文",
"author": "開発者名",
"domains": [
"example.com",
"*.example.com"
],
"scripts": {
"main": "main.js",
"runAt": "document_idle"
},
"styles": ["style.css"],
"permissions": [],
"config": {
"buttonPosition": "bottom-right"
}
}
| フィールド | 型 | 必須 | 説明 |
|---|---|---|---|
| id | string | ○ | 一意識別子。英数字・ハイフンのみ |
| name | string | ○ | 表示名 |
| version | string | ○ | セマンティックバージョニング |
| description | string | - | 説明文 |
| author | string | - | 作成者名 |
| domains | string[] | ○ | 対象ドメインの配列(1つ以上) |
| scripts.main | string | ○ | メインスクリプトのファイル名 |
| scripts.runAt | string | - | 注入タイミング。デフォルト: document_idle |
| styles | string[] | - | CSSファイル名の配列 |
| permissions | string[] | - | 将来拡張用 |
| config | object | - | プラグイン固有の設定値 |
web-system-injection/
├── manifest.json # Chrome拡張マニフェスト(Manifest V3)
├── background.js # Service Worker
├── content-loader.js # Content Script(全ページに注入)
├── sdk.js # プラグイン向けユーティリティAPI(WSI.*)
├── popup/
│ ├── popup.html # ポップアップUI
│ ├── popup.js # ポップアップロジック
│ └── popup.css # ポップアップスタイル
├── lib/
│ └── jszip.min.js # ZIP解凍ライブラリ
└── icons/
├── icon16.png
├── icon48.png
└── icon128.png
{
"manifest_version": 3,
"name": "Web System Injection",
"version": "1.0.0",
"description": "既存Webサイトにカスタム機能を注入するプラグインシステム",
"permissions": [
"storage",
"activeTab",
"scripting"
],
"host_permissions": [
"<all_urls>"
],
"background": {
"service_worker": "background.js"
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["sdk.js", "content-loader.js"],
"run_at": "document_idle"
}
],
"action": {
"default_popup": "popup/popup.html",
"default_icon": {
"16": "icons/icon16.png",
"48": "icons/icon48.png",
"128": "icons/icon128.png"
}
},
"icons": {
"16": "icons/icon16.png",
"48": "icons/icon48.png",
"128": "icons/icon128.png"
}
}
{
"plugins": [
{
"id": "example-plugin",
"name": "Example Plugin",
"version": "1.0.0",
"description": "...",
"author": "...",
"domains": ["example.com"],
"runAt": "document_idle",
"enabled": true,
"code": "// main.js の内容(文字列)",
"css": "/* style.css の内容(文字列)*/",
"config": {},
"installedAt": "2026-04-12T00:00:00Z",
"updatedAt": "2026-04-12T00:00:00Z"
}
],
"pluginData_example-plugin": {
"key1": "value1"
}
}
plugins — プラグイン一覧とそのコード・設定pluginData_{pluginId} — 各プラグインが WSI.storage で保存するデータページにフローティングボタンを追加する。
WSI.addButton({
text: "実行", // ボタンテキスト
icon: "▶", // アイコン文字(任意)
position: "bottom-right", // 表示位置: bottom-right / bottom-left / top-right / top-left
onClick: () => { /* 処理 */ } // クリック時のコールバック
});
ページにサイドパネルを追加する。
WSI.addPanel({
title: "パネルタイトル",
width: "300px", // パネル幅
position: "right", // 表示位置: right / left
content: "<div>HTML内容</div>", // パネル内のHTML
onOpen: () => {}, // 開いたときのコールバック
onClose: () => {} // 閉じたときのコールバック
});
プラグイン固有の永続ストレージ。
await WSI.storage.get("key"); // 値の取得
await WSI.storage.set("key", "value"); // 値の保存
await WSI.storage.remove("key"); // 値の削除
await WSI.storage.getAll(); // 全データ取得
デバッグ用ログ出力。ブラウザのコンソールに [WSI:プラグインID] プレフィックス付きで出力される。
WSI.log("処理を開始しました");
// コンソール出力: [WSI:example-plugin] 処理を開始しました
SPA等でのページ内遷移を検知して処理を実行する。
WSI.onPageLoad((url) => {
console.log("ページ遷移:", url);
});
plugin.json の config フィールドに定義された設定値を取得する。
const config = WSI.getConfig();
console.log(config.buttonPosition); // "bottom-right"
1. ユーザーがポップアップUIで「プラグインを追加」をクリック
2. ファイルピッカーが開き、ZIPファイルを選択
3. JSZipでZIPを展開
4. plugin.json を読み取り、バリデーション実行
- 必須フィールドの存在チェック
- id の形式チェック(英数字・ハイフンのみ)
- domains が1つ以上あるか
- main.js ファイルがZIP内に存在するか
5. バリデーション成功 → プレビュー表示
6. 既存の同一IDプラグインがあれば上書き確認
7. ユーザーが「インポート」を実行
8. plugin.json + main.js + style.css の内容を chrome.storage.local に保存
9. 完了メッセージを表示
1. ページ読み込み時に content-loader.js が実行される
2. chrome.storage.local から plugins 一覧を取得
3. 現在のドメイン(location.hostname)とプラグインの domains を照合
4. マッチ&有効なプラグインをフィルタリング
5. 各プラグインに対して:
a. WSI SDK のインスタンスを生成(プラグインIDをスコープとして紐づけ)
b. CSS があれば <style> タグとしてページに注入
c. main.js のコードを即時実行関数でラップし、WSI を引数として渡して実行
6. 実行ログを WSI.log 経由でコンソールに出力
chrome.storage.local の上限(約10MB)に依存するpluginData_{pluginId} で名前空間を分離するaddButton, storage, log, getConfigaddPanel, onPageLoad動作確認用の最小プラグイン。対象ドメインにフローティングボタンを表示する。
{
"id": "hello-world",
"name": "Hello World",
"version": "1.0.0",
"description": "動作確認用のサンプルプラグイン",
"author": "WSI Team",
"domains": ["example.com"],
"scripts": {
"main": "main.js",
"runAt": "document_idle"
},
"styles": [],
"config": {
"message": "Hello from WSI!"
}
}
const config = WSI.getConfig();
WSI.addButton({
text: "👋",
position: "bottom-right",
onClick: () => {
alert(config.message);
WSI.log("ボタンがクリックされました");
}
});
WSI.log("Hello World プラグインが読み込まれました");
このプロジェクトを Claude Code で実装する際の注意事項:
lib/ にバンドルする。CDN参照不可chrome://extensions での手動読み込みで行う