Claude Code modのUIが出る面と出ない面の対応表
modが描くペインや帯が出るのはターミナルとDesktopのCodeタブだけで、VS Code拡張では出ません。面ごとの対応表と、Desktopの分割表示の不具合、フォールバックの書き方をまとめます。
Claude Codeのmodが描くUI(ペイン、プロンプト上の帯、ステータス、トースト)は、ターミナルとDesktopアプリのCodeタブには出ます。VS Code拡張のチャットパネルには出ません。modのフックは動くのに、描いたものだけが現れません。
これは不具合報告で見つかった挙動であると同時に、公式のmodsドキュメントの対応表にも「VS Code拡張は描画なし」と書かれている仕様です。報告者が求めているのは、描画の追加か、少なくとも出ないことの明示です。この記事は、どの面で何が出て何が出ないかを一枚の表にします。modの作り方はClaude Codeのmodを80行で作るにあります。
modのUIが出る場所と出ない場所
公式のmodsドキュメントは、フックと描画を分けて書いています。フックは、プラグインを読み込むセッションならどこでも走ります。描画はもっと狭く、ターミナルとDesktopアプリだけがペインや帯を出します。
| 実行場所 | フック | modの描画 |
|---|---|---|
ターミナルのclaude(エディターの統合ターミナル、JetBrainsプラグインを含む) | フック動く | modの描画出る |
| DesktopアプリのCodeタブ(WSLセッションを除く) | フック動く | modの描画出る(ターミナル専用の要素を除く) |
| DesktopアプリのWSLセッション | フック動かない(プラグインが使えない) | modの描画出ない |
| VS Code拡張のチャットパネル | フック動く | modの描画出ない |
claude -pとAgent SDK | フック動く | modの描画出ない |
| claude.aiやモバイルアプリからのRemote Control | フック動く(手元のマシン上のセッション) | modの描画手元のターミナルに出る |
| クラウドセッション | フックプラグインが届く場合は動く | modの描画出ない |
VS Code拡張は「フックは動く、描画は出ない」の行です。ツール呼び出しへの介入、コマンドの登録、$.prompt.submit、$.ui.askは拡張のパネルでも動くと報告されています。足りないのは描画だけです。
VS Code拡張で何が起きているか
GitHubの報告(#99045、macOS、拡張2.1.287)では、$.ui.openが{ isPlaced: false }を返します。ペインも、帯も、$.ui.statusも、$.ui.toastも現れません。エラーは出ず、コマンドはテキストの応答にフォールバックします。同じmodを端末のclaudeで動かすと、4つとも描かれます。
報告者の調査はこうです。同梱のエンジンはui_renderでsurface: "vscode"を受け付け、型定義にもvscode向けの要素表があります。ところが拡張のバンドル(extension.jsとwebview/index.js)にはui_attach、ui_render、ui_pressが1つもありません。エンジン側の準備はあるのに、拡張側がチャットパネルを描画面として接続していない、という見立てです。
grep -c ui_render \
~/.vscode/extensions/anthropic.claude-code-2.1.287-darwin-arm64/extension.js \
~/.vscode/extensions/anthropic.claude-code-2.1.287-darwin-arm64/webview/index.js報告者の手元ではどちらのファイルも0でした。拡張のバージョンとOSでパス名は変わります。
このIssueのコメントでは、Windows 11の拡張2.1.292でも、toast、ステータス、帯、ペインのどれも描かれない結果が報告されています。同じmodをclaude --plugin-dirで動かしたCLI(2.1.293)では4つとも描かれました。ラベルはarea:ideとplatform:vscodeで、報告者は「これまで動いたことはない」と書いています。つまり退行ではなく、最初から未対応の状態です。
報告者が望む姿は2通りです。VS Codeのチャットパネルをvscodeの描画面として接続するか、それがまだ先なら、ドキュメントとplugin-authoringのskillに「VS Codeはmod UIを描かない」と書き、$.ui.openが理由つきの値を返すようにするか、です。
Desktopアプリは描くが、分割表示では左のペインだけ
DesktopアプリのCodeタブは描画に対応しています。ただし別の報告(#99265、オープン中)があります。チャットを並べて開く分割表示で、AbovePromptの帯が1つのチャットにしか描かれません。
この報告の経緯は次のとおりです。
- 最初の報告者(Windows、Desktop 1.40609、Claude Code 2.1.286)は、左のチャットでは帯が約1秒ごとに要求され表示されるのに、右のチャットでは一度も要求されなかったと書いています。
SessionModeのフッター項目は右のチャットでも更新されました。 - 原因として、レンダラーのバンドルにあるコードが挙がっています。帯のコンポーネントは、ローカルで共有されておらず、かつ
isPanelActiveが真のセッションだけを描くようになっています。 - 後続のコメントでは、帯が出るのはキーボードフォーカスのあるペインではなく、分割表示の一番左のペインだという測定が複数出ています。チャットを左に動かすと帯が付き、右へ動かすと外れます。
- 同じチャットを別ウィンドウで開くと、帯はすぐに戻ります。
macOSのコメントには別の症状もあります。起動時にすでに開いていたチャットの一部で、アクティブなペインであっても帯が一度も要求されないというものです。再読み込みやアプリの再起動でも直らず、新しいチャットや、起動時に開いていなかった古いチャットでは正常でした。コメントは原因を、セッションのメタ情報の取得が一度空で返り、再取得されないためと推測していますが、最初の空の取得は観測できていません。コード上の推測にとどまります。
影響範囲は帯だけとは限りません。あるコメントは、$.ui.openで開いたペインと$.ui.statusも、チャットを一番左のペインから動かすと消えると書いています。一方、Linuxのコメントでは、右のペインでも$.ui.statusが描かれ、帯だけが出なかったとあります。ビルドや環境で結果が割れているため、帯が確実に影響を受ける、ステータスとペインは環境による、と読むのが妥当です。
要素ごとの対応表
面が描画に対応していても、要素が対応しているとは限りません。公式の要素表では、次のように割れます。
| 要素 | ターミナル | Desktop |
|---|---|---|
Box、Text、Button、Link、Code、Markdown、Input、Select、Client | ターミナル描ける | Desktop描ける |
Svg | ターミナル描けない | Desktop描ける |
Raster、Image | ターミナル描ける | Desktop描けない |
Svgだけを返すペインは、ターミナルでは空で開きます。RasterのヒートマップはDesktopに出ません。どちらもe.surfaceで面を調べ、別の木を返す書き方が前提です。
描画サイトの側にも差があります。Pane、AbovePrompt、Spinner、SessionMode、PromptHint、トランスクリプト系のサイトは両方で使えます。ToolProgress、TurnDuration、InfoNoticeはターミナルだけです。
描画が出ないときの切り分け
「出ない」の原因は、面の違いだけではありません。上から順に確かめると早く絞れます。
- modがそもそも読み込まれているか。別のIssue(#99130)に、2.1.288で
rollout switch served offによりフックのモジュールが読み込まれなかった報告があります。デバッグログにhooks module ... not loadedが出ます。この場合、面を変えても直りません - 実行場所は、表で「出る」の行か。VS Code拡張、
claude -p、クラウドセッションなら、出ないのが現状の挙動です - ターミナルなら、
$.ui.openの戻り値isPlacedがfalseでないか。modが自分から開くペイン(タイマーやturn.startのフックなど)は、幅が144列未満の端末では保留されます。ユーザーが一度そのペインを自分で開いたあとは110列あれば足ります。コマンドやボタンなど、ユーザーの操作から開いたペインは幅に関係なく出ます - ツリーが検証に通っているか。通らなければ
ui.render (Pane) refused:の行が出ます - 出ないのがトーストなら、デバッグログに
$.ui.toast (mod名): 本文の行があるかを見ます。行があるのに見えないときは、holdToastsを渡したペインが表示中でないかを確かめます。そのペインを閉じると保留は終わります。クラシックのレンダラーでは、トーストはプロンプトのすぐ下に、modの名前で始まる1行で出ます
claude --debug --plugin-dir ./hello-mod3つ目と5つ目は公式のトラブルシュートにある条件で、VS Code拡張のisPlaced: falseとは理由が違います。拡張では理由が返らないというのが、冒頭の報告者の不満です。
描けない面でも使えるmodにする
面ごとの差は、mod側で吸収できます。公式のドキュメントも、描画が出ない場所ではトランスクリプトの1行か、コマンドのテキスト応答にフォールバックするよう勧めています。
例えば次のような形になります(報告者のコマンド例を、フォールバックを足す形に書き換えたものです)。
on('command.run', { command: 'hello-pane' }, async $ => {
const r = await $.ui.open({ id: 'hello', title: 'Hello', focus: true })
if (!r.isPlaced) {
// 描けない面、または保留。結果を文字で返す
return { text: `ペインは開きませんでした(${r.reason ?? '理由なし'})。内容: hello` }
}
return { text: 'ペインを開きました' }
})ペインの中身は、ui.renderのフックでe.surfaceを見て分けます。terminalとdesktopだけが値として文書化されており、VS Code拡張ではui.render自体が呼ばれません。つまり、描画の有無を面の名前で判定するより、$.ui.openの戻り値で判定するほうが、今後面が増えても壊れにくい形です。
実行中のアプリは、$.sessionのsurfacesでも調べられます。公式の概要ページも、描くmodは動いているアプリを確かめ、描画のない場所ではトランスクリプトの1行かコマンドのテキスト応答に切り替えられる、と書いています。使い分けはこうです。描くかどうかを前もって決めたいなら$.session.surfaces、実際にペインが置かれたかを見たいなら$.ui.openのisPlacedです。isPlacedがfalseのときはreasonに理由の文字列が入るので、フォールバックの文面にそのまま使えます。幅が足りずに保留されただけのときもfalseになるため、isPlacedのほうが現場の状態に近い判定です。
もう1つの設計上の注意は、情報を描画だけに閉じ込めないことです。使用量やタイマーのような常時表示は、帯に出すとDesktopの分割表示で右のペインから消えます。同じ値を/usage-bandのようなコマンドのテキスト応答でも引けるようにしておくと、VS Code拡張でも、分割表示の右のペインでも読めます。報告者の1人は、ステータスラインを帯の代わりに使っています。なお、Desktopのセッションでmodの保存時に自動で再読み込みさせるには、環境変数CLAUDE_CODE_PLUGIN_DIR_WATCH=1が必要だという報告もあります。
追うならこの3件
3件ともオープンのIssueです。直れば、修正されたバージョンがIssueに残ります。
| 追うもの | 内容 |
|---|---|
| #99045 | 内容VS Code拡張がmod UIを描かない。ドキュメントの対応表では「描画なし」 |
| #99265 | 内容Desktopの分割表示で、帯が左のペインにしか出ない |
| #99130 | 内容modのフックモジュールが読み込まれない(段階的な展開の設定が原因とされる) |
VS Code拡張のパネルが反応しないときの一般的な切り分けは、Claude CodeのVS Code拡張機能が応答しないときの切り分け手順にまとめています。mod導入時のリリース内容はClaude Code v2.1.287、描画クラッシュの修正はv2.1.289で扱っています。描画に失敗したClientを拾う方法はui.faultの記事にあります。
まとめ
modのUIを見せたい相手がVS Code拡張のユーザーなら、描画には頼れません。ターミナルとDesktop(WSLを除く)を前提にし、Desktopの分割表示では帯が左のペインにしか出ない可能性を見込みます。
どの面でも届けたい情報は、コマンドのテキスト応答にも出せるようにしておくのが堅実です。$.ui.openのisPlacedを見て分岐すれば、描ける面では描き、描けない面では文字に落とせます。