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,171 @@
|
||||
import './wb-legacy-disabled.mjs'
|
||||
|
||||
/**
|
||||
* 按文案定位元素并导出其与父级的样式 —— 用于采集那些类名不稳定(CSS Module 哈希)的界面。
|
||||
*
|
||||
* 用法:node scripts/wb-probe.mjs <port> <文案1,文案2,...> [outJson]
|
||||
*/
|
||||
|
||||
import { writeFile } from 'node:fs/promises'
|
||||
|
||||
const port = Number(process.argv[2] ?? 9223)
|
||||
const texts = (process.argv[3] ?? '').split(',').map((s) => s.trim()).filter(Boolean)
|
||||
const outJson = process.argv[4] ?? 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 TEXTS = ${JSON.stringify(texts)};
|
||||
const PROPS = ${JSON.stringify(PROPS)};
|
||||
const clean = (v) => (v == null ? '' : String(v).replace(/\\s+/g, ' ').trim());
|
||||
const pick = (el) => {
|
||||
if (!el) return null;
|
||||
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 R = (el) => { if (!el) return null; const r = el.getBoundingClientRect(); return [Math.round(r.x), Math.round(r.y), Math.round(r.width), Math.round(r.height)]; };
|
||||
|
||||
const out = {};
|
||||
for (const t of TEXTS) {
|
||||
let best = null;
|
||||
document.querySelectorAll('*').forEach((el) => {
|
||||
if (el.children.length !== 0) return;
|
||||
if (clean(el.textContent) !== t) return;
|
||||
const r = el.getBoundingClientRect();
|
||||
if (r.width <= 0 || r.height <= 0) return;
|
||||
if (!best || r.width * r.height < best.getBoundingClientRect().width * best.getBoundingClientRect().height) best = el;
|
||||
});
|
||||
if (!best) { out[t] = null; continue; }
|
||||
const p1 = best.parentElement;
|
||||
const p2 = p1?.parentElement ?? null;
|
||||
out[t] = {
|
||||
self: { tag: best.tagName.toLowerCase(), cls: clean(best.className).slice(0, 90), rect: R(best), st: pick(best) },
|
||||
parent: p1 ? { tag: p1.tagName.toLowerCase(), cls: clean(p1.className).slice(0, 90), rect: R(p1), st: pick(p1) } : null,
|
||||
grand: p2 ? { tag: p2.tagName.toLowerCase(), cls: clean(p2.className).slice(0, 90), rect: R(p2), st: pick(p2) } : null
|
||||
};
|
||||
}
|
||||
|
||||
// 输入区
|
||||
const inp = document.querySelector('textarea, [contenteditable="true"], input[type="text"]');
|
||||
out.__input = inp
|
||||
? {
|
||||
tag: inp.tagName.toLowerCase(),
|
||||
cls: clean(inp.className).slice(0, 90),
|
||||
rect: R(inp),
|
||||
placeholder: inp.getAttribute('placeholder') || inp.getAttribute('data-placeholder') || '',
|
||||
parent: inp.parentElement
|
||||
? { cls: clean(inp.parentElement.className).slice(0, 90), rect: R(inp.parentElement), st: pick(inp.parentElement) }
|
||||
: null,
|
||||
grand: inp.parentElement?.parentElement
|
||||
? { cls: clean(inp.parentElement.parentElement.className).slice(0, 90), rect: R(inp.parentElement.parentElement), st: pick(inp.parentElement.parentElement) }
|
||||
: null
|
||||
}
|
||||
: null;
|
||||
|
||||
return JSON.stringify({ viewport: { w: innerWidth, h: innerHeight }, out });
|
||||
})()`
|
||||
|
||||
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}`)
|
||||
for (const [key, v] of Object.entries(data.out)) {
|
||||
console.log(`\n### ${key}`)
|
||||
if (!v) {
|
||||
console.log(' (未找到)')
|
||||
continue
|
||||
}
|
||||
if (key === '__input') {
|
||||
console.log(` ${v.tag}.${v.cls} ${JSON.stringify(v.rect)} placeholder="${v.placeholder}"`)
|
||||
if (v.parent) console.log(` ↑ .${v.parent.cls} ${JSON.stringify(v.parent.rect)} ${JSON.stringify(v.parent.st)}`)
|
||||
if (v.grand) console.log(` ↑↑ .${v.grand.cls} ${JSON.stringify(v.grand.rect)} ${JSON.stringify(v.grand.st)}`)
|
||||
continue
|
||||
}
|
||||
console.log(` 自身 ${v.self.tag}.${v.self.cls} ${JSON.stringify(v.self.rect)}`)
|
||||
console.log(` ${JSON.stringify(v.self.st)}`)
|
||||
if (v.parent) {
|
||||
console.log(` 父级 ${v.parent.tag}.${v.parent.cls} ${JSON.stringify(v.parent.rect)}`)
|
||||
console.log(` ${JSON.stringify(v.parent.st)}`)
|
||||
}
|
||||
if (v.grand) {
|
||||
console.log(` 祖父 ${v.grand.tag}.${v.grand.cls} ${JSON.stringify(v.grand.rect)}`)
|
||||
console.log(` ${JSON.stringify(v.grand.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)
|
||||
})
|
||||
Reference in new issue
Block a user