1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

生成AIとの対話でChrome拡張機能「MultiSearch」を開発した全記録

Posted at

生成AIとの対話でChrome拡張機能「MultiSearch」を開発した全記録

はじめに

本記事は、Claude Code Sonnet 4.5(Anthropic社の生成AI)との実際の対話ログをもとに、Chrome拡張機能を開発した過程を記録したものです。

作ろうとしたChrome拡張の概要

解決したかった課題

社内の座席表(ExcelファイルをWebページとして表示したもの)で、複数の社員名を同時に検索したい

座席表の特徴:

  • ExcelベースのHTML(主にテーブル構造)
  • framesetを使用している
  • 社内ネットワークからのみアクセス可能

既存のブラウザ検索機能では1つのキーワードしか検索できず、複数人を探す際に不便でした。
また、既存の拡張機能もありましたが、勉強がてら自分で作ろうと思いました。

当初の要件

ChatGPTで開発を進めていたが、途中からClaudeに切り替えることにしました。ChatGPTに作成してもらった仕様書を提示し、以下の機能を実装する方向で開始:

  • 複数キーワードの同時検索(改行区切り)
  • 検索結果のハイライト表示
  • Chrome標準検索のようなEnterキーでのジャンプ機能

生成AIとのやりとりの進め方

最初の相談

ChatGPTが生成した仕様書を提示し、「コードのたたき台を作ってほしい」と依頼しました。この時点で以下を明確に伝えました:

  • Chrome拡張機能は初めて作る
  • JavaScriptは少し書けるが初心者レベル
  • 難しい表現やコードは避けてほしい

開発の進め方

段階的に機能を追加していく方式を採用:

  1. まず基本的な検索機能を実装
  2. 動作確認
  3. 問題が発生したら報告
  4. 修正・機能追加

このサイクルを繰り返しました。

詰まったポイント・試行錯誤

1. 座席表で検索が動かない問題

発生した問題:

  • Wikipediaなどの一般的なWebページでは動作
  • 座席表では「明らかに存在するキーワードがヒットしない」

デバッグの流れ:

Claudeから「F12でコンソールを開いてログを確認してください」と指示があり、以下が判明:

テキストノード数: 1
マッチ件数: 0

通常は数百〜数千のテキストノードが検出されるはずが、1つしか見つかっていませんでした。

原因究明:

Claudeの提案で座席表のHTML構造を確認:

  • <iframe> タグの有無を確認 → なし
  • <frameset> タグの有無を確認 → あり

解決方法:

manifest.jsonに以下を追加:

"host_permissions": [
  "<all_urls>"
]

検索実行時に allFrames: true オプションを指定:

chrome.scripting.executeScript({
  target: { tabId: currentTabId, allFrames: true },
  func: highlightText,
  args: [input]
});

これでframesetを含むすべてのフレームで検索が実行されるようになりました。

2. 完全一致モードが機能しない

発生した問題:

完全一致モードで「F69-1」を検索すると、「F69-10」「F69-11」「F69-12」もヒットしてしまう。

原因:

正規表現の単語境界 \b を使用していましたが、数字とハイフンの組み合わせでは正しく動作しませんでした。

// 問題のあるコード
return '\\b' + escaped + '\\b';

解決方法:

Lookahead/Lookbehindを使用した正規表現に変更:

return '(?<!\\S)' + escaped + '(?!\\S)';

これにより、前後に空白以外の文字がない場合のみマッチするようになりました。

3. 再検索時に前の結果が残る問題

発生した問題:

検索キーワードを変更して再検索しても、前回の検索結果が残ったままでした。

原因:

ハイライト関数内でクリア処理を行っていましたが、すでにspanで囲まれたテキストノードが取得できず、完全にクリアされていませんでした。

解決方法:

検索ボタンをクリックしたら、まず完全にクリアしてから新しい検索を実行するように変更:

// まずクリア
chrome.scripting.executeScript({
  target: { tabId: currentTabId, allFrames: true },
  func: clearHighlightInPage,
  args: [HIGHLIGHT_CLASS, HIGHLIGHT_STYLE_ID]
}, function() {
  // クリア後に検索実行
  chrome.scripting.executeScript({
    target: { tabId: currentTabId, allFrames: true },
    func: highlightTextInPage,
    args: [input, exactMatchMode, ...]
  });
});

4. リファクタリング後の構文エラー

発生した問題:

コードをモジュール化・オブジェクト化したところ、以下のエラーが発生:

TypeError: this.processSearchResults is not a function
Uncaught SyntaxError: Unexpected token '{'

原因:

  1. オブジェクトメソッド内で this のコンテキストが失われていた
  2. chrome.scripting.executeScript にオブジェクトのメソッド(シリアライズできない)を渡そうとしていた

解決方法:

「安定していたバージョンに戻してリファクタリングをやり直してください」と依頼し、以下の方針で修正:

  • オブジェクトメソッドではなく、独立した関数として定義
  • ES5互換の記法で記述
  • コンテンツスクリプトは必ず独立した関数にする
// NG: オブジェクトのメソッド
const contentScripts = {
  clearHighlight(highlightClass, styleId) { ... }
};

// OK: 独立した関数
function clearHighlightInPage(highlightClass, styleId) { ... }

機能追加の過程

初期実装から追加した機能

開発中に以下の機能を段階的に追加:

  1. スペース無視機能

    • 「山田 太郎」で検索 → 「山田太郎」「山田 太郎」もヒット
    • 正規表現でスペースを \s* に置換
  2. 検索結果のループナビゲーション

    • 最後まで行ったら最初に戻る
    • Shift+Enterで逆方向にも移動可能
  3. ポップアップ内への結果表示

    • アラートではなくポップアップ内に表示
    • キーワードごとの件数を表示
  4. 検索状態の保存・復元

    • タブごとに検索状態を保持
    • ポップアップを閉じても前回の結果を復元
  5. サイドパネル対応

    • 座席表とパネルを並べて表示できるように
    • ポップアップから切り替え可能
  6. ダークモード

    • UI全体を暗いテーマに変更可能
  7. Tab→Enter対応

    • Tabキーで検索ボタンに移動してEnterを押しても検索できるように

拡張機能名の変更

開発中に2回名前を変更:

  1. 当初: 「座席表検索」
  2. 汎用性を考慮: 「MultiSpot」
  3. 最終: 「MultiSearch」(気が変わったため)

生成AIを使って感じたこと

デバッグの効率

エラーが発生した際、Claudeから「F12でコンソールを開いて以下のログを確認してください」という具体的な指示があり、問題の切り分けがスムーズでした。

段階的な開発の重要性

一度に多くの機能を実装しようとすると、どこでエラーが起きているか特定が困難になりました。「まず動くものを作ってから機能追加」という進め方が有効でした。

仕様の明確化

「どんな動作が期待されるか」を具体的に伝えることで、適切な実装が得られました。特に以下のような伝え方が効果的でした:

  • エラーメッセージの全文を貼る
  • コンソールのログを共有する
  • 期待する動作と実際の動作を対比する

リファクタリングのタイミング

動作が安定しているバージョンから大幅にリファクタリングすると、予期せぬエラーが発生しました。「安定版をベースに慎重にリファクタリングしてください」と依頼することで、問題を回避できました。

最終的にできたもの

実装された機能

  • 複数キーワード検索(改行区切り、最大100キーワード)
  • 部分一致/完全一致の切り替え
  • スペース無視検索
  • ハイライト表示とナビゲーション
  • ポップアップ/サイドパネル表示
  • ダークモード
  • 検索状態の保存・復元
  • キーボードショートカット対応

セキュリティ対策

  • 入力値の長さ制限(検索テキスト: 10,000文字、1キーワード: 1,000文字)
  • 正規表現の特殊文字エスケープ
  • Content Security Policy設定
  • アクセシビリティ対応(aria-label属性)

技術仕様

  • Manifest Version: 3
  • 対応ブラウザ: Chrome 114以降
  • frameset対応(allFrames: true)
  • TextNode単位での処理(DOM構造を保持)

ファイル構成

最終的なファイル:

  1. manifest.json
  2. popup.html
  3. sidepanel.html
  4. panel.css
  5. panel.js
  6. icon16.png, icon48.png, icon128.png(アイコン生成ツールで作成)

仕様書

開発完了後、Claudeに仕様書の作成を依頼し、以下の内容を含む完全な仕様書を作成:

  • 機能仕様
  • UI仕様
  • 技術仕様
  • セキュリティ仕様
  • パフォーマンス仕様
  • 制限事項
  • 今後の拡張予定

まとめ

生成AIとの対話を通じてChrome拡張機能を開発した結果、以下のことが分かりました:

  1. 段階的な開発が重要: 一度に多くを実装せず、動くものから順に機能追加
  2. 具体的な情報共有が効果的: エラーメッセージやログを正確に伝える
  3. 安定版の保持: リファクタリング前の安定バージョンを保持しておく
  4. 試行錯誤の記録: チャット履歴が開発ログとして機能する

開発期間中、複数回のエラーや仕様変更がありましたが、Claudeとの対話を通じて問題を解決し、最終的に実用的な拡張機能を完成させることができました。


開発コスト:

  • 開発期間: 3日(5時間) ※最初のChatGPTとの会話も含む
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?