29
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Cursor Browserのような「ブラウザを開いてUI操作でAIがコードを修正する」をVSCodeの拡張機能で実現したい奮闘記

29
Posted at

はじめに

最近Cursorエディタでは、v2にてBrowser、さらにv2.2にてビジュアルエディタという機能が追加されました。

とても便利な機能で、大体のフロントエンジニアがCursorに乗り換えたほうがいいのでは?と思っているくらいです。

「この要素、ちょっと修正したいな」と思った時、ブラウザ上の要素をポチッと選んで、そのままAIに修正を依頼する。あのシームレスな体験、最高ですよね。

私もCursorに乗り換えたい民の一人なのですが、

「VSCode上でもやれたらいいなぁ...」

となぜかふと思ってしまい、拡張機能として作れないか奮闘することにしました🔥

前提

いきなり全部作るのは無謀なので(というか公開されているAPIで戦うしかなく、完全再現はとても難しい)、

  • VSCode上でブラウザを立ち上げる
  • 要素がブラウザUI上で選択できる
  • 選択した要素をコンテキストとしてAI Agentに渡す

とりあえずここまでを目指してプロトタイプを実装してみました。

1. 実現できた体験(デモ)

コマンドパレットから拡張機能のコマンドを起動し、開発環境のURL(localhost:3000など)を入力

demo.gif

VSCode内で専用のブラウザが立ち上がる

スクリーンショット 2025-12-23 5.49.45.png

右上のアイコンをクリックして要素をクリック

demo2.gif

demo3.gif

自動でCopilot Chatが開き、選択した要素(HTML/CSS)のコンテキストが注入される

スクリーンショット 2025-12-23 6.00.30.png

ユーザーは追加のプロンプトで「このボタンの色を赤くして」と送るだけ。

demo4.gif

無事にcopilotが修正してくれました🎉

 .png

できているのか...?

動きやUIは頑張りました!

しかし、まだまだ改善点がありまくりです。

例えば、コンテキストとして渡している要素はHTMLとCSSくらいしか渡していないので、実際のプロジェクトで行うと選択された要素がない別のファイルを見にいって修正しようとしたりします。(特に大規模なものなら尚更)

以上のことを頑張ることでだいぶ良くなるかもしれません。

さらに課題だったところ

VSCode内でブラウザを立ち上げるには、WebView APIiframeで描画させる必要があります。

ここに関して、VSCode上でブラウザを表示させるだけであれば、これで問題は起きません。

しかし、やりたいことは

  • 要素を選択するUIを提供する
  • HTMLやCSSを読み取るためのJSの実行

であり、それを実現しようとするとSame-Origin Policyに引っかかかってしまい、問題が発生します。

Webview(親)とiframe(子) のオリジンが異なるためです。

親から子要素のDOMに一切アクセスできません😇
これでは「要素を選択する」ことすら不可能になります。

どのように解決したか

これを回避するために、「ブラウザの外部から操作する」のではなく、「最初から操作用のスクリプトが埋め込まれた状態のHTMLを配信する」 というアプローチを取ることにしました。

結論としては、Proxyサーバーを介してHTMLを配信させることで解決に持っていけました。

ProxyサーバーにはHonoを採用しました!(理由は好きで利用も簡単だからです!)

アーキテクチャ図

実装コードの要点

Honoの proxy ヘルパーを使うとこれだけのコードで「スクリプト入りのHTML」を錬成できます。

// server/proxyServer.ts
import { Hono } from 'hono';
import { proxy } from 'hono/proxy';

app.all('*', async (c) => {
  // 開発サーバーへリクエストを転送
  const res = await proxy('http://localhost:3000')(c);

  // HTMLレスポンス時のみ、</body>の直前にスクリプトを注入
  if (res.headers.get('content-type')?.includes('text/html')) {
    const html = await res.text();
    const inspector = '<script>/* 要素選択ロジック */</script>';
    return c.html(html.replace('</body>', `${inspector}</body>`));
  }
  return res;
});

Copilot Chatとの連携について

要素の情報を選択や取得をした後、いかにしてCopilotに繋げるかも重要ですが、ここでは、VSCodeの内部コマンドworkbench.action.chat.openを利用しました。

// chat/visualEditorParticipant.ts
vscode.commands.executeCommand('workbench.action.chat.open', {
  query: `[${elementSummary}] `, // 要素情報をプリセット
  isPartialQuery: true           // ← これが鍵かも
});

コマンド内のisPartialQueryオプションを指定することで、チャット欄に文字が入った状態で送信せずに止めることができます。

ユーザーは選択済みの要素情報の後に「背景を青にして」と追加のプロンプトを書き加えて送信するだけで、Copilotにあとは勝手に実装してくれるという仕組みになりました。

ディレクトリ構造

src/
├── extension.ts              # エントリーポイント
├── browserPanel.ts           # Webview UI・ツールバーの管理
├── chat/
│   └── visualEditorParticipant.ts  # 取得要素の整形とChatへの受け渡し
├── server/
│   ├── proxyServer.ts        # Honoプロキシの実装
│   └── inspectorScript.ts    # ページに注入するJS(要素の選択や取得、ハイライトなど)
└── types/
    └── elementInfo.ts        # 型定義

最後に

だいぶ面白いものを作ることができ、学びも多くありました。
要素の抽出やコンテキストとしての整形を上手くやればさらに良いものになりそうです!
次はスクショをAgentに渡す機能を作ってもさらに面白そうです!

おまけに

車輪の再開発を避けるべく、他のアプローチとして、

などいろいろ考えましたが難しかったです😇

良い実装方法があったら教えてほしい...

リポジトリはこちらです!

P.S.

(普通にCursorを使ったほうがいい)

29
12
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
29
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?