inspecting-hermes-desktop-dom について
Chrome DevTools Protocolを使い、実行中のHermesデスクトップアプリのDOMを調べる開発用スキルです。ソースコードだけに頼らず、描画要素、計算済みスタイル、位置情報、コンソール出力を確認できます。
主な機能
- 01実行中のレンダラーDOMの取得
- 02計算済みスタイルと位置情報の確認
- 03一致するCSSルールと適用ルールの特定
- 04レンダラーのコンソール出力の確認
- 05安定したdata-slotセレクターの利用
活用シーン
- 01実行中アプリでUI変更を確認
- 02スタイルが適用されない原因の調査
- 03コンポーネント編集前のセレクター特定
- 04報告されたレンダラーエラーの確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ nousresearch/ hermes-agent --skill inspecting-hermes-desktop-domインストール後、Claude Code で/inspecting-hermes-desktop-domを入力して読み込まれたか確認します。
詳細
Hermes デスクトップのライブ DOM を調べる
概要
apps/desktop を開発していて、ユーザーが同じアプリ(hgui / npm run dev)を実行している場合、ユーザーが見ているウィンドウのレンダリング済みライブ DOMを読み取れます。計算済みスタイル、ジオメトリ、実際に適用された CSS ルール、コンソール出力などを確認できるため、.tsx から推測して間違えることがありません。
開発サーバーを実行すると、127.0.0.1:9222 で Chrome DevTools Protocol のポートが自動的に開きます。レンダラーは Chromium のページなので、DevTools が読み取れるものはすべてスクリプトでも読み取れます。
実際に画面を見ることの代わりにはなりません。 CDP は「計算済みのパディング値は何か」「この要素はレンダリングされたか」「どのセレクターが一致するか」といった事実に関する質問には答えられますが、見た目が良いかどうかは判断できません。色のバランスや余白の感覚、「見た目が悪いかどうか」は、ユーザー自身の目かスクリーンショットで確認する必要があります。事実は CDP で確かめ、美的な判断はユーザーに委ねてください。
使用する場面
- UI の変更が実行中のアプリに実際に反映されたか確認するとき
- 「この要素がまだ X なのはなぜ?」というとき — 何かを編集する前に、実際に適用されたルールを見つけます
- 変更する予定のコンポーネントで、安定したセレクターを特定するとき
- 実際のノードでデザイントークンの計算値を確認するとき
- ユーザーが言及しているもののコピーできない、レンダラーのコンソールエラーを読むとき
使用しない場面: パフォーマンスのプロファイリングやヒープの調査(node-inspect-debugger、
debugging-hermes-desktop)、または本当の質問が「見た目は正しいか」という場合。
ポート
開発サーバーの実行時は 127.0.0.1:9222 で開きます。以下の 2 つの場合に限り閉じています(apps/desktop/electron/dev-cdp.ts)。
- パッケージ化されたビルド — 常に閉じており、環境変数で上書きすることもできません。
HERMES_DESKTOP_DEV_SERVERがない場合 — パッケージ化されたアプリのスモークテストには、パッケージ化されていないelectron .からdist/を実行するため、パッケージ化されたアプリと同じ動作になります。
何かをする前に確認してください。
curl -s --max-time 3 http://127.0.0.1:${HERMES_DESKTOP_CDP_PORT:-9222}/json/version
何も表示されない場合は、ポートが開いていません。別のポートを勝手に推測しないでください。
ポートを開くために、ユーザーのアプリを再起動してはいけません。 セッションと状態が失われます。代わりに、以下の手順で独立したインスタンスを起動してください。
DOM を読む
apps/desktop/scripts/eval.mjs は、ワンライナー用のスクリプトです。
cd apps/desktop
node scripts/eval.mjs "document.querySelectorAll('[data-slot]').length"
複数の手順が必要な場合は、ターゲットの検出と Promise に対応した eval を備える共有クライアントを使用してください。
import { CDP, SELECTORS } from './scripts/perf/lib/cdp.mjs'
const cdp = await CDP.connect({ port: 9222, match: '5174' })
const out = await cdp.eval(`JSON.stringify({
radius: getComputedStyle(document.documentElement).getPropertyValue('--radius-scalar').trim(),
composer: !!document.querySelector('[data-slot="composer-rich-input"]')
})`)
cdp.close()
scripts/perf/lib/cdp.mjs の SELECTORS には、安定した data-slot フック(composer、thread viewport、assistant message、turn pair、profile rail)が用意されています。querySelector を独自に考案するより、こちらを優先してください。コンポーネントの移動に合わせて、一括で更新されます。
最も役立つ質問:どのルールが適用されたか?
スタイルが「適用されない」からといって、すべての呼び出し箇所を編集するのは、よくある無駄な作業です。まず実際のノードを確認してください。
const el = document.querySelector('[data-slot="aui_assistant-message-root"] a')
JSON.stringify({
ownClasses: el.className,
weight: getComputedStyle(el).fontWeight,
parents: (() => {
const out = []
let n = el
while ((n = n.parentElement) && out.length < 6) out.push(n.className)
return out
})()
})
ノード自体にクラスが付いていない場合、その値は継承されたものです。呼び出し箇所を片っ端から変更しても解決せず、祖先のルールを確認する必要があります。プラグインのスタイルシート(たとえば @tailwindcss/typography の prose a { font-weight: 500 })は、ユーティリティクラスより優先されることがよくあります。その場合は、使用箇所ごとではなく共有クラスで上書きしてください。
独立した自分用インスタンス
ポートが開いていない場合や、ユーザーのウィンドウに影響を与えてはいけない場合は、次のようにします。
cd apps/desktop
HERMES_HOME=$HOME/.hermes/cache/scratch/cdp-probe-home \
HERMES_DESKTOP_DEV_SERVER=http://127.0.0.1:5174 \
HERMES_DESKTOP_CDP_PORT=9333 \
npx electron . --user-data-dir=$HOME/.hermes/cache/scratch/cdp-probe-userdata
別の --user-data-dir を指定すると、Electron のシングルインスタンスロックを回避できるため、実行中の hgui と競合しません。別の HERMES_HOME を指定すれば、実際のセッションにも影響しません。同じ理由から、9222 以外のポートを選んでください。バックグラウンドで実行し、作業が終わったら終了させてください。
パフォーマンスハーネスも使用する場合は、npm run perf:serve を実行すると、一時的な HERMES_HOME があらかじめ設定された同じ環境で起動します。
注意事項
- 「空きを作る」ために、ユーザーの開発サーバーやアプリを絶対に終了しないでください。 配信中に終了すると、Chromium のソケットプールが破棄され、その結果発生する
ERR_NETWORK_CHANGEDは、直前に変更した内容が原因だと誤解されます。 - 使い捨ての
HERMES_HOMEにはバックエンドがありません。 アプリはhermes:apiに対してECONNREFUSEDをログに出力し、自動終了することがあります。レンダラーは引き続きマウントされ、DOM も読み取れます。速やかに読み取り、自動終了したプローブをポートの異常と取り違えないでください。Chromium はポートにバインドするとDevTools listening on ws://127.0.0.1:<port>/…をログに出力します。この行がポートの開通を示す証拠です。 - 一度だけプローブするのではなく、ポーリングしてください。 起動直後のアプリがポートに応答するまで、1、2 秒かかります。
- DOM 全体を出力しないでください。 デスクトップアプリでは数百のノードがレンダリングされるため、
outerHTMLを出力するとコンテキストが埋め尽くされます。評価式の中で小さな JSON オブジェクトに絞り込んでください。 CDP.connectにmatchを渡してください。 渡さないと、メインウィンドウではなく、ペットオーバーレイ、クイックエントリーウィンドウ、または開発者ツールのターゲットに接続することがあります。cdp.evalは値を返しますが、生のRuntime.evaluateは値を二重にネストします (.result.result.value)。ラッパーを使用してください。- このリポジトリでは、
vite devの実行中にimport.meta.env.DEVはtrueになります。apps/desktop/scripts/profile-typing-lag.mdにある、そうではないとする記述は古くなっています。