feat(parity): G3 visual parity harness; first old-vs-gpui comparison recorded
Fixed sample sheet (boxdraw families, blocks/shades/quadrants/sextants, Powerline separators, CJK alignment ruler, 256/truecolor bars) injected via the focus-free ConPTY injector, captured with PrintWindow PW_RENDERFULLCONTENT. First pass: structural parity across the board - boxes closed and seamless, CJK ruler column-aligned, Powerline triangles crisper on the GPUI side thanks to boxdraw geometry. Follow-ups: same-theme comparison after config sharing, real fastfetch once installed, zoomed stroke-weight audit.
This commit is contained in:
1 parent
b46dee5cf9
commit
ebfa958e54
2 files changed
+149
-2
No files matched your search
+8
-2
@@ -56,8 +56,14 @@
|
||||
全部 0% CPU)。旧壳发行版始终带侧载对所以从未暴露。修复列入 P2;
|
||||
运维规则:任何 nebula 系 exe 旁必须部署侧载对。
|
||||
- **1b IME 清单**:输出一页人工验收清单,等用户有空亲手敲。
|
||||
- **1c 视觉对账集**:fastfetch + powerline 提示符 + TUI 边框在新旧两壳的
|
||||
并排截图脚本与基准图。
|
||||
- **1c 视觉对账集**:`scripts/visual_parity.ps1`——固定样张(boxdraw 全家
|
||||
桶、块/浓度/象限、Powerline 分隔符、CJK 对齐标尺、256 色/真彩)注入
|
||||
两壳并截图(`.tmp_parity_<tag>.png`)。
|
||||
**首轮对账 2026-08-13:结构级持平**——框线闭合无缝、CJK 标尺逐列对
|
||||
齐、Powerline 三角在 GPUI 侧(boxdraw 几何)边缘更干净;`mixed/half/
|
||||
trans` 几行两壳同样偏怪(旧壳像素栅格化的既有观感,非回归)。
|
||||
待办:config 共享后做同主题对比;fastfetch 未安装(装上即可入集);
|
||||
放大级笔画粗细对账。
|
||||
|
||||
### P2 实验场补齐终端体验(在 `nebula_gpui` 快速迭代)
|
||||
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
# 视觉对账(G3 闸门):向终端注入一份固定样张,截图存档,供新旧两壳
|
||||
# 并排对比。样张覆盖 GPUI 迁移最容易翻车的渲染面:
|
||||
# boxdraw 全家桶(单/重/双线、圆角、虚线、对角线)、块元素与浓度块、
|
||||
# 象限块、Powerline 分隔符(三角/箭头/圆头)、CJK 对齐标尺、
|
||||
# 256 色与真彩渐变。
|
||||
# fastfetch 若在 PATH 上则追加真实 fastfetch 输出。
|
||||
#
|
||||
# 用法:
|
||||
# .\scripts\visual_parity.ps1 -Exe <nebula.exe> -Tag winit-old -ExeArgs '--working-directory D:\temp_build\nebula'
|
||||
# .\scripts\visual_parity.ps1 -Exe <nebula-gpui.exe> -Tag gpui-lab
|
||||
param(
|
||||
[Parameter(Mandatory = $true)][string]$Exe,
|
||||
[Parameter(Mandatory = $true)][string]$Tag,
|
||||
[string]$ExeArgs = '',
|
||||
[int]$SettleSec = 4
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$root = Split-Path -Parent $PSScriptRoot
|
||||
$contentFile = Join-Path $root '.tmp_parity_content.ps1'
|
||||
$injector = Join-Path $PSScriptRoot 'perf_inject.ps1'
|
||||
$outPng = Join-Path $root ".tmp_parity_$Tag.png"
|
||||
|
||||
# ---- 1. 样张(每次重写,保持权威版本在本脚本内)---------------------------
|
||||
$e = [char]27
|
||||
@"
|
||||
`$e = [char]27
|
||||
Write-Host "== visual parity sample =="
|
||||
Write-Host '-- boxdraw: light / heavy / double / rounded / dashes / diagonals'
|
||||
Write-Host '┌───┬───┐ ┏━━━┳━━━┓ ╔═══╦═══╗ ╭───╮'
|
||||
Write-Host '│ a │ b │ ┃ c ┃ d ┃ ║ e ║ f ║ │ g │'
|
||||
Write-Host '├───┼───┤ ┣━━━╋━━━┫ ╠═══╬═══╣ ╰───╯'
|
||||
Write-Host '└───┴───┘ ┗━━━┻━━━┛ ╚═══╩═══╝ ┄┄┅┅┈┈┉┉ ╌╌╍╍'
|
||||
Write-Host 'mixed: ┍┑┕┙ ┎┒┖┚ ╒╕╘╛ ╓╖╙╜ half: ╴╵╶╷╸╹╺╻ trans: ╼╽╾╿'
|
||||
Write-Host 'diag: ╱╲╳╱╲╳ vertical dashes: ┆┇┊┋╎╏'
|
||||
Write-Host '-- blocks / shades / quadrants'
|
||||
Write-Host '▁▂▃▄▅▆▇█ ▏▎▍▌▋▊▉█ ░░▒▒▓▓██ ▀▄▌▐▔▕'
|
||||
Write-Host '▖▗▘▝▚▞▙▛▜▟ legacy: 🬀🬁🬂🬃🬄🬅🬆🬇🬈🬉🬊'
|
||||
Write-Host '-- powerline separators'
|
||||
Write-Host "`$e[48;5;25m`$e[38;5;15m one `$e[0m`$e[38;5;25m`$e[48;5;208m$([char]0xE0B0)`$e[38;5;15m two `$e[0m`$e[38;5;208m`$e[48;5;28m$([char]0xE0B0)`$e[38;5;15m three `$e[0m`$e[38;5;28m$([char]0xE0B0)`$e[0m done"
|
||||
Write-Host "chevrons: $([char]0xE0B1) $([char]0xE0B1) rtl: $([char]0xE0B2) $([char]0xE0B3) round: $([char]0xE0B4) $([char]0xE0B6)"
|
||||
Write-Host '-- CJK alignment ruler (竖线必须逐列对齐)'
|
||||
Write-Host '|12345678901234567890123456789012|'
|
||||
Write-Host '|中文宽字符对齐测试一二三四五六七八|'
|
||||
Write-Host '|あいうえおかきくけこさしすせそたち|'
|
||||
Write-Host '|ab中cd文ef字gh宽ij齐kl测mn试op对qr|'
|
||||
Write-Host '-- colors'
|
||||
`$bar = ''
|
||||
foreach (`$i in 16..51) { `$bar += "`$e[48;5;`${i}m " }
|
||||
Write-Host "`$bar`$e[0m"
|
||||
`$grad = ''
|
||||
foreach (`$i in 0..31) { `$r = `$i * 8; `$grad += "`$e[48;2;`${r};64;`$((255 - `$r))m " }
|
||||
Write-Host "`$grad`$e[0m"
|
||||
if (Get-Command fastfetch -ErrorAction SilentlyContinue) { fastfetch }
|
||||
Write-Host '== end of sample =='
|
||||
"@ | Set-Content -Path $contentFile -Encoding UTF8
|
||||
|
||||
# ---- 2. Win32 助手 ----------------------------------------------------------
|
||||
Add-Type @'
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
public class ParityWin {
|
||||
[DllImport("user32.dll")] static extern bool EnumWindows(EnumProc cb, IntPtr lp);
|
||||
[DllImport("user32.dll")] static extern uint GetWindowThreadProcessId(IntPtr h, out uint pid);
|
||||
[DllImport("user32.dll")] static extern bool IsWindowVisible(IntPtr h);
|
||||
[DllImport("user32.dll")] public static extern bool GetWindowRect(IntPtr h, out RECT r);
|
||||
[DllImport("user32.dll")] public static extern bool SetWindowPos(IntPtr h, IntPtr after, int x, int y, int w, int hh, uint flags);
|
||||
[DllImport("user32.dll")] public static extern bool PrintWindow(IntPtr h, IntPtr dc, uint flags);
|
||||
public struct RECT { public int L, T, R, B; }
|
||||
delegate bool EnumProc(IntPtr h, IntPtr lp);
|
||||
public static List<IntPtr> WindowsOf(uint pid) {
|
||||
var list = new List<IntPtr>();
|
||||
EnumWindows((h, lp) => {
|
||||
uint p; GetWindowThreadProcessId(h, out p);
|
||||
if (p == pid && IsWindowVisible(h)) list.Add(h);
|
||||
return true;
|
||||
}, IntPtr.Zero);
|
||||
return list;
|
||||
}
|
||||
}
|
||||
'@
|
||||
Add-Type -AssemblyName System.Drawing
|
||||
Add-Type -MemberDefinition '[DllImport("shcore.dll")] public static extern int SetProcessDpiAwareness(int value);' -Name Dpi -Namespace ParityDpi
|
||||
[void][ParityDpi.Dpi]::SetProcessDpiAwareness(2)
|
||||
|
||||
function Get-MainWindow([int]$ProcessId) {
|
||||
for ($i = 0; $i -lt 40; $i++) {
|
||||
$wins = [ParityWin]::WindowsOf($ProcessId)
|
||||
if ($wins.Count -gt 0) {
|
||||
$best = $null; $bestW = -1
|
||||
foreach ($h in $wins) {
|
||||
$r = New-Object ParityWin+RECT
|
||||
[void][ParityWin]::GetWindowRect($h, [ref]$r)
|
||||
if (($r.R - $r.L) -gt $bestW) { $bestW = $r.R - $r.L; $best = $h }
|
||||
}
|
||||
return $best
|
||||
}
|
||||
Start-Sleep -Milliseconds 500
|
||||
}
|
||||
throw "no visible window for pid $ProcessId"
|
||||
}
|
||||
|
||||
# ---- 3. 驱动:启动 → 注入 → 截图 → 清理 ------------------------------------
|
||||
Write-Host "launching $Exe $ExeArgs"
|
||||
$proc = if ($ExeArgs) {
|
||||
Start-Process -FilePath $Exe -ArgumentList $ExeArgs -PassThru
|
||||
} else {
|
||||
Start-Process -FilePath $Exe -PassThru
|
||||
}
|
||||
Start-Sleep -Seconds 7
|
||||
if ($proc.HasExited) { throw "terminal exited immediately (mux hand-over? pass -ExeArgs)" }
|
||||
$hwnd = Get-MainWindow -ProcessId $proc.Id
|
||||
# 可见但不抢焦点(HWND_TOPMOST, SWP_NOSIZE|NOMOVE|NOACTIVATE)。
|
||||
[void][ParityWin]::SetWindowPos($hwnd, [IntPtr](-1), 0, 0, 0, 0, 0x13)
|
||||
|
||||
$command = "powershell -NoProfile -ExecutionPolicy Bypass -File $contentFile"
|
||||
$ok = $false
|
||||
for ($attempt = 0; $attempt -lt 5; $attempt++) {
|
||||
$out = & powershell -NoProfile -ExecutionPolicy Bypass -File $injector `
|
||||
-HostPid $proc.Id -Text $command -Enter 2>&1 | Out-String
|
||||
if ($out -match 'ok=True') { $ok = $true; break }
|
||||
Start-Sleep -Seconds 2
|
||||
}
|
||||
if (-not $ok) { Stop-Process -Id $proc.Id -Force; throw "inject failed: $out" }
|
||||
Start-Sleep -Seconds $SettleSec
|
||||
|
||||
$r = New-Object ParityWin+RECT
|
||||
[void][ParityWin]::GetWindowRect($hwnd, [ref]$r)
|
||||
$w = $r.R - $r.L; $h = $r.B - $r.T
|
||||
$bmp = New-Object System.Drawing.Bitmap $w, $h
|
||||
$g = [System.Drawing.Graphics]::FromImage($bmp)
|
||||
$dc = $g.GetHdc()
|
||||
# PW_RENDERFULLCONTENT = 2:GPU 合成(D3D/GL)表面必需。
|
||||
$shot = [ParityWin]::PrintWindow($hwnd, $dc, 2)
|
||||
$g.ReleaseHdc($dc); $g.Dispose()
|
||||
$bmp.Save($outPng, [System.Drawing.Imaging.ImageFormat]::Png)
|
||||
$bmp.Dispose()
|
||||
Write-Host "shot ok=$shot ${w}x${h} -> $outPng"
|
||||
|
||||
Stop-Process -Id $proc.Id -Force -ErrorAction SilentlyContinue
|
||||
Reference in new issue
Block a user