Inspecting Hermes Desktop Dom
動いている Hermes デスクトップの DOM/CSS を CDP で読みます。
skill の情報
| 提供元 | 最初から入っています |
| パス | skills/software-development/inspecting-hermes-desktop-dom |
| バージョン | 1.0.0 |
| 作者 | Hermes Agent |
| ライセンス | MIT |
| 対応プラットフォーム | linux, macos, windows |
| タグ | desktop, electron, cdp, dom, ui-verification, self-inspection |
| 関連 skill | node-inspect-debugger, systematic-debugging, dogfood |
参考: SKILL.md 全文
動いている Hermes デスクトップの DOM を調べる
概要
apps/desktop を開発していて、利用者が同じアプリ(hgui / npm run dev)を動かしているときは、その人が見ている画面の実際に描画された DOM を読めます。計算後のスタイル、位置と大きさ、どの CSS ルールが実際に勝ったか、 コンソールの出力まで分かるので、.tsx から推測して外すことがなくなります。
開発サーバーで起動した場合、Chrome DevTools Protocol のポートが 127.0.0.1:9222 に自動で開きます。描画側は Chromium のページなので、DevTools で読めるものは スクリプトからも読めます。
これは実際に目で見ることの代わりにはなりません。 CDP が答えられるのは*事実*の問い (「計算後の padding はいくつか」「この要素は描画されたか」「どのセレクタが当たっているか」) です。結果の見栄えが良いかどうかは判断できません。色の釣り合い、余白の感じ、 「これは見苦しいか」は、やはり利用者の目かスクリーンショットが必要です。事実は 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/ に対して動かすのは、パッケージ済みアプリの動作確認のやり方なので、同じ扱いになります。
HERMES_DESKTOP_CDP_PORT でポートを変えられます(=9333)。無効にもできます(=off)。
何かを始める前に確認します。
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 が付いています。
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、スレッドの表示領域、アシスタントのメッセージ、やり取りの組、プロフィールの帯)。 自分で 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=/tmp/cdp-probe-home \
HERMES_DESKTOP_DEV_SERVER=http://127.0.0.1:5174 \
HERMES_DESKTOP_CDP_PORT=9333 \
npx electron . --user-data-dir=/tmp/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回だけ試すのではなく、繰り返し確認してください。 起動直後のアプリは、ポートが応答するまでに
1〜2秒かかります。
- DOM 全体を出力しては決していけません。 デスクトップは何百ものノードを描画するので、
outerHTML はコンテキストを埋め尽くします。評価する式の中で、小さな JSON オブジェクトまで 絞り込んでください。
CDP.connectにはmatchを渡してください。 これがないと、メインの画面ではなくペットの
重ね表示、クイック入力の画面、devtools の対象につながることがあります。
cdp.evalは値をそのまま返しますが、生のRuntime.evaluateは二重に包みます
(.result.result.value)。ラッパーを使ってください。
- このリポジトリでは
vite devのもとでimport.meta.env.DEVはtrueです。 そうでないと書いてある
apps/desktop/scripts/profile-typing-lag.md の記述は古いものです。