关于 pxtree
pxtree 是供编码 Agent 检查网页布局的命令行工具和 MCP 服务,可用紧凑文本树及测量结果呈现 Chromium 的实际渲染。它还能比较运行变化,并在同次测量中输出无障碍树或截图。
主要功能
- 01以文本树呈现网页渲染
- 02测量元素尺寸与位置
- 03比较同一页面的运行变化
- 04支持多个视口和配色方案
- 05可附加无障碍树或截图
适用场景
- 01检查开发服务器页面的布局问题
- 02对比修复前后的页面测量
- 03检查不同视口或配色下的呈现
- 04查看页面无障碍结构
MCP 相关
pxtree 提供 MCP 服务,包含 measure 和 read_me_first 两个工具。编码 Agent 可请求网页测量报告、ARIA 树和截图。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
2收录的工具列表 · 更新于 2 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| aria | boolean | 否 | 布尔值:在报告后添加页面或每个匹配元素的 Playwright aria 快照 |
| diff | boolean | 否 | 布尔值:与相同 target 和设置的上一次运行进行比较。默认值为 true |
| wait | any | 否 | 脚本运行后:以毫秒为单位的休眠时长,可为数字或数字字符串,且不超过 timeout;或者 CSS 选择器字符串,等待其在 timeout 内出现 |
| colors | boolean | 否 | 布尔值:在 [text] 和 [renders] 中打印十六进制颜色值 |
| report | enum | 否 | 可选值为 "tree"、"findings"、"summary"、"changes"、"none"。tree:全部内容。findings:仅显示包含发现项的行及其祖先行。summary:不显示树。changes:事实行和自上次运行以来的变化。none:仅事实行。默认值为 treetree | findings | summary | changes | none |
| script | string | 否 | 字符串:async (page) => {} 的函数体,或完整函数(如 async (page) => {}),在测量前使用 Playwright 页面运行。此代码受信任,会以 Node 身份在服务器进程中运行,并拥有完整权限 |
| scroll | any | 否 | 一个停止点或停止点数组:数字或数字字符串表示窗口 y 偏移量,"end" 表示底部,其他值均视为要滚动到顶部的 CSS 选择器。数组会依次测量每个停止点,每个停止点运行一次。默认值为 0 |
| target | string | 是 | 字符串:URL、类似 localhost:5173 的主机名,或工作目录下的 HTML 文件路径 |
| diffKey | string | 否 | 名称字符串,用于替代自上次运行以来的键中的 script 和 wait,以便比较使用相同名称的脚本运行和普通运行 |
| element | string | 否 | CSS 选择器字符串:仅打印匹配元素及其祖先行 |
| schemes | array | 否 | "light" 和 "dark" 的数组,表示每次运行使用的 prefers-color-scheme。默认值为 ["light"] |
| timeout | number | 否 | 达到 DOMContentLoaded 的毫秒数。脚本、wait、reveal、settle 和测量都会各自重新获得此时长。默认值为 30000 |
| children | boolean | 否 | 布尔值。与 element 一起使用时:包含匹配项内部的内容。默认值为 true |
| maxChars | number | 否 | 数字:报告的最大字符数。超出后会在一行处截断树;事实、自上次运行以来的内容和摘要仍会保留。默认值为 80000 |
| viewports | array | 否 | 一个包含 { width, height } 的数组,数值为整数,每个尺寸运行一次。默认值为 [{ width: 1280, height: 800 }] |
| screenshot | boolean | 否 | 布尔值:为每次运行在操作系统临时目录下保存一个 PNG,并返回其路径 |
详细介绍
pxtree
在无头 Chromium 中加载网页,并以紧凑的文本树形式打印实际渲染结果,让 AI 编码 Agent 无需截图即可检查布局。
开发服务器页面中包含一行三张卡片、一行三个方案、一列步骤、一行统计数据、一篇文章、一个表单、一列价格和一行磁贴:
$ npx -y pxtree@latest localhost:5173
1280x800 light dpr 1 ltr scroll 0/382 page 1280x1182 painted to 1134
since last run: no changes
summary: 7 findings
a.button tops 64..136 across siblings: ul.cards
div.stat tops 0..6 across siblings: div.stats
input.field starts 0..3 across siblings: form.fields
12 wider than li.plan: li.plan.featured
72 taller than li.card: li.card 3 of 3
20 shorter than div.tile: div.tile.short
gaps 16 16 24 16 between li.step: ul.steps
body 1280x1182 [pad 24][gaps 24 24 24 24 16 16 …, free 24 at end]
ul.cards 1232x208 [gaps across 16, free 600 at end][!! a.button tops 64..136 across siblings]
li.card 200x136 [pad 16][gaps 8][renders background]
h3 "Starter" 168x24 [text 19/24]
p "Short text." 168x24 @0,32 [text 16/24]
a.button "Choose" 168x40 @0,64 [pad 8 16][text 16/24][renders background]
li.card 200x136 @216,0 [pad 16][gaps 8][renders background]
h3 "Team" 168x24 [text 19/24]
p "Short text." 168x24 @0,32 [text 16/24]
a.button "Choose" 168x40 @0,64 [pad 8 16][text 16/24][renders background]
li.card 200x208 @432,0 [pad 16][gaps 8][renders background][!! 72 taller than li.card]
h3 "Business" 168x24 [text 19/24]
p "A much longer…" 168x96 @0,32 [text 16/24, 4 lines, 444 on one line]
a.button "Choose" 168x40 @0,136 [pad 8 16][text 16/24][renders background]
ul.plans 1232x56 @0,232 [gaps across 16, free 588 at end]
li.plan "Monthly" 200x56 [pad 16][text 16/24][renders background]
li.plan "Yearly" 200x56 @216,0 [pad 16][text 16/24][renders background]
li.plan.featured "Lifetime" 212x56 @432,0 [pad 16][text 16/24][renders background][!! 12 wider than li.plan]
ul.steps 1232x192 @0,312 [gaps 16 16 24 16][!! gaps 16 16 24 16 between li.step]
li.step "One" 1232x24 [text 16/24]
li.step "Two" 1232x24 @0,40 [text 16/24]
li.step.active "Three" 1232x24 @0,80 [text 16/24]
li.step "Four" 1232x24 @0,128 [text 16/24]
li.step "Five" 1232x24 @0,168 [text 16/24]
div.stats 1232x62 @0,528 [gaps across 16, free 456 at end][!! div.stat tops 0..6 across siblings]
div.stat "Revenue" 182x56 [pad 16][text 16/24][renders background]
div.stat "Orders" 182x56 @198,0 [pad 16][text 16/24][renders background]
div.stat.alert "Refunds" 182x56 @396,6 [pad 16][text 16/24][renders background]
div.stat "Customers" 182x56 @594,0 [pad 16][text 16/24][renders background]
article 1232x132 @0,614 [gaps 12]
h2 "First heading" 1232x24 [text 24/24]
p "First paragraph." 1232x24 @0,36 [text 16/24]
h2 "Second heading" 1232x24 @0,72 [text 24/24]
p "Second paragraph." 1232x24 @0,108 [text 16/24]
p.with-link "Read the first." 1232x24 @0,762 [text 16/24]
a "guide" 39x17 @69,3 [text 16/24]
form.fields 1232x108 @0,802 [gaps 8][!! input.field starts 0..3 across siblings]
input.field "Name" 208x21 [pad 1 2][text 13/15][renders background, border]
input.field "Email" 208x21 @0,29 [pad 1 2][text 13/15][renders background, border]
input.field.nudged "Company" 208x21 @3,58 [pad 1 2][text 13/15][renders background, border]
input.field "Phone" 208x21 @0,87 [pad 1 2][text 13/15][renders background, border]
div.prices 300x72 @0,934
span.price "$9" 18x24 @282,0 [text 16/24]
…×2 similar span.price 36x24..49x24
div.tiles 1232x80 @0,1030 [gaps across 16, free 900 at end]
div.tile 100x80 [renders background]
div.tile.short 100x60 @116,0 [renders background][!! 20 shorter than div.tile]
div.tile 100x80 @232,0 [renders background]
第一行是事实行:视口、配色方案、设备像素比、方向、当前滚动位置与最大滚动位置之差、文档尺寸,以及 painted to(最后绘制的像素)。since last run 会与相同 URL 和设置的上一次运行结果进行比较,或与相同 --diff-key 名称的运行结果进行比较。这些历史记录保存在 ~/.cache/pxtree 中,绝不会写入你的项目;使用 --no-diff 可跳过历史记录。
ul.cards 行表示,三张卡片中“Choose”按钮的顶部与各自卡片顶部相距 64 px 至 136 px。li.plan.featured "Lifetime" 行表示,该方案在所在行中的位置为 x 432,尺寸为 212x56,比同级方案共享的宽度多 12 px。这些发现是带阈值的测量结果,不是结论。Agent 会根据代码判断这些情况是否符合预期。
为什么不用截图
- 提供 CSS px 数值,而不是需要模型估算的像素。
- Agent 可以一次性以文本形式读取,并优先看到列出的发现。
- 一次调用即可覆盖多个视口和配色方案。
安装
pxtree 使用 Playwright 的 Chromium。安装一次即可,约占用 150 MB:
npx -y [email protected] install chromium
pxtree 本身不会下载任何内容,也不会修改你的项目。--channel chrome 会改用已安装的 Chrome。需要 Node 20 或更高版本。
MCP 服务
OpenCode,opencode.json:
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"pxtree": { "type": "local", "command": ["npx", "-y", "pxtree@latest", "mcp"], "enabled": true }
}
}
其他客户端和配置文件
任何读取 mcpServers 的 MCP 客户端:
{
"mcpServers": {
"pxtree": { "command": "npx", "args": ["-y", "pxtree@latest", "mcp"] }
}
}
VS Code,.vscode/mcp.json:
{
"servers": {
"pxtree": { "type": "stdio", "command": "npx", "args": ["-y", "pxtree@latest", "mcp"] }
}
}
Amp,settings.json:
{
"amp.mcpServers": {
"pxtree": { "command": "npx", "args": ["-y", "pxtree@latest", "mcp"] }
}
}
Zed,settings.json:
{
"context_servers": {
"pxtree": { "command": "npx", "args": ["-y", "pxtree@latest", "mcp"], "env": {} }
}
}
MCP Registry 名称:io.github.alabsi91/pxtree。该服务提供两个工具:measure 和 read_me_first。后者会返回技能包含的内容:如何使用 pxtree、如何报告,以及阅读指南。其说明要求 Agent 在每个会话中调用一次 read_me_first,因此无需安装技能。
measure 接受 target 和 CLI 标志作为输入:viewports、schemes、scroll、element、children、colors、wait、script、screenshot(设为 true 时,会在临时目录下为每次运行保存一张 PNG)、timeout、maxChars(默认值为 80000)、diff、diffKey、report(tree、findings、summary、changes 或 none)以及 aria(设为 true 时会添加 aria 树)。一次调用可以同时返回报告、aria 树和截图。输入值有范围限制:视口边长为 1 到 10000,最多 10 个视口,timeout 最大为 120000。文件目标必须位于服务的工作目录下。此规则可防止意外操作,但并非安全边界:script 是受信任的代码,会以 Node 身份在服务进程中运行。
技能
对于会读取 Agent Skills 的 Agent,包括通过 .agents/skills 使用的 Codex:
npx -y skills add alabsi91/pxtree
也可以手动创建链接。Codex、Cursor、Copilot、Gemini CLI、OpenCode、Amp 和 Devin 会读取 ~/.agents/skills。Claude Code 会读取 ~/.claude/skills。
git clone https://github.com/alabsi91/pxtree
mkdir -p ~/.agents/skills ~/.claude/skills
ln -s "$PWD/pxtree/skills/pxtree" ~/.agents/skills/pxtree
ln -s "$PWD/pxtree/skills/pxtree" ~/.claude/skills/pxtree
仅供单个项目使用时,请使用 <project>/.agents/skills 或 <project>/.claude/skills。
仅使用 Shell 的 Agent
告诉 Agent 运行此命令。它会输出标志、输出语法、每个标签和检测结果,以及限制。
npx -y pxtree@latest guide
CLI
npx -y pxtree@latest https://example.com --viewport 390x844,1280x800 --scheme light,dark
npx -y pxtree@latest localhost:5173 --scroll '#pricing' --element '.card'
npx -y pxtree@latest ./dist/index.html
npx -y pxtree@latest localhost:5173 --script "await page.click('text=Menu')" --wait '.menu'
npx -y pxtree@latest localhost:5173 --element '#pricing' --screenshot pricing.png
npx -y pxtree@latest localhost:5173 --viewport 390,1280
npx -y pxtree@latest localhost:5173 --report summary --aria
npx -y pxtree@latest localhost:5173 --report findings
npx -y pxtree@latest localhost:5173 --report changes
npx -y pxtree@latest localhost:5173 --report none --screenshot shot.png
只有当选择器恰好匹配一个元素时,截图才会裁剪到 --element 匹配项。否则截图会覆盖整个视口,事实行会说明匹配了多少个元素。
--report 用于选择打印多少测量结果:tree(默认,全部内容)、findings(仅保留有检测结果的行及其祖先的名称)、summary(不显示树)、changes(显示事实行和上次运行后的变化,用于检查修复效果)或 none(仅显示事实行)。在 --viewport 中只指定宽度时,会匹配相应的高度:390x844、768x1024、820x1180、1024x768、1280x800、1440x900、1920x1080;其他宽度均为 800。--aria 会在报告后添加 Playwright 的页面 ARIA 快照,或每个 --element 匹配项的 ARIA 快照:角色、名称、状态和阅读顺序,均取自与测量相同的状态。这样一次调用就能提供检测结果、无障碍树和截图。
npx -y pxtree@latest --help 会列出标志。npx -y pxtree@latest guide 会说明如何读取输出。
限制
- 仅检查视口内的覆盖情况。
- 不会遍历声明式的封闭 shadow root。
- 不会遍历 iframe。请单独测量框架 URL。
- 伪元素的绘制内容会放置在元素的盒子上。
- 裁剪路径按边框盒计算。忽略边框圆角。
- 在经过旋转或缩放的盒子内部,不会报告重叠、居中或宽度问题。其轴对齐盒无法说明元素的位置。
- 不会测量图片或渐变上的对比度;
[text]标签会在此类情况下显示on image。对于渐变文字等透明文字填充,也不会测量对比度;此时会显示fill transparent。 small target会跳过文本行内的链接,以及间距足够的目标,正如 WCAG 2.5.8 所允许的那样。- pxtree 不会猜测意图。全幅区域、头像堆叠、打开的弹出层或折叠面板都会照常打印其数值。
- 仅模拟桌面端:
(hover: hover)和(pointer: fine)匹配。不支持触摸。 - 垂直书写模式会打印物理位置。
- 加载时最多等待
load事件 2 s,以及网络静默 1.5 s;文件会跳过这些等待。加载较慢的页面需要使用--wait。
API
import { createSession, format } from 'pxtree';
const session = await createSession();
const result = await session.measure('localhost:5173', { viewports: [{ width: 390, height: 844 }] });
console.log(format(result));
await session.close();
许可证
MIT
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。