GoogleのJulesで、指示が入力途中で送信されてしまう問題をブラウザ拡張で解消する 

海外アプリ・サイトでよくあるEnterキーで漢字変換を確定させるときに送信されてしまうのを防ぐシリーズです。
5月31日に無料ユーザーでも1日に実行できるタスクが60個に拡張されて使いやすくなりましたが、この問題により使うのをやめたという投稿を見て、使ってもらえる人を増やすべく(減らさないべく?)記事を書きました。
私の作りたいもののレベルではGemini 2.5 Proで十分の性能があるのでたくさん使わせてもらってます。

Claude Sonnet 4に出力してもらったものをそのまま貼ります。

```javascript:content.js
// Jules Enter Invalidate Extension
// 漢字変換確定時のEnterキーによる誤送信を防ぐスクリプト

(function() {
    'use strict';

    console.log('Jules Enter Invalidate Extension loaded');

    // IME入力状態を追跡するフラグ
    let isComposing = false;
    let lastCompositionEnd = 0;

    // フォーム要素を監視し、イベントリスナーを追加する関数
    function attachListeners() {
        // テキストエリア、入力フィールド、編集可能要素を対象とする
        const selectors = [
            'textarea',
            'input[type="text"]',
            '[contenteditable="true"]',
            '[data-testid*="input"]',
            '[class*="input"]',
            '[class*="textarea"]',
            'form input',
            'form textarea'
        ];

        selectors.forEach(selector => {
            const elements = document.querySelectorAll(selector);
            elements.forEach(element => {
                if (!element.hasAttribute('data-jules-protected')) {
                    attachToElement(element);
                    element.setAttribute('data-jules-protected', 'true');
                }
            });
        });
    }

    // 個別の要素にイベントリスナーを追加
    function attachToElement(element) {
        // compositionstart: IME入力開始
        element.addEventListener('compositionstart', function(e) {
            isComposing = true;
            console.log('Composition started');
        }, true);

        // compositionend: IME入力終了（漢字変換確定）
        element.addEventListener('compositionend', function(e) {
            isComposing = false;
            lastCompositionEnd = Date.now();
            console.log('Composition ended');
        }, true);

        // keydown: キーが押された時
        element.addEventListener('keydown', function(e) {
            if (e.key === 'Enter' || e.keyCode === 13) {
                // IME入力中の場合、Enterキーをブロック
                if (isComposing) {
                    console.log('Blocked Enter during composition');
                    e.stopPropagation();
                    e.stopImmediatePropagation();
                    return false;
                }

                // 変換確定直後（100ms以内）のEnterキーもブロック
                const timeSinceComposition = Date.now() - lastCompositionEnd;
                if (timeSinceComposition < 100) {
                    console.log('Blocked Enter immediately after composition');
                    e.stopPropagation();
                    e.stopImmediatePropagation();
                    return false;
                }
            }
        }, true);

        // keypress: より確実にキャッチするため
        element.addEventListener('keypress', function(e) {
            if (e.key === 'Enter' || e.keyCode === 13) {
                if (isComposing) {
                    console.log('Blocked Enter (keypress) during composition');
                    e.preventDefault();
                    e.stopPropagation();
                    e.stopImmediatePropagation();
                    return false;
                }

                const timeSinceComposition = Date.now() - lastCompositionEnd;
                if (timeSinceComposition < 100) {
                    console.log('Blocked Enter (keypress) immediately after composition');
                    e.preventDefault();
                    e.stopPropagation();
                    e.stopImmediatePropagation();
                    return false;
                }
            }
        }, true);
    }

    // DOMの変更を監視して新しく追加された要素にもリスナーを追加
    function observeDOM() {
        const observer = new MutationObserver(function(mutations) {
            let shouldAttach = false;
            mutations.forEach(function(mutation) {
                if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
                    mutation.addedNodes.forEach(function(node) {
                        if (node.nodeType === Node.ELEMENT_NODE) {
                            shouldAttach = true;
                        }
                    });
                }
            });
            
            if (shouldAttach) {
                setTimeout(attachListeners, 100);
            }
        });

        observer.observe(document.body, {
            childList: true,
            subtree: true
        });
    }

    // 初期化
    function init() {
        console.log('Initializing Jules Enter Invalidate Extension');
        
        // 既存の要素にリスナーを追加
        attachListeners();
        
        // DOM変更の監視を開始
        observeDOM();
        
        // 定期的に新しい要素をチェック（念のため）
        setInterval(attachListeners, 2000);
    }

    // DOMが読み込まれたら初期化
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }

    // ページの読み込み完了後にも再度実行
    window.addEventListener('load', function() {
        setTimeout(init, 1000);
    });

})();
```

```json:manifest.json
{
  "manifest_version": 3,
  "name": "Jules Enter Invalidate",
  "version": "1.0",
  "description": "Julesで漢字変換確定時のEnterキー送信を防ぐ拡張機能",
  "permissions": [
    "activeTab"
  ],
  "content_scripts": [
    {
      "matches": ["https://jules.google.com/*"],
      "js": ["content.js"],
      "run_at": "document_end"
    }
  ]
}
```

ブラウザ拡張機能の詳細については省略。フォルダの中に上記2つのスクリプトを入れてブラウザの拡張機能で読み込ませるだけです。

ぱっと見他のサイトとかでもそのまま使えるのかな