最近、社内で記事の共有などをする機会が増え、ブラウザのブックマークやGoogle Keepを使用していたが、仕事とプライベートで使用しているブラウザ、Googleアカウントなどが違ったりして、どこに保存したか分からなくなることが多かったので、自作でchrome拡張を作成して運用してみることにした。
まだ作成途中だが、結構簡単に作れたので記事にしてみた。
リポジトリ
作成したもの
- URLを保存できる
- URLの追加/削除が簡単にできる
- 簡単にURLをMarkdown形式でコピーできる
- 右クリックで追加/削除できる
使用技術
Plasmoを使用して開発
- ブラウザ拡張機能を作成するためのReactフレームワーク
- TypeScript & Reactで簡単にChrome拡張が作成できる
- 従来のChrome拡張で必要だった設定周りフレームワーク側で抽象化されている
コードの例
以下のコマンドでプロジェクトを作成
$ pnpm create plasmo
プロジェクトの作成時は以下のようなコードが生成される
import { useState } from "react" function IndexPopup() { const [data, setData] = useState("") return ( <div> <h2> Welcome to your{" "} <a href="https://www.plasmo.com" target="_blank"> Plasmo </a>{" "} Extension! </h2> <input onChange={(e) => setData(e.target.value)} value={data} /> <a href="https://docs.plasmo.com" target="_blank"> View Docs </a> </div> ) } export default IndexPopup
上記のコードで、こんな感じのchrome拡張を作成できる
実装
Plasmoを作ってブラウザ拡張を作っていく。
まず、UIはさくっと作るためにTailwind CSSを利用。PlasmoでTailwind CSSを使用するには以下で参考に実装。
Plasmoで現在ブラウザで開いているURLの取得は以下のコードで取得できる。
const [currentPage, setCurrentPage] = useState<Data>({ title: "", url: "", favIconUrl: "" }) useEffect(() => { chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => { const activeTab = tabs[0] if (!activeTab) return setCurrentPage({ title: activeTab.title, url: activeTab.url, favIconUrl: activeTab.favIconUrl }) }, [items, removeButton])
ここで取得した値をPlasmoのStorage APIを使って保存できる。コードは、以下の通り。
import { useStorage } from "@plasmohq/storage/hook" (...略) const [items, setItems] = useStorage<Data[]>("saveItems", [])
これで以下の通りUIを整えれば、まずは最小限な実装で以下みたいにブラウザ拡張が作れます。
次に右クリックのContextMenuに追加するには、Background Service Workerを利用。
コードは以下のように追加。
chrome.runtime.onInstalled.addListener(() => { chrome.contextMenus.create({ id: "save", title: "ページを追加/削除", contexts: ["all"], }); });
ContextMenuをクリックした時の動作追加のコードはは以下の通り
chrome.contextMenus.onClicked.addListener(async (info) => { if (info.menuItemId === "save") { const activeTab = await storage.get<Data>("activeTab"); if (!activeTab) return; const items = (await storage.get<Data[]>("saveItems")) ?? []; const isCurrentPageURL = items.some((v) => v.url === activeTab.url); if (isCurrentPageURL) { const newItems = items.filter((v) => v.url !== activeTab.url); await storage.set("saveItems", newItems); } else { items.push(activeTab); await storage.set("saveItems", items); } chrome.runtime.sendMessage({ type: "UPDATE", }); } });
後は、backgroundで現在の開いているURLを取得するのは以下のコードで実装可能
function getActiveTabInfo() { const queryInfo = { active: true, currentWindow: true, }; chrome.tabs.query(queryInfo, (tabs) => { const activeTab = tabs[0]; console.log("Active Tab URL:", activeTab.url); if (!activeTab) return; storage.set("activeTab", activeTab); }); } // アクティブタブが変更されたときのイベントリスナー chrome.tabs.onActivated.addListener((activeInfo) => { getActiveTabInfo(); }); // タブが更新されたときのイベントリスナー chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => { // タブが完全に読み込まれたときに情報を取得 if (changeInfo.status === "complete") { getActiveTabInfo(); } }); // 初回起動時にもタブ情報を取得 getActiveTabInfo();
ここまで実装すると以下の動画みたいに右クリックからURLの保存が実装できる。