import './wb-legacy-disabled.mjs' /** * 导出真实 WorkBuddy 某个子树的结构与样式(CDP)。 * * 用法:node scripts/wb-tree.mjs [maxNodes] * 例: node scripts/wb-tree.mjs 9223 .conversation-sidebar 200 */ import { writeFile, mkdir } from 'node:fs/promises' import { join } from 'node:path' const port = Number(process.argv[2] ?? 9223) const rootSelector = process.argv[3] ?? '.conversation-sidebar' const maxNodes = Number(process.argv[4] ?? 200) const outDir = process.argv[5] ?? null const PROPS = [ 'display', 'flex-direction', 'align-items', 'justify-content', 'width', 'min-width', 'max-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', 'box-shadow', 'font-size', 'font-weight', 'line-height', 'letter-spacing', 'text-transform', 'opacity', 'overflow-y', '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' && !/windowKind=/.test(t.url))[0] if (!page) { console.error('未找到主页面目标') process.exit(1) } const ws = new WebSocket(page.webSocketDebuggerUrl) await new Promise((resolve, reject) => { const timer = setTimeout(() => reject(new Error('连接超时')), 10000) ws.addEventListener('open', () => { clearTimeout(timer) resolve() }) ws.addEventListener('error', () => { clearTimeout(timer) reject(new Error('连接失败')) }) }) const send = (method, params = {}) => new Promise((resolve, reject) => { const id = Math.floor(Math.random() * 1e9) const timer = setTimeout(() => reject(new Error(`超时 ${method}`)), 20000) const handler = (ev) => { const msg = JSON.parse(ev.data) if (msg.id === id) { clearTimeout(timer) ws.removeEventListener('message', handler) resolve(msg.result) } } ws.addEventListener('message', handler) ws.send(JSON.stringify({ id, method, params })) }) const expression = `(() => { const ROOT = ${JSON.stringify(rootSelector)}; const PROPS = ${JSON.stringify(PROPS)}; const MAX = ${maxNodes}; const clean = (v) => (v == null ? '' : String(v).replace(/\\s+/g, ' ').trim()); const root = document.querySelector(ROOT); if (!root) return JSON.stringify({ error: 'selector not found: ' + ROOT }); const rows = []; const walk = (el, depth) => { if (rows.length >= MAX) return; const cs = getComputedStyle(el); const r = el.getBoundingClientRect(); if (r.width < 1 || r.height < 1) return; const st = {}; 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)') st[p] = v; } rows.push({ depth, tag: el.tagName.toLowerCase(), cls: clean(el.className).slice(0, 120), text: clean(el.childElementCount === 0 ? el.textContent : '').slice(0, 70), rect: { x: Math.round(r.x), y: Math.round(r.y), w: Math.round(r.width), h: Math.round(r.height) }, styles: st }); for (const c of el.children) walk(c, depth + 1); }; walk(root, 0); return JSON.stringify({ selector: ROOT, viewport: { w: innerWidth, h: innerHeight }, rows }); })()` const res = await send('Runtime.evaluate', { expression, returnByValue: true }) const data = JSON.parse(res.result.value) if (data.error) { console.error(data.error) process.exit(1) } console.log(`root=${data.selector} viewport=${data.viewport.w}x${data.viewport.h} nodes=${data.rows.length}`) const lines = [] for (const row of data.rows) { const indent = ' '.repeat(row.depth) const cls = row.cls ? '.' + row.cls.split(/\s+/).slice(0, 2).join('.') : '' const txt = row.text ? ` "${row.text}"` : '' lines.push(`${indent}${row.tag}${cls} [${row.rect.x},${row.rect.y} ${row.rect.w}x${row.rect.h}]${txt}`) const keys = Object.keys(row.styles) if (keys.length) { lines.push(`${indent} ${JSON.stringify(row.styles)}`) } } const text = lines.join('\n') console.log(text.slice(0, 12000)) if (outDir) { await mkdir(outDir, { recursive: true }) await writeFile(join(outDir, 'tree.json'), JSON.stringify(data, null, 2), 'utf8') console.log(`\n已写出 ${join(outDir, 'tree.json')}`) } ws.close() } main().catch((err) => { console.error('失败:', err.message) process.exit(1) })