fix(shell): 去掉所有窗口级玻璃(白框/黑块根因);feat: 多会话切换 + 文件桥(桌面壳 HTTP 网关 403 的解法)
白框根因:SetWindowCompositionAttribute(Win10 亚克力) / DwmExtendFrameIntoClientArea(客户区变玻璃) / WebView2 Transparent 背景 三者在真机上都会让 8px 边环露白、WebView2 变黑。现在窗口永远由 应用自绘深色,背景效果只作为页面侧半透明(不再碰 DWM 与窗口 alpha)。 新增: - 会话切换:标题栏 ⇄ 按钮 + 列表(会话标题/轮/步/消息数,可'跟随最新');宿主侧 pinnedSessionId, 被选中的会话不再被其它会话的事件抢走;snapshot 里带 sessions[] - 文件桥:桌面壳对未认证请求一律 403(实测 6 种请求形态),浮窗改走文件——宿主每秒写 snapshot.json, 浮窗渲染;浮窗的 chat/watch/newSession 写 outbox.json,宿主轮询执行。端到端实测通过 - 编码修复:快照按显式 UTF-8 读(PS 5.1 Get-Content -Raw 会按 GBK 读坏中文 JSON); outbox 写 UTF-8 无 BOM
This commit is contained in:
+47
-17
@@ -67,6 +67,9 @@ $alreadyRunning = $false
|
||||
try { $null = [System.Threading.Mutex]::OpenExisting($mutexName); $alreadyRunning = $true } catch { $alreadyRunning = $false }
|
||||
$commandFile = Join-Path $stateDir 'command.json'
|
||||
$aliveFile = Join-Path $stateDir 'alive.json'
|
||||
# 文件桥:插件每秒写 snapshot.json(桌面壳 HTTP 网关 403,浮窗只能走文件)
|
||||
$snapshotFile = Join-Path $DshHome 'float-window\snapshot.json'
|
||||
$outboxFile = Join-Path $DshHome 'float-window\outbox.json'
|
||||
if ($alreadyRunning) {
|
||||
if ($Action) {
|
||||
try {
|
||||
@@ -127,6 +130,7 @@ namespace DshFloat
|
||||
[StructLayout(LayoutKind.Sequential)] struct WINCOMPDATA { public int Attrib; public IntPtr Data; public int Size; }
|
||||
|
||||
[DllImport("dwmapi.dll")] static extern int DwmSetWindowAttribute(IntPtr h, int attr, ref int val, int size);
|
||||
[DllImport("dwmapi.dll")] static extern int DwmGetWindowAttribute(IntPtr h, int attr, out int val, int size);
|
||||
[DllImport("dwmapi.dll")] static extern int DwmExtendFrameIntoClientArea(IntPtr h, ref MARGINS m);
|
||||
[DllImport("user32.dll")] static extern int SetWindowCompositionAttribute(IntPtr h, ref WINCOMPDATA d);
|
||||
|
||||
@@ -135,12 +139,17 @@ namespace DshFloat
|
||||
BackdropMode = mode;
|
||||
int type = mode == 1 ? 2 : (mode == 2 ? 3 : 0); // DWMSBT_MAINWINDOW / TRANSIENTWINDOW / AUTO
|
||||
int hr = DwmSetWindowAttribute(Handle, 38, ref type, 4); // DWMWA_SYSTEMBACKDROP_TYPE
|
||||
int got = -1;
|
||||
int hrGet = DwmGetWindowAttribute(Handle, 38, out got, 4);
|
||||
bool effective = mode == 0 ? true : (hr == 0 && hrGet == 0 && got == type);
|
||||
// 关键 1:**不再**把客户区扩展成"玻璃"(真机上会让 8px 边环与 WebView2 露出白底)
|
||||
// 关键 2:**不要**再用 SetWindowCompositionAttribute 的 ACCENT_ENABLE_ACRYLICBLURBEHIND,
|
||||
// 那是 Win10 时代的未文档化做法,Win11 上会把窗口变成纯黑(实测 PrintWindow 全黑)。
|
||||
// 现在窗口始终由应用自己画深色,系统背景只当装饰,"霜化"观感交给页面自己的半透明。
|
||||
MARGINS m = new MARGINS();
|
||||
if (mode != 0) { m.l = -1; m.r = -1; m.t = -1; m.b = -1; }
|
||||
DwmExtendFrameIntoClientArea(Handle, ref m);
|
||||
SetAccent(mode == 3 ? 4 : 0, mode == 0 ? 0 : unchecked((int)0x99202028));
|
||||
Invalidate(true);
|
||||
return "mode=" + mode + " hr=" + hr;
|
||||
return "mode=" + mode + " hr=" + hr + " got=" + got + " effective=" + effective;
|
||||
}
|
||||
|
||||
void SetAccent(int state, int color)
|
||||
@@ -156,16 +165,15 @@ namespace DshFloat
|
||||
Marshal.FreeHGlobal(p);
|
||||
}
|
||||
|
||||
// 始终由应用自己画背景(深色):玻璃模式下 DWM 若没生效也不会露出白底
|
||||
protected override void OnPaintBackground(PaintEventArgs e)
|
||||
{
|
||||
if (BackdropMode == 0) base.OnPaintBackground(e);
|
||||
base.OnPaintBackground(e);
|
||||
}
|
||||
|
||||
protected override void WndProc(ref Message m)
|
||||
{
|
||||
const int WM_NCHITTEST = 0x84;
|
||||
const int WM_ERASEBKGND = 0x14;
|
||||
if (m.Msg == WM_ERASEBKGND && BackdropMode != 0) { m.Result = (IntPtr)1; return; }
|
||||
if (m.Msg == WM_NCHITTEST && AllowResize)
|
||||
{
|
||||
long lp = m.LParam.ToInt64();
|
||||
@@ -275,20 +283,27 @@ function Apply-Exclude {
|
||||
|
||||
function Apply-Backdrop([string]$Mode) {
|
||||
if (-not $Mode) { $Mode = 'opaque' }
|
||||
# 真机结论:这台机器上任何"窗口级"的系统背景(DWM 系统背景类型 / Win10 亚克力 /
|
||||
# 把客户区扩展成玻璃)都会让 WebView2 内容变黑、8px 边环露白底(用户原话"边框有白色的框")。
|
||||
# 所以窗口**永远**保持应用自己绘制的深色,"背景效果"只作为页面侧的半透明/霜化观感,
|
||||
# 页面 alpha 由不透明度滑杆控制。
|
||||
$script:state.backdrop = $Mode
|
||||
$code = switch ($Mode) { 'mica' { 1 } 'acrylic' { 2 } 'blur' { 3 } 'translucent' { 0 } default { 0 } }
|
||||
try {
|
||||
$info = $form.ApplyBackdrop($code)
|
||||
Log "backdrop $Mode -> $info"
|
||||
} catch { Log "backdrop ERROR: $($_.Exception.Message)" }
|
||||
# 玻璃模式必须让 WebView2 背景透明,否则页面自己把桌面糊住了
|
||||
if ($wv.CoreWebView2) {
|
||||
try {
|
||||
$wv.DefaultBackgroundColor = if ($code -eq 0) { [System.Drawing.Color]::FromArgb(255, 15, 17, 21) } else { [System.Drawing.Color]::Transparent }
|
||||
} catch { Log "bg color ERROR: $($_.Exception.Message)" }
|
||||
try { $wv.DefaultBackgroundColor = [System.Drawing.Color]::FromArgb(255, 15, 17, 21) } catch { }
|
||||
}
|
||||
# 半透明:整窗 alpha(WebView2 是子窗口,这条路径观感一般,作为兜底)
|
||||
if ($Mode -eq 'translucent') { Apply-Opacity $script:state.opacity } else { Set-WindowAlpha 1.0 }
|
||||
Set-WindowAlpha 1.0
|
||||
Log "backdrop $Mode (page-level only)"
|
||||
Push-State
|
||||
}
|
||||
|
||||
# 页面 -> 宿主:写 outbox.json(UTF-8 无 BOM,宿主侧 JSON.parse 才吃得下)
|
||||
function Write-Outbox([hashtable]$Msg) {
|
||||
try {
|
||||
$Msg.at = (Get-Date).ToString('o')
|
||||
$json = $Msg | ConvertTo-Json -Compress
|
||||
[System.IO.File]::WriteAllText($outboxFile, $json, (New-Object System.Text.UTF8Encoding($false)))
|
||||
Log "outbox $($Msg.cmd) ($($json.Length)B)"
|
||||
} catch { Log "outbox write failed: $($_.Exception.Message)" }
|
||||
}
|
||||
|
||||
function Set-WindowAlpha([double]$value) {
|
||||
@@ -371,6 +386,9 @@ $onWebMessage = {
|
||||
'backdrop' { Apply-Backdrop ([string]$m.value); Save-State; Push-State }
|
||||
'scale' { $script:state.scale = [double]$m.value; $wv.ZoomFactor = [double]$m.value; Save-State; Push-State }
|
||||
'reload' { $wv.Reload() }
|
||||
'chat' { Write-Outbox @{ cmd = 'chat'; text = [string]$m.text } }
|
||||
'watch' { Write-Outbox @{ cmd = 'watch'; sessionId = [string]$m.sessionId } }
|
||||
'newSession' { Write-Outbox @{ cmd = 'session'; action = 'create' } }
|
||||
'openExternal' { if ($m.url -match '^https?://') { Start-Process $m.url } }
|
||||
}
|
||||
}
|
||||
@@ -391,6 +409,7 @@ function Invoke-Cmd([string]$c) {
|
||||
|
||||
$script:lastEndpointText = ''
|
||||
$script:lastCommandText = ''
|
||||
$script:lastSnapshotText = ''
|
||||
$timer = New-Object System.Windows.Forms.Timer
|
||||
$timer.Interval = 700
|
||||
$timer.Add_Tick({
|
||||
@@ -401,6 +420,17 @@ $timer.Add_Tick({
|
||||
if ($t -ne $script:lastEndpointText) { $script:lastEndpointText = $t; Send-Endpoint }
|
||||
}
|
||||
} catch {}
|
||||
# 文件桥:宿主每秒写 snapshot.json(桌面壳的 HTTP 网关会 403,浮窗只能走文件)
|
||||
# 必须显式按 UTF-8 读:PS 5.1 的 Get-Content -Raw 会按 GBK 解码,中文摘要会破坏 JSON
|
||||
try {
|
||||
if (Test-Path $snapshotFile) {
|
||||
$s = [System.IO.File]::ReadAllText($snapshotFile, [System.Text.Encoding]::UTF8)
|
||||
if ($s -and $s -ne $script:lastSnapshotText) {
|
||||
$script:lastSnapshotText = $s
|
||||
Send-ToPage @{ type = 'snapshot'; data = ($s | ConvertFrom-Json) }
|
||||
}
|
||||
}
|
||||
} catch { Log "snapshot error: $($_.Exception.Message)" }
|
||||
# 插件/命令行下发的动作
|
||||
try {
|
||||
if (Test-Path $commandFile) {
|
||||
|
||||
Reference in New Issue
Block a user