chore: establish YCode replication baseline and TODO

This commit is contained in:
xiaohan.yuan committed 2026-10-17 02:43:49 +08:00
commit 6dc59f89ba
1118 files changed
+196002

No files matched your search

+170
View File
@@ -0,0 +1,170 @@
import './wb-legacy-disabled.mjs'
/**
* 采集首页的垂直结构(各区块 y 坐标与尺寸)。
*
* 用法:node scripts/wb-home-layout.mjs <port> [outJson]
*/
import { writeFile } from 'node:fs/promises'
const port = Number(process.argv[2] ?? 9223)
const outJson = process.argv[3] ?? null
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 SEL = [
'.workbuddy-topbar',
'.conversation-sidebar',
'[class*="welcome"]',
'[class*="greeting"]',
'.wb-segmented',
'.quick-actions',
'.quick-actions__list',
'[class*="input-box"]',
'[class*="cr-input"]',
'.cr-input-container',
'.cr-input-editor-host',
'[class*="permission-setting"]',
'.cr-input-footer',
'[class*="suggestion"]',
'[class*="quick-prompt"]',
'img[class*="mascot"]',
'[class*="mascot"]',
'[class*="empty"]',
'[class*="scene"]'
];
const clean = (v) => (v == null ? '' : String(v).replace(/\\s+/g, ' ').trim());
const out = [];
for (const sel of SEL) {
let nodes = [];
try { nodes = [...document.querySelectorAll(sel)]; } catch { continue; }
if (!nodes.length) continue;
out.push({
sel,
count: nodes.length,
items: nodes.slice(0, 6).map((el) => {
const r = el.getBoundingClientRect();
const cs = getComputedStyle(el);
return {
cls: clean(el.className).slice(0, 90),
rect: [Math.round(r.x), Math.round(r.y), Math.round(r.width), Math.round(r.height)],
display: cs.display,
flexDirection: cs.flexDirection,
alignItems: cs.alignItems,
justifyContent: cs.justifyContent,
gap: cs.gap,
padding: cs.padding,
margin: cs.margin,
radius: cs.borderRadius,
bg: cs.backgroundColor,
shadow: cs.boxShadow === 'none' ? '' : cs.boxShadow,
fontSize: cs.fontSize,
fontWeight: cs.fontWeight
};
})
});
}
// 主内容容器的直接子元素序列(看垂直堆叠顺序)
const side = document.querySelector('.conversation-sidebar');
const sideRight = side ? side.getBoundingClientRect().right : 240;
let best = null, bestArea = 0;
document.querySelectorAll('div').forEach((el) => {
const r = el.getBoundingClientRect();
if (r.x >= sideRight - 4 && r.width > 500 && r.height > 300) {
const a = r.width * r.height;
if (a > bestArea) { bestArea = a; best = el; }
}
});
let stack = [];
if (best) {
const walk = (el, depth, path) => {
if (depth > 4 || stack.length > 60) return;
[...el.children].forEach((c, i) => {
const r = c.getBoundingClientRect();
if (r.width < 60 || r.height < 8) return;
stack.push({
depth,
path: path + '/' + i,
tag: c.tagName.toLowerCase(),
cls: clean(c.className).slice(0, 70),
rect: [Math.round(r.x), Math.round(r.y), Math.round(r.width), Math.round(r.height)],
text: clean(c.textContent).slice(0, 40)
});
walk(c, depth + 1, path + '/' + i);
});
};
walk(best, 0, '');
}
return JSON.stringify({ viewport: { w: innerWidth, h: innerHeight }, out, stack });
})()`
const r = await send('Runtime.evaluate', { expression: expr, returnByValue: true })
const data = JSON.parse(r.result.value)
console.log(`viewport ${data.viewport.w}x${data.viewport.h}\n`)
for (const entry of data.out) {
console.log(`### ${entry.sel} (${entry.count})`)
for (const it of entry.items) {
console.log(` .${it.cls}`)
console.log(` rect ${JSON.stringify(it.rect)} display=${it.display}/${it.flexDirection} align=${it.alignItems} justify=${it.justifyContent} gap=${it.gap}`)
console.log(` pad=${it.padding} margin=${it.margin}`)
console.log(` radius=${it.radius} bg=${it.bg} fs=${it.fontSize}/${it.fontWeight}`)
if (it.shadow) console.log(` shadow=${it.shadow}`)
}
console.log('')
}
console.log('### 垂直堆叠(y 升序)')
const sorted = [...data.stack].sort((a, b) => a.rect[1] - b.rect[1])
for (const s of sorted) {
console.log(` y=${String(s.rect[1]).padStart(4)} h=${String(s.rect[3]).padStart(4)} x=${String(s.rect[0]).padStart(4)} w=${String(s.rect[2]).padStart(4)} ${' '.repeat(s.depth)}${s.tag}.${s.cls.split(/\s+/)[0]} "${s.text}"`)
}
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)
})