Ccmmutty logo
Commutty IT
0 pv8 min read

コードがほとんど書けない自分でも、Geminiのデフォルトを「Pro」に固定するChrome拡張を作れた話

https://cdn.magicode.io/media/notebox/e8e6da49-784e-4a05-85da-24385d3e336d.jpeg
日常業務の調査や文章作成、ちょっとした整理に、Googleの「Gemini」を活用している方は多いのではないでしょうか。
私自身も会社のGoogle Workspaceアカウントが利用できるため、かなり活用しています。
ただ、ひとつだけ地味に気になっていたことがありました。
それは、新しいチャットやGemを開くたびに、デフォルトが「高速モード」になっていて、毎回手動で「Pro」に切り替えないといけないことです。
ほんの数秒の話なのですが、毎日何度も触っていると、じわじわ面倒に感じます。
そこで今回、この手間をなくすために Geminiを開いたら自動で「Pro」に切り替えるChrome拡張機能 を作ってみました。
……と書くと少し格好よく見えるかもしれませんが、正直に言うと、私はコードをほとんど書けません。
JavaScriptなんて一度も書いたことがありません。
それでも、生成AIに相談しながら少しずつ組み立てていったら、最終的にはちゃんと動くものができました。
今回はその過程も含めて、知見として共有したいと思います。

きっかけは、毎回の「Pro切り替え」が面倒だったこと

最初に考えたのはとても単純で、
「Geminiを開いたタイミングで“高速モード”のボタンを見つけてクリックし、そのあと“Pro”をクリックすればいいのでは?」
というものでした。
実際、その方向で生成AIに相談しながらスクリプトを作ってみたのですが、最初はうまくいったり、いかなかったりでした。

最初の壁:Geminiは普通のWebページではなかった

試してみてわかったのは、Geminiは一般的な「ページを開き直すたびに全部読み込み直すサイト」ではなく、SPA(Single Page Application) 的に動いているということでした。
つまり、左メニューから新しいチャットやGemに移動しても、ページ全体が再読み込みされるわけではなく、画面の一部だけが動的に差し替わっているような挙動です。
そのため、最初に考えたような
「ページ読み込み時に1回だけ処理を走らせる」
というやり方だと、最初は動いても、その後の画面切り替えには追従できませんでした。
このあたりは自分ひとりでは気づきにくく、生成AIとのやり取りの中で「そういうことか」と理解できた部分でした。

解決策:DOMの変化を監視するようにした

最終的には、ブラウザ標準APIの MutationObserver を使って、画面のDOM変化を監視する形にしました。
要するに、
  • 画面の要素が切り替わったら検知する
  • 「高速モード」の表示があればクリックする
  • メニューが開いたら「Pro」を探してクリックする
という流れです。
また、DOMの変化は短時間に何度も発生することがあるため、setTimeout で少し待ってまとめて処理する、いわゆるデバウンスも入れています。
このあたりも、完全に自力で考えたというより、生成AIに相談しながら「重くなりにくい方法」に寄せていったというのが実際のところです。

実際のコード

必要なファイルは manifest.jsoncontent.js の2つだけです。

1. manifest.json

{
  "manifest_version": 3,
  "name": "Gemini Auto Pro",
  "version": "1.0",
  "description": "Geminiを開いたとき、自動的にProモデルに切り替えます。",
  "content_scripts": [
    {
      "matches": ["https://gemini.google.com/*"],
      "js": ["content.js"],
      "run_at": "document_idle"
    }
  ]
}

2. content.js

// content.js
// 画面の変化を監視して、Geminiのモデルを自動でProに切り替える

(function() {
    'use strict';

    // === 設定 ===
    const CURRENT_MODEL_TEXT = '高速モード'; 
    const TARGET_MODEL_TEXT = 'Pro'; 
    // ============

    let lastUrl = location.href;
    let isSwitchedForCurrentUrl = false;
    let isMenuOpening = false;
    let debounceTimer = null;

    // 指定したテキストを持つ「ボタンらしい要素」だけを効率よく探す関数
    function findElementByText(text) {
        // 検索対象をボタンやメニューの項目だけに絞り込む(処理を軽くするため)
        const selectors = 'button, [role="button"], [role="menuitem"], [role="option"], [role="combobox"], li';
        const elements = document.querySelectorAll(selectors);
        
        for (const el of elements) {
            // 画面に表示されていない要素や、リンク(<a>タグ)の中身は無視
            if (el.offsetWidth === 0 || el.offsetHeight === 0 || el.closest('a')) {
                continue;
            }

            const textContent = el.textContent.trim();
            
            // テキストが含まれており、かつ長すぎない(チャット本文などの誤認防止)
            if (textContent.includes(text) && textContent.length < 50) {
                return el;
            }
        }
        return null;
    }

    // 画面に変化があった時に実行されるメインロジック
    function checkAndSwitchModel() {
        // 1. URLが変わったかチェック(Gem移動や新規チャット時にフラグをリセット)
        if (lastUrl !== location.href) {
            lastUrl = location.href;
            isSwitchedForCurrentUrl = false;
            isMenuOpening = false;
        }

        // すでに現在のチャットで切り替え済みの場合は何もしない
        if (isSwitchedForCurrentUrl) return;

        // 2. メニューを開こうとしている最中の場合
        if (isMenuOpening) {
            const targetOption = findElementByText(TARGET_MODEL_TEXT);
            if (targetOption) {
                targetOption.click(); // Proをクリック
                isSwitchedForCurrentUrl = true; // 切り替え完了!
                isMenuOpening = false;
            }
            return; // メニューが現れるのを待っている状態なので、ここで一旦終了
        }

        // 3. まだ切り替えていない場合:「高速モード」ボタンを探す
        const currentModelBtn = findElementByText(CURRENT_MODEL_TEXT);
        if (currentModelBtn) {
            currentModelBtn.click(); // メニューを開く
            isMenuOpening = true;    // メニュー展開待ち状態にする
            return;
        }

        // 高速モードが見当たらず、すでに「Pro」が選択されているかチェック
        const targetModelBtn = findElementByText(TARGET_MODEL_TEXT);
        if (targetModelBtn) {
            // すでにProの表記があれば、この画面では完了扱いにする
            isSwitchedForCurrentUrl = true;
        }
    }

    // 「画面のどこかが書き換わった」ことを検知する高性能なセンサー(MutationObserver)
    const observer = new MutationObserver(() => {
        // 変更があるたびに連続で実行すると重いため、100ミリ秒だけ待ってから1回だけ実行する(デバウンス処理)
        if (debounceTimer) clearTimeout(debounceTimer);
        debounceTimer = setTimeout(() => {
            checkAndSwitchModel();
        }, 100);
    });

    // ページ全体の変化を監視開始
    observer.observe(document.body, {
        childList: true,       // 要素が追加・削除されたか
        subtree: true,         // 孫要素以下の変化も見るか
        characterData: true    // テキストが書き換わったか
    });

    // 初回読み込み時にも1回実行しておく
    checkAndSwitchModel();

})();

導入方法

  1. ローカルの任意の場所にフォルダ(例: GeminiAutoPro)を作成します
  2. 上記の2つのファイルを保存します
  3. Chromeで chrome://extensions/ を開きます
  4. 右上の「デベロッパー モード」をオンにします
  5. 「パッケージ化されていない拡張機能を読み込む」をクリックし、作成したフォルダを選択します
これで導入完了です。
Geminiを開いたときに、自動でモデル切り替えが走るようになります。
毎回手で切り替えていた人にとっては、思った以上に快適です。

作ってみて感じたこと

今回いちばん大きかったのは、「自分はコードが得意ではないから無理」と決めつけなくても、生成AIを使えば業務上の小さな不便を解消できることがあると実感できたことです。
もちろん、今回のコードもほぼ自力で書いていません。
でも、それで十分だと思っています。
大事なのは「全部を理解して書けること」よりも、困りごとに気付いて、AIと対話しながら解決方法を相談していくことなのかもしれません。
また、今回のように試してみると、SPAやDOM監視のような、自分ひとりならなかなか触れなかった考え方にも自然に触れられました。
単に便利なものができただけでなく、学びとしてもかなり面白かったです。

おわりに

「毎回ちょっと面倒だな」と感じることは、意外と放置しがちです。
でも、その小さな不便こそ、生成AIを使って改善する題材としてはちょうどいいのかもしれません。
コードが得意ではない自分でも、今回のように実用的な小さな改善を形にすることができました。
同じように「自分はあまりコードを書けない」と感じている方にとっても、何かひとつ試してみるきっかけになれば嬉しいです。
※ UIの文言やDOM構造が変わると動かなくなる可能性があります。記事執筆時点の内容としてご覧ください。
※ 非公式な方法のため、利用は自己責任でお願いします。

Discussion

コメントにはログインが必要です。