chore: establish YCode replication baseline and TODO
This commit is contained in:
commit
6dc59f89ba
1118 files changed
+196002
No files matched your search
@@ -0,0 +1,140 @@
|
||||
import './wb-legacy-disabled.mjs'
|
||||
|
||||
/**
|
||||
* 首页补充探测:大标题、输入框下方行、建议列表。
|
||||
*
|
||||
* 用法:node scripts/wb-home-parts.mjs <port>
|
||||
*/
|
||||
|
||||
const port = Number(process.argv[2] ?? 9223)
|
||||
|
||||
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]
|
||||
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 clean = (v) => (v == null ? '' : String(v).replace(/\\s+/g, ' ').trim());
|
||||
const R = (el) => { const r = el.getBoundingClientRect(); return [Math.round(r.x), Math.round(r.y), Math.round(r.width), Math.round(r.height)]; };
|
||||
const info = (el) => {
|
||||
if (!el) return null;
|
||||
const cs = getComputedStyle(el);
|
||||
return {
|
||||
tag: el.tagName.toLowerCase(),
|
||||
cls: clean(el.className).slice(0, 80),
|
||||
rect: R(el),
|
||||
fontSize: cs.fontSize,
|
||||
fontWeight: cs.fontWeight,
|
||||
lineHeight: cs.lineHeight,
|
||||
color: cs.color,
|
||||
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,
|
||||
border: cs.borderTopWidth + ' ' + cs.borderTopColor
|
||||
};
|
||||
};
|
||||
|
||||
// 1) 最大字号元素(标题)
|
||||
let heading = null, maxFs = 0;
|
||||
document.querySelectorAll('body *').forEach((el) => {
|
||||
const r = el.getBoundingClientRect();
|
||||
if (r.y < 30 || r.y > 350 || r.width < 40 || r.height < 10) return;
|
||||
if (el.children.length > 2) return;
|
||||
const cs = getComputedStyle(el);
|
||||
const fs = parseFloat(cs.fontSize);
|
||||
if (fs > maxFs) { maxFs = fs; heading = el; }
|
||||
});
|
||||
|
||||
// 2) 输入框外层容器的全部子元素(含下方那行)
|
||||
const box = document.querySelector('[class*="home-route__input-box"]') || document.querySelector('.cr-input-box');
|
||||
const boxKids = box ? [...box.children].map((c) => ({ ...info(c), text: clean(c.textContent).slice(0, 50) })) : [];
|
||||
|
||||
// 3) 建议列表:找 y > 520 且高度 32 左右的成组元素
|
||||
const suggCandidates = [];
|
||||
document.querySelectorAll('button, [role="button"], div, li').forEach((el) => {
|
||||
const r = el.getBoundingClientRect();
|
||||
if (r.y < 500 || r.y > 800 || r.height < 24 || r.height > 44 || r.width < 120 || r.width > 700) return;
|
||||
const cs = getComputedStyle(el);
|
||||
if (cs.borderRadius === '0px' && cs.backgroundColor === 'rgba(0, 0, 0, 0)') return;
|
||||
const t = clean(el.textContent);
|
||||
if (!t || t.length > 50) return;
|
||||
suggCandidates.push({ ...info(el), text: t });
|
||||
});
|
||||
|
||||
return JSON.stringify({
|
||||
viewport: { w: innerWidth, h: innerHeight },
|
||||
heading: info(heading),
|
||||
box: info(box),
|
||||
boxKids,
|
||||
sugg: suggCandidates.slice(0, 12),
|
||||
suggParent: suggCandidates[0] ? info(suggCandidates[0].parentElement) : null,
|
||||
suggGrand: suggCandidates[0] ? info(suggCandidates[0].parentElement?.parentElement) : null
|
||||
});
|
||||
})()`
|
||||
|
||||
const r = await send('Runtime.evaluate', { expression: expr, returnByValue: true })
|
||||
const d = JSON.parse(r.result.value)
|
||||
|
||||
console.log(`viewport ${d.viewport.w}x${d.viewport.h}`)
|
||||
const show = (label, o) => {
|
||||
if (!o) return console.log(`\n### ${label}\n (无)`)
|
||||
console.log(`\n### ${label}`)
|
||||
console.log(` ${o.tag}.${o.cls} rect ${JSON.stringify(o.rect)}`)
|
||||
console.log(` fs=${o.fontSize}/${o.fontWeight} lh=${o.lineHeight} color=${o.color}`)
|
||||
console.log(` display=${o.display}/${o.flexDirection} align=${o.alignItems} justify=${o.justifyContent} gap=${o.gap}`)
|
||||
console.log(` pad=${o.padding} margin=${o.margin} radius=${o.radius} bg=${o.bg} border=${o.border}`)
|
||||
if (o.text) console.log(` text="${o.text}"`)
|
||||
}
|
||||
show('大标题', d.heading)
|
||||
show('输入框外层 .cr-input-box', d.box)
|
||||
console.log(`\n### 输入框外层的子元素(${d.boxKids.length})`)
|
||||
for (const k of d.boxKids) {
|
||||
console.log(` ${k.tag}.${k.cls} ${JSON.stringify(k.rect)} gap=${k.gap} pad=${k.padding} radius=${k.radius} bg=${k.bg}`)
|
||||
if (k.text) console.log(` "${k.text}"`)
|
||||
}
|
||||
show('建议项父级', d.suggParent)
|
||||
show('建议项祖父', d.suggGrand)
|
||||
console.log(`\n### 建议项候选(${d.sugg.length})`)
|
||||
for (const s of d.sugg) {
|
||||
console.log(` ${s.tag}.${s.cls} ${JSON.stringify(s.rect)} radius=${s.radius} bg=${s.bg} border=${s.border} fs=${s.fontSize} "${s.text}"`)
|
||||
}
|
||||
ws.close()
|
||||
}
|
||||
|
||||
main().catch((e) => {
|
||||
console.error('失败:', e.message)
|
||||
process.exit(1)
|
||||
})
|
||||
Reference in new issue
Block a user