inspecting-hermes-desktop-dom
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseInspecting the live Hermes desktop DOM
检查Hermes桌面端的实时DOM
Overview
概述
When you are developing and the user is running that same app
( / ), you can read the live rendered DOM of the window
they are looking at — computed styles, geometry, which CSS rule actually won,
console output — instead of inferring it from and being wrong.
apps/desktophguinpm run dev.tsxDev-server runs open a Chrome DevTools Protocol port on
automatically. The renderer is a Chromium page, so everything DevTools can read,
a script can read.
127.0.0.1:9222This does not replace looking at it. CDP answers factual questions ("what
is the computed padding", "did this element render", "which selector matches").
It cannot tell you whether the result looks good. Colour balance, spacing feel,
and "is this ugly" still need the user's eyes or a screenshot. Answer facts with
CDP; hand aesthetics to the user.
当你开发且用户正在运行同一应用( / )时,你可以读取他们当前查看窗口的实时渲染DOM——包括计算样式、几何结构、实际生效的CSS规则、控制台输出——而不是从文件推断,避免出错。
apps/desktophguinpm run dev.tsx开发服务器会自动在开启Chrome DevTools Protocol(CDP)端口。渲染器是一个Chromium页面,因此DevTools能读取的所有内容,脚本都能读取。
127.0.0.1:9222**这不能替代直接查看界面。**CDP只能回答事实性问题(“计算后的内边距是多少”、“这个元素是否渲染了”、“哪个选择器匹配生效”)。它无法判断结果是否美观。色彩平衡、间距观感以及“是否难看”这类问题仍需要用户的肉眼观察或截图。用CDP解答事实问题,将美学判断交给用户。
When to Use
使用场景
- Verifying a UI change actually took effect in the running app
- "Why is this element still X?" — find the winning rule before editing anything
- Locating a stable selector for a component you're about to change
- Checking a design token's computed value on a real node
- Reading renderer console errors the user mentions but can't copy out
Don't use for: perf profiling or heap work (,
), or anything where the real question is "does this
look right".
node-inspect-debuggerdebugging-hermes-desktop- 验证UI变更是否在运行中的应用中实际生效
- 排查“为什么这个元素还是X状态?”——在编辑任何内容前找到实际生效的规则
- 为即将修改的组件定位稳定的选择器
- 检查设计令牌在真实节点上的计算值
- 读取用户提到但无法复制的渲染器控制台错误
**不适用场景:**性能分析或堆内存相关工作(使用、),或任何核心问题为“这个看起来是否正常”的场景。
node-inspect-debuggerdebugging-hermes-desktopThe port
端口说明
Open on for any dev-server run. Closed in exactly two cases
():
127.0.0.1:9222apps/desktop/electron/dev-cdp.ts- packaged builds — always, and no environment value overrides it;
- no — an unpackaged
HERMES_DESKTOP_DEV_SERVERagainstelectron .is how the packaged app gets smoke tested, so it behaves like one.dist/
HERMES_DESKTOP_CDP_PORT=9333=offCheck before doing anything else:
bash
curl -s --max-time 3 http://127.0.0.1:${HERMES_DESKTOP_CDP_PORT:-9222}/json/versionEmpty → no port. Do not guess another port silently.
Never relaunch the user's app to get a port. That destroys their session and
their state. Launch your own isolated instance instead (below).
任何开发服务器运行时都会在开启端口。仅在以下两种情况下端口会关闭(对应):
127.0.0.1:9222apps/desktop/electron/dev-cdp.ts- 打包构建版本——始终关闭,且无法通过环境变量覆盖;
- 未设置——针对
HERMES_DESKTOP_DEV_SERVER运行未打包的dist/是打包应用的冒烟测试方式,因此其行为与打包版本一致。electron .
通过可修改端口(如)或禁用端口(如)。
HERMES_DESKTOP_CDP_PORT=9333=off在执行任何操作前先检查端口状态:
bash
curl -s --max-time 3 http://127.0.0.1:${HERMES_DESKTOP_CDP_PORT:-9222}/json/version返回空内容→端口未开启。请勿静默尝试其他端口。
**绝不要为了获取端口而重启用户的应用。**这会销毁用户的会话和状态。请启动独立的隔离实例(见下文)。
Reading the DOM
读取DOM
apps/desktop/scripts/eval.mjsbash
cd apps/desktop
node scripts/eval.mjs "document.querySelectorAll('[data-slot]').length"For multi-step work use the shared client — it has target discovery and
promise-aware eval:
js
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()SELECTORSscripts/perf/lib/cdp.mjsdata-slotquerySelectorapps/desktop/scripts/eval.mjsbash
cd apps/desktop
node scripts/eval.mjs "document.querySelectorAll('[data-slot]').length"对于多步骤操作,使用共享客户端——它具备目标发现和支持Promise的eval功能:
js
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.mjsSELECTORSdata-slotquerySelectorThe question this is best at: which rule won?
最适合解决的问题:哪个规则生效了?
Editing every call site because a style "isn't applying" is the classic waste.
Read the real node first:
js
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
})()
})If the node carries no class of its own, the value is inherited — sweeping
call sites will not fix it, and you need the ancestor rule. A plugin stylesheet
(e.g. 's ) routinely beats
a utility class; override on the shared class, not at each usage.
@tailwindcss/typographyprose a { font-weight: 500 }因为样式“未生效”就修改所有调用位置是典型的无用功。先读取真实节点信息:
js
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/typographyprose a { font-weight: 500 }Your own isolated instance
独立隔离实例
When there is no port, or you must not disturb the user's window:
bash
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-userdataThe separate dodges Electron's single-instance lock, so it
cannot collide with a running ; the separate keeps it away
from real sessions. Pick a port other than 9222 for the same reason. Run it in
the background and kill it when done.
--user-data-dirhguiHERMES_HOMEnpm run perf:serveHERMES_HOME当端口未开启,或你不能干扰用户窗口时:
bash
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独立的可避开Electron的单实例锁,因此不会与正在运行的冲突;独立的可避免影响真实会话。同理,请选择9222以外的端口。在后台运行该实例,使用完毕后关闭。
--user-data-dirhguiHERMES_HOME如果你同时需要性能测试工具,会自动使用临时完成相同操作。
npm run perf:serveHERMES_HOMEPitfalls
注意事项
- Never kill the user's dev server or app to "free" anything. A mid-serve
kill nukes Chromium's socket pool, and the resulting gets blamed on whatever you just changed.
ERR_NETWORK_CHANGED - A throwaway has no backend. The app logs
HERMES_HOMEforECONNREFUSEDand may exit on its own. The renderer still mounts and the DOM is readable — read promptly, and don't mistake a self-exited probe for a broken port. Chromium logshermes:apiwhen it binds; that line is the proof the port opened.DevTools listening on ws://127.0.0.1:<port>/… - Poll, don't probe once. A just-launched app needs a second or two before the port answers.
- Never dump the whole DOM. The desktop renders hundreds of nodes and
will bury your context. Project down to a small JSON object inside the evaluated expression.
outerHTML - Pass to
match. Without it you may attach to the pet overlay, quick-entry window, or a devtools target instead of the main window.CDP.connect - returns the value; raw
cdp.evaldouble-nests it (Runtime.evaluate). Use the wrapper..result.result.value - is
import.meta.env.DEVundertruein this repo. The note invite devclaiming otherwise is stale.apps/desktop/scripts/profile-typing-lag.md
- **绝不要为了“释放”资源而终止用户的开发服务器或应用。**中途终止会销毁Chromium的套接字池,导致的错误会被归咎于你刚刚修改的内容。
ERR_NETWORK_CHANGED - **临时没有后端服务。**应用会针对
HERMES_HOME记录hermes:api错误,甚至可能自行退出。但渲染器仍会挂载,DOM仍可读取——请及时读取,不要将自行退出的探测实例误认为端口故障。Chromium在绑定端口时会记录ECONNREFUSED,该日志行是端口已开启的证明。DevTools listening on ws://127.0.0.1:<port>/… - **轮询检查,不要仅探测一次。**刚启动的应用需要一两秒时间才能响应端口请求。
- **绝不要导出整个DOM。**桌面端会渲染数百个节点,会淹没你的上下文。请在eval表达式内将结果精简为小型JSON对象。
outerHTML - **调用时传入
CDP.connect参数。**否则你可能会连接到宠物浮层、快速输入窗口或DevTools目标,而非主窗口。match - 直接返回值;原生
cdp.eval会返回双层嵌套结构(Runtime.evaluate)。请使用封装后的方法。.result.result.value - 在本仓库的环境下,
vite dev的值为import.meta.env.DEV。true中相反的说明已过时。apps/desktop/scripts/profile-typing-lag.md