📌 AI関連

Chromeサイドパネルにブックマークレットを集約する拡張機能の作り方【Manifest V3】

Chromeサイドパネルにブックマークレットのボタンを集約したWeb Toolboxのイメージ

Web制作やサイト運用をしていると、URLコピー、alt属性確認、見出し構造の可視化、OGP確認など、短いJavaScriptを実行する「ブックマークレット」が少しずつ増えていきます。便利な一方で、ブックマークバーが埋まり、目的のツールを探す時間が無視できなくなっていました。

そこで今回は、ブックマークレットをChromeのサイドパネルへ集約する自作拡張機能「Web Toolbox」を、Manifest V3とVanilla JavaScriptで制作しました。固定位置のボタンから現在のページへコードを実行でき、各ボタンの割り当ても画面上で変更できます。

この記事では完成画面の紹介だけでなく、なぜSide Panel APIとUser Scripts APIを採用したのか、任意コードをどう保存するのか、どこでつまずきやすいのかまで、実装の流れに沿って解説します。npmやフレームワークは使わないため、Chrome拡張機能を初めて作る人にも再現しやすい構成です。

この記事から持ち帰れること

  • Chrome Side Panelを使った業務ツールの基本構成
  • Manifest V3で任意のブックマークレットを実行する方法
  • ツール定義とUIを分離して保守しやすくする設計
  • Chrome Storageへボタンごとの設定を保存する方法
  • 狭いサイドパネルでも操作速度を落とさないUI設計

ブックマークレット管理で困っていたこと

Web制作の確認作業は細かく、しかも繰り返し発生します。たとえば画像のalt属性を確認した直後に見出し構造を見て、次にcanonicalやnoindexを調べる、といった流れです。AEMの運用では、Page PathやDAM、Author/Publish環境への移動など、案件固有の操作も加わります。

従来はChromeのブックマークバーにブックマークレットを並べていましたが、数が増えると次の問題が起きました。

  • 通常のブックマークと業務ツールが混在する
  • 名前だけでは処理内容を思い出しにくい
  • フォルダに入れると実行までのクリック数が増える
  • ツールの位置が変わると、毎回探し直すことになる
  • ページを書き換えたあと、元へ戻す方法がツールごとに異なる

必要だったのは高機能な管理システムではなく、「いつも同じ場所にあるボタンを押せば、現在のページで処理が動く」という小さなランチャーでした。

完成したWeb Toolboxの機能

Web Toolboxは、Chromeのサイドパネル内を2つのエリアに分けています。

1. DAILY TOOLS

上段には、毎日使うツールを最大16個置ける固定グリッドを用意しました。初期状態ではURL COPY、TITLE COPY、ALT VIEW、HEADINGS、IMAGES、LINKS、OGP、CANONICALなどを登録しています。

重要なのは、自動で並べ替えないことです。業務ツールでは、使用頻度順に動く賢さよりも「左列の上から3番目」と位置で覚えられることのほうが、操作速度につながります。

2. PAGE CHECK

下段はBASIC、AEM、RELEASEの3タブです。一般的なページチェック、AEM固有作業、公開前確認を分けました。最後に開いたタブはChrome Storageへ保存され、次回も同じタブから始まります。

3. RESETとActive表示

ブックマークレットは、outlineの追加、DOMの挿入、classの変更など、ページへさまざまな影響を与えます。Version 0.2では個別の巻き戻し処理を作らず、RESETで現在のタブを再読み込みする方式にしました。実行したボタンにはチェックを付け、最後に動かしたツール名も表示します。

4. 画面から任意のコードを割り当て

「割り当てを編集」を押すと、すべてのボタンが編集モードになります。ボタン名、説明、ブックマークレット、有効/無効を入力して保存でき、javascript:付きのコードもそのまま貼り付けられます。編集モード中と保存時にはコードを実行しないため、保存内容を確認してから通常モードへ戻せます。

技術構成:フレームワークなしのManifest V3

構成はHTML、CSS、JavaScriptだけです。ビルド工程を設けず、「パッケージ化されていない拡張機能を読み込む」からすぐ起動できることを優先しました。

web-toolbox/
├─ manifest.json
├─ background.js
├─ sidepanel/
│  ├─ index.html
│  ├─ app.js
│  └─ style.css
├─ data/
│  ├─ daily-tools.js
│  ├─ basic-tools.js
│  ├─ aem-tools.js
│  ├─ release-tools.js
│  └─ tools.js
├─ services/
│  ├─ tool-config.js
│  ├─ tool-runner.js
│  └─ storage.js
└─ utils/
   ├─ clipboard.js
   └─ helpers.js

UI、ツール定義、実行処理、保存処理を分離しています。後からBookmarklet ManagerやJSON Import/Exportを追加するときも、画面全体を書き直さずに済む構成です。

STEP 1:manifest.jsonでSide Panelを登録する

ChromeのSide Panel APIを使うには、Manifest V3でsidePanel権限とside_panel.default_pathを指定します。Side Panel API自体はChrome 114以降で利用できますが、今回使うchrome.userScripts.execute()がChrome 135以降のため、拡張全体の最小バージョンを135にしています。

{
  "manifest_version": 3,
  "name": "Web Toolbox",
  "version": "0.2.0",
  "minimum_chrome_version": "135",
  "permissions": [
    "sidePanel",
    "storage",
    "userScripts"
  ],
  "host_permissions": [
    "http://*/*",
    "https://*/*"
  ],
  "background": {
    "service_worker": "background.js"
  },
  "action": {
    "default_title": "Web Toolboxを開く"
  },
  "side_panel": {
    "default_path": "sidepanel/index.html"
  }
}

権限は「とりあえず全部」ではなく、用途から逆算します。今回は任意のHTTP/HTTPSページでツールを動かすランチャーなので、2つのURLパターンをHost Permissionとして明示しました。Chrome公式も、必要な権限を限定し、可能な場合はoptional permissionを検討するよう案内しています。

参考:chrome.sidePanel API(Chrome for Developers)/Declare permissions(Chrome for Developers)

STEP 2:ツールバーアイコンからパネルを開く

service workerでは、ツールバーの拡張アイコンを押したときにサイドパネルを開く設定を行います。

const enableActionClick = () =>
  chrome.sidePanel.setPanelBehavior({
    openPanelOnActionClick: true
  });

chrome.runtime.onInstalled.addListener(enableActionClick);
chrome.runtime.onStartup.addListener(enableActionClick);

ポップアップと違い、サイドパネルはページを見ながら操作でき、別タブへ移動してもツールパレットを残せます。ページチェックのように「対象ページ」と「操作UI」を同時に見たい用途と相性が良い仕組みです。

STEP 3:ボタン定義をHTMLから分離する

各ボタンをHTMLへ直書きすると、ツールを追加するたびに画面コードを編集することになります。そこでツールを、JSON化しやすいJavaScriptオブジェクトとして管理しました。

{
  id: "daily-url-copy",
  label: "URL COPY",
  title: "現在のURLをコピー",
  description: "表示中ページのURLをコピーします。",
  category: "daily",
  order: 1,
  enabled: true,
  code: `javascript:window.__webToolbox.copyText(location.href);`
}

idは保存設定との紐付け、orderは固定位置、enabledは未設定枠の無効化に使います。UI側は配列を読み取ってボタンを生成するだけなので、初期ツールを差し替えてもレイアウトロジックは変わりません。

STEP 4:任意コードはUser Scripts APIで実行する

今回の実装で最も重要なのが、ユーザーが入力したJavaScript文字列の扱いです。chrome.scripting.executeScript()は拡張に含まれるファイルやシリアライズ可能な関数の実行には向いていますが、任意のコード文字列を直接実行するAPIではありません。

Chrome公式は、ユーザーが用意した任意コードを扱う拡張にはUser Scripts APIが必要だと説明しています。Web Toolboxではeval()をサイドパネルに置かず、次のようにchrome.userScripts.execute()へコードを渡しています。

const code = stripJavascriptProtocol(tool.code);

const results = await chrome.userScripts.execute({
  target: { tabId: tab.id },
  world: "MAIN",
  injectImmediately: true,
  js: [
    { code: BOOKMARKLET_HELPERS_SOURCE },
    { code }
  ]
});

javascript:は登録時に付いていてもよく、実行直前に除去します。また、多くの既存ブックマークレットとの互換性を優先してMAIN worldを選びました。これはページ本体と同じJavaScript環境を使うため互換性が高い一方、ページ側から干渉される可能性があります。信頼できる自作コードだけを登録する前提です。

chrome.userScripts.execute()はChrome 135以降です。さらにChrome 138以降では、拡張機能の詳細画面にある「ユーザー スクリプトを許可」を個別にONにする必要があります。135〜137ではデベロッパーモードが許可スイッチを兼ねます。

参考:chrome.userScripts API(Chrome for Developers)/Chrome 138のUser Scripts許可設定変更

STEP 5:任意のブックマークレットを画面から割り当てる

初期版ではコードファイルを書き換えてツールを追加していました。しかし、実際に日常利用するなら「新しいブックマークレットを入れるたびに拡張を編集して再読み込み」は負担です。そこでVersion 0.2では、各ボタンに編集ダイアログを追加しました。

  • ボタン名:40文字まで
  • 説明:200文字まで
  • コード:200,000文字まで
  • 有効/無効:コードが未登録の枠も位置を残せる
  • 初期値へ戻す:変更をフォームへ読み込み、保存時に確定

入力値はそのまま保存せず、許可した4フィールドだけを検証して取り出します。ID、カテゴリ、表示順はユーザー入力で変更できないようにし、固定配置が崩れるのを防ぎました。

export function validateAssignment(value) {
  const label = value.label.trim();
  const description = value.description.trim();
  const code = value.code;
  const enabled = value.enabled === true;

  if (!label) throw new Error("ボタン名を入力してください。");
  if (enabled && !stripJavascriptProtocol(code)) {
    throw new Error("ブックマークレットを貼り付けてください。");
  }

  return { label, description, code, enabled };
}

STEP 6:ボタン単位でChrome Storageへ保存する

カスタム設定はchrome.storage.localへ保存します。すべての設定を1つの巨大なオブジェクトへまとめず、toolOverride:daily-url-copyのようにボタン単位のキーへ分けました。

async saveToolOverride(toolId, assignment) {
  await chrome.storage.local.set({
    ["toolOverride:" + toolId]: assignment
  });
}

この形なら、複数ウィンドウで別々のボタンを編集しても、設定オブジェクト全体を上書きしにくくなります。同じボタンを同時に保存した場合だけ最後の保存を優先する、分かりやすい挙動です。

保存設定は初期ツールへ上書き合成します。ただし保存データが壊れていた場合は、そのボタンだけ初期値へフォールバックさせます。1件の不正データでサイドパネル全体が開かなくなるのを避けるためです。

業務ツールとして使いやすくするUIの工夫

ボタンの高さと位置を固定する

ボタン名が長くても高さを44pxに固定し、1行で省略表示します。全文はツールチップで確認できます。使用頻度による自動ソートは行いません。

編集と実行を明確に分ける

編集モード中はすべてのボタンに鉛筆マークを出し、RESETも無効にします。保存ボタンを押してもコードは動かず、「編集を終了」してから初めて実行できます。設定作業中の誤実行を避けるためです。

狭い幅とキーボード操作に対応する

通常は2列、幅260px以下では1列に切り替えます。編集ダイアログはパネル内で縦スクロールでき、TabキーやEscでも操作できます。BASIC/AEM/RELEASEタブは左右キーでも移動可能です。

実装後に行ったテスト

「画面が表示できた」で終わらせず、設定値と実行コードの受け渡しを自動テストしました。今回用意した9テストでは、次を確認しています。

  • javascript:付きコードと改行・引用符を保持できる
  • 空のボタン名、空コード、文字数超過を拒否する
  • 46個すべての枠を独立して上書きできる
  • ID、カテゴリ、表示順を上書きできない
  • 保存失敗時に以前の設定を壊さない
  • RESET後もボタンの割り当ては残る
  • 現在のタブとMAIN worldへ、保存したコードだけが渡る
  • Chrome内部ページと実行エラーを通知できる

さらにChrome APIをモックしたローカル画面で、保存、再表示、キャンセル、保存失敗時の入力保持、初期値復元、RESET、240px/340px幅を操作確認しました。任意コードを扱うツールでは、成功ケースだけでなく「保存できなかったときに入力が消えないか」も重要です。

Chromeへインストールして使う手順

  1. Chromeでchrome://extensions/を開く
  2. デベロッパーモードをONにする
  3. 「パッケージ化されていない拡張機能を読み込む」を押す
  4. web-toolboxフォルダを選択する
  5. Chrome 138以降では、拡張機能の詳細から「ユーザー スクリプトを許可」をONにする
  6. ツールバーのWeb Toolboxアイコンを押してサイドパネルを開く

通常のHTTP/HTTPSページを開き、まずはHEADINGSやIMAGESを実行すると動作を確認しやすいです。chrome://、Chrome Web Store、拡張機能ページなど、Chromeがスクリプト注入を禁止するページでは実行できません。

制作を通して分かった5つのこと

1. 業務ツールは機能数より到達速度

毎日使う道具では、検索や自動整理より「同じ場所にある」ことが効きます。賢い並べ替えを入れない判断も、UX設計の一部です。

2. 任意コードの実行方式を最初に決める

UIから作り始めると、最後にManifest V3の制約へぶつかります。コード文字列を扱う必要があるなら、対応Chromeバージョン、User Scripts権限、実行worldを先に決めると手戻りを減らせます。

3. 初期値とユーザー設定を分ける

配布時のおすすめ設定はコードファイル、個人の割り当てはStorageへ置くと、アップデートとカスタマイズを両立できます。

4. 無効な枠も削除しない

AEMのように案件ごとに内容が変わるツールは、未設定でもボタン枠を残すと位置が安定します。あとからコードを割り当てる入口にもなります。

5. RESETは単純なほうが壊れにくい

すべてのブックマークレットに個別の復元処理を要求すると、追加のハードルが上がります。初期版では再読み込みに統一し、確実に元へ戻せることを優先しました。

よくある質問

Reactやnpmは必要ですか?

必要ありません。今回の構成はHTML、CSS、JavaScriptのみで、ビルドせずに読み込めます。管理画面が大規模になった段階でフレームワークを検討しても遅くありません。

既存のブックマークレットをそのまま貼れますか?

javascript:付きでも付いていなくても登録できます。ただし、ページのCSP、ブラウザ仕様、依存する外部ライブラリなどによっては動かないコードもあります。

登録した設定はどこに保存されますか?

chrome.storage.localです。Chromeやサイドパネルを閉じても残りますが、拡張機能をアンインストールすると削除されます。クラウド同期はVersion 0.2では行いません。

複数のツールを続けて実行できますか?

実行できます。ただしActive表示は最後に実行した1件だけです。DOM変更が競合した場合はRESETでページを再読み込みします。

Chrome内部ページで動かないのは不具合ですか?

不具合ではありません。chrome://やChrome Web Storeなど、拡張機能からスクリプトを注入できないページがあります。Web Toolbox側でもHTTP/HTTPS以外は実行前に止めています。

この記事の検証範囲と更新時の確認ポイント

この記事は、実際に制作したWeb Toolbox Version 0.2のソースコードとテスト結果をもとに、2026年8月29日時点のChrome公式ドキュメントを照合して執筆しています。サンプルコードは説明のため一部を短くしています。完全な実装では、エラー通知、文字数検証、保存失敗時の入力保持、壊れた設定のフォールバックも加えています。

Chrome拡張機能のAPIは更新されるため、実際に制作するときは、chrome.userScripts.execute()の対応バージョン、「ユーザー スクリプトを許可」の操作、Manifest権限をChrome for Developersで再確認してください。記事を更新する場合も、コードの動作確認日とChromeバージョンを明記すると、読者が情報の鮮度を判断しやすくなります。

まとめ:自分専用のチェック環境は小さく始められる

Web Toolboxは、たくさんの機能を作ることより、既存のブックマークレットへ最短で到達することを目標にしたChrome拡張機能です。

  • Side Panelで対象ページを見ながら操作する
  • ボタン位置を固定し、探す時間を減らす
  • User Scripts APIでユーザー定義コードを扱う
  • 初期値と個人設定を分けて保守する
  • RESETは再読み込みに統一して追加コストを下げる

まずはURLコピーと、普段最もよく使う1つのチェック処理だけでも十分です。毎日繰り返す小さな操作を固定ボタンへ移すと、自分の制作フローに合ったツールパレットへ少しずつ育てられます。

※当サイトはアフィリエイト広告を利用しています。商品の価格・仕様は変更になる場合があります。