153 lines
4.5 KiB
JavaScript
153 lines
4.5 KiB
JavaScript
import './wb-legacy-disabled.mjs'
|
|
|
|
/**
|
|
* 采集 WorkBuddy 首页/新建任务页(空态)结构与样式。
|
|
*
|
|
* 用法:node scripts/wb-home.mjs <port> [outJson]
|
|
*/
|
|
|
|
import { writeFile } from 'node:fs/promises'
|
|
|
|
const port = Number(process.argv[2] ?? 9223)
|
|
const outJson = process.argv[3] ?? null
|
|
|
|
const PROPS = [
|
|
'display',
|
|
'flex-direction',
|
|
'flex-wrap',
|
|
'align-items',
|
|
'justify-content',
|
|
'width',
|
|
'height',
|
|
'min-height',
|
|
'padding-top',
|
|
'padding-right',
|
|
'padding-bottom',
|
|
'padding-left',
|
|
'margin-top',
|
|
'margin-bottom',
|
|
'gap',
|
|
'background-color',
|
|
'color',
|
|
'border-top-width',
|
|
'border-top-color',
|
|
'border-radius',
|
|
'font-size',
|
|
'font-weight',
|
|
'line-height',
|
|
'box-shadow',
|
|
'position'
|
|
]
|
|
|
|
async function main() {
|
|
const list = await (await fetch(`http://127.0.0.1:${port}/json/list`)).json()
|
|
const page = list.filter((t) => t.type === 'page')[0]
|
|
if (!page) throw new Error('未找到页面')
|
|
|
|
const ws = new WebSocket(page.webSocketDebuggerUrl)
|
|
await new Promise((r, j) => {
|
|
const t = setTimeout(() => j(new Error('连接超时')), 8000)
|
|
ws.addEventListener('open', () => {
|
|
clearTimeout(t)
|
|
r()
|
|
})
|
|
ws.addEventListener('error', () => {
|
|
clearTimeout(t)
|
|
j(new Error('连接失败'))
|
|
})
|
|
})
|
|
|
|
const send = (method, params = {}) =>
|
|
new Promise((res, rej) => {
|
|
const id = Math.floor(Math.random() * 1e9)
|
|
const timer = setTimeout(() => rej(new Error(`超时 ${method}`)), 25000)
|
|
const h = (ev) => {
|
|
const m = JSON.parse(ev.data)
|
|
if (m.id === id) {
|
|
clearTimeout(timer)
|
|
ws.removeEventListener('message', h)
|
|
res(m.result)
|
|
}
|
|
}
|
|
ws.addEventListener('message', h)
|
|
ws.send(JSON.stringify({ id, method, params }))
|
|
})
|
|
|
|
const expr = `(() => {
|
|
const PROPS = ${JSON.stringify(PROPS)};
|
|
const clean = (v) => (v == null ? '' : String(v).replace(/\\s+/g, ' ').trim());
|
|
const picked = (el) => {
|
|
const cs = getComputedStyle(el);
|
|
const o = {};
|
|
for (const p of PROPS) {
|
|
const v = clean(cs.getPropertyValue(p));
|
|
if (v && v !== 'normal' && v !== 'auto' && v !== 'none' && v !== '0px' && v !== 'rgba(0, 0, 0, 0)') o[p] = v;
|
|
}
|
|
return o;
|
|
};
|
|
const rect = (el) => { const r = el.getBoundingClientRect(); return [Math.round(r.x), Math.round(r.y), Math.round(r.width), Math.round(r.height)]; };
|
|
|
|
// 主内容区:从侧栏右侧开始、宽度最大的顶层容器
|
|
const side = document.querySelector('.conversation-sidebar');
|
|
const sideRight = side ? side.getBoundingClientRect().right : 240;
|
|
let root = null;
|
|
document.querySelectorAll('body *').forEach((el) => {
|
|
const r = el.getBoundingClientRect();
|
|
if (r.x >= sideRight - 2 && r.width > 400 && r.height > 400 && r.y < 100) {
|
|
if (!root || r.width * r.height > root.getBoundingClientRect().width * root.getBoundingClientRect().height) root = el;
|
|
}
|
|
});
|
|
if (!root) return JSON.stringify({ error: '未找到主内容区' });
|
|
|
|
const rows = [];
|
|
const walk = (el, depth) => {
|
|
if (depth > 10 || rows.length > 160) return;
|
|
const r = el.getBoundingClientRect();
|
|
if (r.width < 4 || r.height < 4) return;
|
|
rows.push({
|
|
d: depth,
|
|
tag: el.tagName.toLowerCase(),
|
|
cls: clean(el.className).slice(0, 100),
|
|
text: el.children.length === 0 ? clean(el.textContent).slice(0, 50) : '',
|
|
rect: rect(el),
|
|
st: picked(el)
|
|
});
|
|
for (const c of el.children) walk(c, depth + 1);
|
|
};
|
|
walk(root, 0);
|
|
|
|
return JSON.stringify({
|
|
viewport: { w: innerWidth, h: innerHeight },
|
|
rootCls: clean(root.className).slice(0, 120),
|
|
rows
|
|
});
|
|
})()`
|
|
|
|
const r = await send('Runtime.evaluate', { expression: expr, returnByValue: true })
|
|
const data = JSON.parse(r.result.value)
|
|
if (data.error) {
|
|
console.error(data.error)
|
|
process.exit(1)
|
|
}
|
|
|
|
console.log(`viewport ${data.viewport.w}x${data.viewport.h} root=.${data.rootCls}`)
|
|
for (const n of data.rows) {
|
|
const cls = n.cls ? '.' + n.cls.split(/\s+/).slice(0, 2).join('.') : ''
|
|
const txt = n.text ? ` "${n.text}"` : ''
|
|
console.log(`${' '.repeat(n.d)}${n.tag}${cls} [${n.rect.join(',')}]${txt}`)
|
|
const keys = Object.keys(n.st)
|
|
if (keys.length) console.log(`${' '.repeat(n.d)} ${JSON.stringify(n.st)}`)
|
|
}
|
|
|
|
if (outJson) {
|
|
await writeFile(outJson, JSON.stringify(data, null, 2), 'utf8')
|
|
console.log(`\n已写出 ${outJson}`)
|
|
}
|
|
ws.close()
|
|
}
|
|
|
|
main().catch((e) => {
|
|
console.error('失败:', e.message)
|
|
process.exit(1)
|
|
})
|