| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081 |
- #!/usr/bin/env node
- /**
- * lx.js — 布局紧凑渲染器(Layout eXtract)
- *
- * 把 MCP `device_get_layout` 的 JSON 结果渲染成一行一个控件的紧凑列表,
- * 避免把 25KB 的原始 JSON 直接塞进上下文。
- *
- * 用法:
- * node _probe/lx.js <layout.json> [文案过滤词]
- * node _probe/lx.js <layout.json> --all # 含 dropped / scrollables 统计
- *
- * 输入既可以是工具返回的原样 JSON,也可以是 {"content":[{"text":"<json>"}]} 形态的 MCP 响应。
- */
- 'use strict';
- const fs = require('fs');
- function readPayload(file) {
- const raw = fs.readFileSync(file, 'utf8');
- const parsed = JSON.parse(raw);
- if (Array.isArray(parsed.content) && parsed.content[0] && parsed.content[0].text) {
- return readPayloadValue(JSON.parse(parsed.content[0].text));
- }
- return readPayloadValue(parsed);
- }
- /** 工具返回可能把布局挂在 tree 字段下,这里统一成 {nodes, dropped, scrollables} */
- function readPayloadValue(value) {
- if (value && value.tree && Array.isArray(value.tree.nodes)) {
- return { device: value.device, ...value.tree };
- }
- return value;
- }
- function shorten(value, max = 60) {
- const text = String(value == null ? '' : value).replace(/\s+/g, ' ');
- return text.length > max ? `${text.slice(0, max - 1)}…` : text;
- }
- function renderNode(node) {
- const flags = [
- node.clk ? 'clk' : '-',
- node.sc ? 'scr' : '-',
- node.ed ? 'ed' : '-',
- node.opaque ? 'shell' : '-',
- ].join(' ');
- const label = shorten(node.text || node.desc || '');
- const parent = node.pt ? ` <-${shorten(node.pt, 18)}` : '';
- return [
- String(node.ref).padEnd(12),
- String(node.cls || '').padEnd(16),
- flags,
- `(${node.cx},${node.cy})`.padEnd(12),
- `[${(node.b || []).join(',')}]`.padEnd(26),
- label + parent,
- ].join(' ');
- }
- function main() {
- const [file, filter] = process.argv.slice(2);
- const layout = readPayload(file);
- const nodes = layout.nodes || [];
- const shown = filter && filter !== '--all'
- ? nodes.filter((n) => (String(n.text || '') + String(n.desc || '')).includes(filter))
- : nodes;
- console.log(`# 节点 ${nodes.length} → 显示 ${shown.length}${layout.truncated ? ' (truncated)' : ''}`);
- shown.forEach((node) => console.log(renderNode(node)));
- if (layout.scrollables && layout.scrollables.length) {
- console.log('\n# scrollables');
- layout.scrollables.forEach((s) => {
- console.log(` ${s.ref} dir=${s.dir} [${(s.b || []).join(',')}]`);
- });
- }
- if (layout.dropped && (filter === '--all' || !filter)) {
- console.log(`\n# dropped ${JSON.stringify(layout.dropped)}`);
- }
- }
- main();
|