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

+102
View File
@@ -0,0 +1,102 @@
/**
* 通过 CDP 截图,用于无人值守环境下的界面功能验证。
*
* 用法:
* node scripts/shot.mjs <port> <outfile>
*/
import { writeFile } from 'node:fs/promises'
const port = Number(process.argv[2] ?? 9335)
const outFile = process.argv[3] ?? 'shot.png'
async function main() {
const listRes = await fetch(`http://127.0.0.1:${port}/json/list`)
const targets = await listRes.json()
const page = targets.find((t) => t.type === 'page')
if (!page) {
console.error('未找到 page 目标。当前目标:', targets.map((t) => `${t.type}:${t.url}`).join(', '))
process.exit(1)
}
console.log('目标页面:', page.url)
const ws = new WebSocket(page.webSocketDebuggerUrl)
let id = 0
const pending = new Map()
const send = (method, params = {}) =>
new Promise((resolve, reject) => {
const msgId = ++id
const timer = setTimeout(() => {
pending.delete(msgId)
reject(new Error(`CDP 调用超时: ${method}`))
}, 8000)
pending.set(msgId, (value) => {
clearTimeout(timer)
resolve(value)
})
ws.send(JSON.stringify({ id: msgId, method, params }))
})
await new Promise((resolve, reject) => {
const timer = setTimeout(() => reject(new Error('WebSocket 连接超时(8s)')), 8000)
ws.addEventListener('open', () => {
clearTimeout(timer)
resolve()
})
ws.addEventListener('error', (e) => {
clearTimeout(timer)
reject(new Error(`WebSocket 错误: ${e?.message ?? 'unknown'}`))
})
})
console.log('CDP 已连接')
ws.addEventListener('message', (ev) => {
const msg = JSON.parse(ev.data)
if (msg.id && pending.has(msg.id)) {
pending.get(msg.id)(msg.result)
pending.delete(msg.id)
}
})
await send('Page.enable')
// 等首屏与字体稳定
await new Promise((r) => setTimeout(r, 2500))
const metrics = await send('Runtime.evaluate', {
expression: `(() => {
const c = document.createElement('canvas')
const gl = c.getContext('webgl2') || c.getContext('webgl')
const dbg = gl && gl.getExtension('WEBGL_debug_renderer_info')
return JSON.stringify({
title: document.title,
theme: document.documentElement.dataset.theme,
hasApi: typeof window.ycode === 'object',
gpuRenderer: dbg ? gl.getParameter(dbg.UNMASKED_RENDERER_WEBGL) : (gl ? 'webgl(no-debug-ext)' : 'NO WEBGL'),
bodyText: (document.body.innerText || '').slice(0, 400),
shell: !!document.querySelector('.app-shell'),
columns: document.querySelector('.app-shell')
? getComputedStyle(document.querySelector('.app-shell')).gridTemplateColumns : null,
composer: !!document.querySelector('.composer__input'),
sessionItems: document.querySelectorAll('.sidebar__item-title').length,
panels: document.querySelectorAll('.panel').length
})
})()`,
returnByValue: true
})
console.log('DOM 状态:', metrics?.result?.value)
const shot = await send('Page.captureScreenshot', { format: 'png' })
if (!shot?.data) {
console.error('截图失败')
process.exit(1)
}
await writeFile(outFile, Buffer.from(shot.data, 'base64'))
console.log('已保存:', outFile)
ws.close()
}
main().catch((err) => {
console.error('异常:', err.message)
process.exit(1)
})