From 5f12ea3df9de86a6c6066e8f6f3b193f960e5bb3 Mon Sep 17 00:00:00 2001 From: JianFeeeee <109188060+JianFeeeee@users.noreply.github.com> Date: Tue, 6 Oct 2026 13:09:12 +0800 Subject: [PATCH] =?UTF-8?q?fix(gui):=20=E8=A1=A5=E6=98=9F=E5=9B=BE?= =?UTF-8?q?=E9=9D=A2=E6=9D=BF=E7=BC=BA=E5=A4=B1=E7=9A=84=20=EF=BC=8C?= =?UTF-8?q?=E4=BF=AE=E5=AF=B9=E8=AF=9D=E9=A1=B5=205=20=E4=B8=AA=E9=A1=B5?= =?UTF-8?q?=E7=AD=BE=E7=A9=BA=E7=99=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 实测故障 对话页里「终端 / 运行中命令 / 记忆 / 上下文 / 知识」五个页签点进去全是 空白,只有「对话」和「星图」正常。 ## 根因:少一个 构造星图面板的那段 HTML 里有 **4 个 `
` 开、只有 3 个 `
` 闭**, 它没闭合 —— 后面拼接的所有 chat-panel 都被浏览器塞进 chat-panel-starmap **内部**。 CDP 查真实 DOM 的铁证: .chat-layout 的直接子节点只有 3 个: DIV.chat-tabs / DIV#chat-panel-chat / DIV#chat-panel-starmap terminal/cmd/memory/context/knowledge 的父节点 = chat-panel-starmap ← 不是同级的 chat-layout 后果:这些面板 display 是flex(switchChatPanel 把 active 加对了), 但父级 display:none ⇒ getBoundingClientRect() 恒为 0x0 ⇒ 显示为空白。 实测尺寸对照(修复前 → 修复后): terminal 0x0 → 1156x621 cmd 0x0 → 1156x621 memory 0x0 → 1156x621 context 0x0 → 1156x621 knowledge 0x0 → 1156x621 ## 为什么一直没被发现 标签不配对在源码里看不出来(跨行字符串拼接的 HTML 片段),浏览器也 **不报错** —— 它只是「尽力修复」成畸形结构。现象是「点了没反应/一片空白」, 很容易被归因为数据没到或后端挂了(我最初就查了半天气网络与接口 401)。 而且它只在部分面板上发作:chat 与 starmap 恰好是前两个,位置靠前不受影响, 于是「大部分能用」掩盖了「后面全废」。 ## 判据(cmd/gui/chat-panel-dom.test.mjs) 3 条:整段 div 标签配平、各面板独立配平、各面板顶层不被嵌套进前一面板。 逐面板断言能定位到**具体是哪个**面板不配平(变异测试下报 `starmap(开4/闭3)`),而不只是「总数差了 1」。 变异测试:去掉一个 `` ⇒ 前两条转红并指出 starmap。 判据自身也错过一次:还原 HTML 时把 `"` 全替换成 `'`,而 marker 用的是双引号, 7 个面板全报「未找到」。已修正并记在注释里。 --- cmd/gui/chat-panel-dom.test.mjs | 149 ++++++++++++++++++++++++++++++++ cmd/gui/renderer/app.js | 2 +- 2 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 cmd/gui/chat-panel-dom.test.mjs diff --git a/cmd/gui/chat-panel-dom.test.mjs b/cmd/gui/chat-panel-dom.test.mjs new file mode 100644 index 00000000..cf56a594 --- /dev/null +++ b/cmd/gui/chat-panel-dom.test.mjs @@ -0,0 +1,149 @@ +// chat 子面板 DOM 结构判据(2026-10-05)。 +// +// ## 要判的是什么 +// +// 实测故障:对话页里「终端 / 运行中命令 / 记忆 / 上下文 / 知识」五个页签 +// 点进去全是空白,只有「对话」和「星图」正常。 +// +// 根因是 **HTML 少一个 **:构造星图面板的那段 HTML 里有 4 个 +//
开、只有 3 个
闭,于是它没闭合 —— 后面拼接的所有 +// chat-panel 都被浏览器塞进 chat-panel-starmap **内部**。 +// +// 实测证据(CDP 查真实 DOM): +// +// .chat-layout 的直接子节点只有 3 个: +// DIV.chat-tabs / DIV#chat-panel-chat / DIV#chat-panel-starmap +// +// 而 terminal/cmd/memory/context/knowledge 的父节点是 +// chat-panel-starmap —— 不是同级的 chat-layout。 +// +// 后果:这些面板 display 是 flex(switchChatPanel 把 active 加对了), +// 但父级 display:none ⇒ getBoundingClientRect() 恒为 0x0 ⇒ 看起来空白。 +// +// ★ 为什么需要判据:标签不配对在源码里完全看不出来(字符串拼接 + +// 跨行的 HTML 片段),浏览器也不报错 —— 它只是「尽力修复」成畸形结构。 +// 只有断言「开闭标签数相等」才能挡住。 +// +// 运行:node chat-panel-dom.test.mjs + +import { readFileSync, existsSync } from "node:fs"; +import { fileURLToPath } from "node:url"; +import { dirname, join } from "node:path"; + +const here = dirname(fileURLToPath(import.meta.url)); +const APP = join(here, "renderer", "app.js"); + +let failures = 0; +const check = (name, ok, detail) => { + if (ok) { + console.log(` ✓ ${name}`); + } else { + failures++; + console.log(` ✗ ${name}${detail ? " — " + detail : ""}`); + } +}; + +if (!existsSync(APP)) { + console.error(`找不到 ${APP}`); + process.exit(1); +} +const src = readFileSync(APP, "utf8"); + +// ── 1) 抽出构造 chat 子面板的整段 HTML ───────────────────────────── +// +// 从第一个 chat-panel 面板开始,到最后一个面板结束。 + +const startIdx = src.indexOf( + `'
`, +); +if (startIdx < 0) { + console.error("定位不到 chat 面板段落 —— app.js 结构可能已变"); + process.exit(1); +} +const lastIdx = src.indexOf("chat-panel-knowledge"); +const seg = src.slice(startIdx, lastIdx + 4000); + +// 把跨行拼接的字符串还原成一段连续 HTML: +// '...' + \n '...' → '...' +const html = seg + .replace(/'\s*\+\s*\n\s*'/g, "") + .replace(/'\s*\+\s*'/g, "") + .replace(/"/g, "'"); + +const opens = (html.match(//g) || []).length; + +check( + "chat 面板段落 div 标签配平", + opens === closes, + `开 ${opens} / 闭 ${closes},差 ${opens - closes} —— ` + + "少一个
会让后面所有面板被塞进前一个面板内部(表现为空白)", +); + +// ── 2) 每个 chat-panel 自身也必须配平 ───────────────────────────── +// +// 总数配平还不够:某处多一个 、另一处少一个,总数仍为 0。 +// 逐面板断言能定位到具体是哪个。 + +const PANELS = ["chat", "starmap", "terminal", "cmd", "memory", "context", "knowledge"]; +const unbalanced = []; +for (const name of PANELS) { + const marker = `id='chat-panel-${name}'`; + const at = html.indexOf(marker); + if (at < 0) { + unbalanced.push(`${name}(未找到)`); + continue; + } + // 从该面板的
/g) || []).length; + if (o !== c) unbalanced.push(`${name}(开${o}/闭${c})`); +} +check( + "各子面板独立配平", + unbalanced.length === 0, + unbalanced.length ? "不配平:" + unbalanced.join("、") : "", +); + +// ── 3) 所有 chat-panel 必须同属一个父容器 ───────────────────────── +// +// 形态判据:源码里每个面板片段的起始处都应是独立的
'(一个字符串字面量的结束)或 +(拼接)。 + +const nested = []; +for (const name of PANELS) { + const marker = `id='chat-panel-${name}'`; + const at = html.indexOf(marker); + if (at < 0) continue; + const from = html.lastIndexOf(" ${opens} /
${closes}`); +console.log(` 面板数: ${PANELS.length}`); +console.log(""); +if (failures > 0) { + console.log(`失败 ${failures} 条`); + process.exit(1); +} +console.log("全部通过"); \ No newline at end of file diff --git a/cmd/gui/renderer/app.js b/cmd/gui/renderer/app.js index db15e5bd..6b0cc5ee 100644 --- a/cmd/gui/renderer/app.js +++ b/cmd/gui/renderer/app.js @@ -1873,7 +1873,7 @@ function buildChatLayout() { '

' + __("星图", "Star Map") + "

" + - '
'; + '
'; html += '

' + __("终端", "Terminal") +