Files
admin be34c630bb Fix blank drawings, drop parse-time animation cost, add render diagnostics
Accuracy / completeness
- docViews: split parsed scene into model space / paper space / free-standing
  blocks (geometry hidden in uninserted blocks no longer shows as a blank
  canvas); auto-select the first block view when model space is empty
- viewer: view switcher panel listing every detected view with expanded
  entity counts; paper space is now parsed (includePaperSpace) and kept apart
- SHX: missing .shx references are surfaced with an import control
  (addReferenceFile), linetypes/text redraw afterwards
- unsupported/proxy entities: markers on canvas + diagnostics block showing
  proxy / wipeout counts and missing SHX names

Speed
- the loading overlay used an infinite CSS spinner + backdrop blur while the
  worker parsed; on software rendering that starved the parse thread
  (TELECOM 18MB: 71s -> 30s wall clock, normalize 33s -> 14s). Replaced with a
  determinate progress bar fed by loader progress events
- transferInputBuffer for worker loads, maxInsertDepth 32, longer worker timeout

Tooling
- scripts/probe-dwg.mjs / probe-app.mjs: headless render + end-to-end probes
  (entity/layer/block/layout/proxy stats, timings, screenshots)
- tools/render-probe.html, scripts/profile-worker.mjs
- version 0.6.0 (versionCode 8)
2026-10-26 20:28:37 +08:00

136 lines
5.0 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
// 端到端检查真实 App(index.html → 打开文件 → 渲染 → 视图面板),输出状态文本与截图。
// 用法:node scripts/probe-app.mjs [文件 ...]
import fs from 'node:fs'
import path from 'node:path'
import { spawn } from 'node:child_process'
import { chromium } from 'playwright-core'
const root = process.cwd()
const ven = path.resolve(root, '..', 'ven')
const outDir = path.join(root, 'tools', 'out')
const port = Number(process.env.PROBE_PORT || 5199)
fs.mkdirSync(outDir, { recursive: true })
const SUPPORTED = /\.(dwg|dxf|pdf|epub|xps|oxps|cbz|png|jpe?g|gif|bmp)$/i
let files = process.argv.slice(2).filter((a) => SUPPORTED.test(a))
if (!files.length) {
const dir = path.join(root, 'testfile')
files = fs.readdirSync(dir).filter((n) => SUPPORTED.test(n)).map((n) => path.join('testfile', n))
}
const chrome = [process.env.CHROME_PATH, path.join(ven, 'chrome-for-testing', 'chrome-linux64', 'chrome')].find(
(p) => p && fs.existsSync(p)
)
const vite = spawn(
process.execPath,
[path.join(root, 'node_modules', 'vite', 'bin', 'vite.js'), '--port', String(port), '--strictPort', '--host', '127.0.0.1'],
{ cwd: root, stdio: 'ignore' }
)
await new Promise((r) => setTimeout(r, 3000))
const browser = await chromium.launch({
executablePath: chrome,
args: [
'--no-sandbox',
'--use-gl=angle',
'--use-angle=swiftshader',
'--enable-unsafe-swiftshader',
'--disable-dev-shm-usage',
'--js-flags=--max-old-space-size=4096'
]
})
const report = []
try {
for (const file of files) {
const abs = path.resolve(root, file)
const label = path.basename(abs)
const page = await browser.newPage({ viewport: { width: 1100, height: 860 }, deviceScaleFactor: 1 })
const errors = []
page.on('pageerror', (e) => errors.push(String(e.message).slice(0, 200)))
await page.goto(`http://127.0.0.1:${port}/index.html`, { waitUntil: 'load' })
if (process.env.PROBE_NO_ANIM === '1') {
await page.addStyleTag({ content: '*{animation:none!important;transition:none!important;backdrop-filter:none!important}' })
}
const t0 = Date.now()
await page.setInputFiles('input[type=file]', abs)
let status = ''
try {
await page.waitForFunction(
() => {
const el = document.querySelector('.viewer-status')
const text = el?.textContent || ''
return text.includes('实体')
},
null,
{ timeout: 180000 }
)
status = (await page.textContent('.viewer-status')) || ''
} catch {
status = `(超时) ${(await page.textContent('.viewer-status').catch(() => '')) || ''}`
}
const elapsed = Date.now() - t0
await page.waitForTimeout(800)
const progress = await page.evaluate(() => window.__progress || null)
if (progress) console.log(' worker 进度:', progress.map((p) => `${p.ms}ms/${p.phase}`).join(' '))
const timings = await page.evaluate(() => window.__loadTimings || null)
if (timings) console.log(' 阶段耗时:', JSON.stringify(timings))
const shot = path.join(outDir, `app-${label.replace(/[^\w.\u4e00-\u9fa5-]/g, '_')}.png`)
await page.screenshot({ path: shot })
// 打开“视图与图层”面板,检查视图列表与诊断信息
let panel = ''
try {
await page.click('[data-action="layers"]', { timeout: 5000 })
await page.waitForTimeout(500)
const sheetShot = shot.replace(/\.png$/, '-panel.png')
await page.screenshot({ path: sheetShot })
panel = (await page.textContent('.sheet').catch(() => '')) || ''
} catch (error) {
panel = `面板打开失败: ${error.message.slice(0, 80)}`
}
// 点第一个“游离块”视图,确认它确实是一张能看的图纸
let viewShot = ''
try {
const rows = await page.$$('.view-row')
if (rows.length > 1) {
await rows[1].click()
await page.waitForTimeout(1200)
viewShot = shot.replace(/\.png$/, '-view1.png')
await page.screenshot({ path: viewShot })
}
} catch {}
console.log(`\n=== ${label} ===`)
console.log(` 打开+渲染墙钟 ${elapsed}ms`)
console.log(` 状态栏: ${status.trim()}`)
const views = panel.match(/[MLB][^\n]{0,60}实体/g)?.slice(0, 6) ?? []
if (views.length) console.log(` 视图列表: ${views.map((v) => v.replace(/\s+/g, ' ')).join(' | ')}`)
const diag = panel.match(/渲染提示[^\n]{0,160}/)?.[0]
if (diag) console.log(` 诊断: ${diag.replace(/\s+/g, ' ')}`)
if (errors.length) console.log(` 页面错误: ${errors.slice(0, 2).join(' | ')}`)
report.push({
file: label,
elapsed,
status: status.trim(),
views,
diag,
errors,
screenshot: path.relative(root, shot),
viewScreenshot: viewShot ? path.relative(root, viewShot) : undefined
})
await page.close()
}
} finally {
await browser.close()
vite.kill('SIGTERM')
}
fs.writeFileSync(path.join(outDir, 'app-report.json'), JSON.stringify(report, null, 2))
setTimeout(() => process.exit(0), 200)