diff --git a/client/electron/package.json b/client/electron/package.json
index 3505e71..366242d 100644
--- a/client/electron/package.json
+++ b/client/electron/package.json
@@ -19,7 +19,7 @@
"build:linux": "vite build && electron-builder --linux",
"preview": "vite preview",
"typecheck": "tsc --noEmit",
- "test": "node test/markdown-xss.test.mjs && node test/narrow-layout.test.mjs && node test/theme.test.mjs && node test/background.test.mjs && node test/packaging.test.mjs && vitest run",
+ "test": "node test/markdown-xss.test.mjs && node test/narrow-layout.test.mjs && node test/theme.test.mjs && node test/background.test.mjs && node test/cross-client-theme.test.mjs && node test/packaging.test.mjs && vitest run",
"test:narrow": "node test/manual/narrow-verify.mjs",
"test:wide": "node test/manual/wide-regression.mjs",
"test:components": "vitest run",
diff --git a/client/electron/scripts/gen-background-takeover.mjs b/client/electron/scripts/gen-background-takeover.mjs
index 26123fa..76073fb 100644
--- a/client/electron/scripts/gen-background-takeover.mjs
+++ b/client/electron/scripts/gen-background-takeover.mjs
@@ -66,8 +66,18 @@ const body = classes
await writeFile(
OUT,
+ /*
+ * ★ 头注释里**不许写出「星号紧接斜杠」的 glob 字面量**(2026-09-14 修)。
+ *
+ * 这里原先写的是「src」加「/」加两颗星加「/」加「.tsx」,而中间那对
+ * 「星号 + 斜杠」会把 CSS 注释**提前闭合**:剩下的尾巴(`.tsx 的实际用法)。`)
+ * 变成 CSS 正文,跟后面第一条规则的**选择器连在一起** —— 非法选择器 ⇒
+ * **那条规则被浏览器整条丢掉**(`.bg-amber-100` 在壁纸模式下不再变半透明),
+ * 而构建只在日志里给一条 `[WARNING] Unexpected "14"`,不报错、不影响构建。
+ * 所以这里只描述、不写 glob 字面量。
+ */
`/* 由 scripts/gen-background-takeover.mjs 生成 —— 不要手改。\n` +
- ` * 背景开启时把源码里在用的浅色表面类改成半透明(清单来自 src/**/*.tsx 的实际用法)。\n` +
+ ` * 背景开启时把源码里在用的浅色表面类改成半透明(清单来自 src 目录下全部 .tsx 的实际用法)。\n` +
` * 共 ${classes.length} 个类:${classes.join(', ')}\n */\n\n${body}\n`,
);
console.log(` 生成 ${classes.length} 个类 → ${OUT}`);
diff --git a/client/electron/src/background-takeover.generated.css b/client/electron/src/background-takeover.generated.css
index 7416e85..0d24aef 100644
--- a/client/electron/src/background-takeover.generated.css
+++ b/client/electron/src/background-takeover.generated.css
@@ -1,5 +1,5 @@
/* 由 scripts/gen-background-takeover.mjs 生成 —— 不要手改。
- * 背景开启时把源码里在用的浅色表面类改成半透明(清单来自 src/**/*.tsx 的实际用法)。
+ * 背景开启时把源码里在用的浅色表面类改成半透明(清单来自 src 目录下全部 .tsx 的实际用法)。
* 共 14 个类:bg-amber-100, bg-amber-50, bg-blue-100, bg-blue-50, bg-gray-100, bg-gray-200, bg-green-100, bg-green-50, bg-orange-100, bg-orange-200, bg-orange-50, bg-red-100, bg-red-50, bg-yellow-100
*/
diff --git a/client/electron/src/components/Sidebar.tsx b/client/electron/src/components/Sidebar.tsx
index 7fbaf76..e2dd684 100644
--- a/client/electron/src/components/Sidebar.tsx
+++ b/client/electron/src/components/Sidebar.tsx
@@ -133,7 +133,15 @@ export default function Sidebar() {
(见 App 里的 ComposeFab),导航栏就只剩三项。
*/}
-
+ {/*
+ 底部这一块原先还是**旧的深色导航假设**(外层 border-chrome-700、
+ 两个按钮 text-chrome-400 + hover:bg-chrome-800)—— 导航改成白玻璃
+ (--nav-bg: 255 255 255 / 0.72)之后,chrome-400 落在白底上只有约 2.6:1
+ (图标要 3:1),hover 还会在白导航上闪出一块近黑。账号头像那个按钮当时
+ 换成了 .nav-item,这两个漏了 —— 就是"导航令牌迁移做了一半"。
+ chrome 那条色阶是给"框架本来就该深"的假设用的,白玻璃导航不该再用它。
+ */}
+
-
+ {/*
+ .nav-item 是未分层 CSS,级联上压得过 Tailwind utilities,
+ 所以直接替掉类名即可,不必再补 text-*。
+ */}
+
diff --git a/client/electron/test/background.test.mjs b/client/electron/test/background.test.mjs
index 99e8836..97dd71a 100644
--- a/client/electron/test/background.test.mjs
+++ b/client/electron/test/background.test.mjs
@@ -215,6 +215,30 @@ check('新组件未使用未映射色族', unmapped.length === 0, unmapped.join(
const PAGE_BASE = new Set(['bg-gray-50', 'bg-slate-100']); // 页面底必须保持全透明
const missing = [...used].filter(c => !PAGE_BASE.has(c) && !gen.includes(`.${c} {`));
check('源码里用到的浅色表面类全被接管', missing.length === 0, `漏了:${missing.join(', ')}`);
+ /*
+ * ★ 生成文件的**头注释不能提前闭合**(2026-09-14 修,与上面那条同一根因的两个后果)。
+ *
+ * 生成器原先在头注释里写了「src」加「/」加两颗星加「/」加「.tsx」,其中那对
+ * 「星号 + 斜杠」把注释**提前闭合**:尾巴变成 CSS 正文,跟第一条规则的选择器
+ * 连在一起成为非法选择器 ⇒ **那条规则被浏览器整条丢掉**(`bg-amber-100` 在
+ * 壁纸模式下不再变半透明)。构建只给一条 `[WARNING] Unexpected "14"`,
+ * 不报错、不影响构建 —— 正是这套判据存在的理由。
+ *
+ * 判据:把注释剥掉之后,文件必须以第一条规则的**选择器**起头。
+ */
+ const stripped = gen.replace(/\/\*[\s\S]*?\*\//g, '').trim();
+ check(
+ '生成 CSS 的头注释没有提前闭合(否则第一条规则会被整条丢掉)',
+ stripped.startsWith("html[data-bg='on'] .bg-"),
+ `剥掉注释后以「${stripped.slice(0, 40)}…」起头`
+ );
+ // 反向对照:判据要真能抓到"注释里带星号+斜杠"这种写法
+ const bad = "/* 来自 src/**/*.tsx 的用法 */\nhtml[data-bg='on'] .bg-amber-100 { color: red }";
+ check(
+ '判据自检:注释里带「星号紧接斜杠」时必须判红',
+ !bad.replace(/\/\*[\s\S]*?\*\//g, '').trim().startsWith("html[data-bg='on'] .bg-"),
+ '自检失败 —— 这条判据抓不到它要抓的东西'
+ );
check('页面底没有被写进半透明清单', [...PAGE_BASE].every(b => !gen.includes(`.${b} {`)));
check('清单不是空跑(至少扫到 10 个类)', used.size >= 10, `实际 ${used.size}`);
@@ -239,16 +263,42 @@ check('新组件未使用未映射色族', unmapped.length === 0, unmapped.join(
* 黑字或者自动反色或者描边呢?」):
* ① 不透明实心(最早)
* ② 深色玻璃(我为了"白字对比度"做的 —— 用错误的方式解决对比度,全页唯一一块黑)
- * ③ **按主题走的令牌**:浅色=白玻璃+深字、深色=深玻璃+亮字
+ * ③ **按主题走的令牌**:浅色=白玻璃+深字(深色主题尚未存在,见下)
* 判据因此不再断言某个固定颜色,而是断言"走令牌"这件事 + 对比度由
* test/manual/nav-contrast-verify.mjs 用 WCAG 比值来量。
*/
- check('导航底色走 --nav-bg 令牌(浅色/深色各一套,不写死)',
+ check('导航底色走 --nav-bg 令牌(不写死)',
/\.nav-rail \{[\s\S]{0,80}background-color: rgb\(var\(--nav-bg\)\)/.test(css) &&
/\.nav-item \{[\s\S]{0,80}color: rgb\(var\(--nav-fg-muted\)\)/.test(css));
- check('浅色与深色两套令牌都定义了(自动反色)',
- /--nav-bg: 255 255 255 \/ 0\.72/.test(css) && /--nav-bg: 15 23 42 \/ 0\.72/.test(css));
- check('导航在壁纸模式下仍参与模糊', /html\[data-bg='on'\] \.nav-rail,[\s\S]{0,200}backdrop-filter: blur\(/.test(css));
+ /*
+ * ★ 下面两条 2026-09-14 重写:原先断言「浅色/深色两套 --nav-bg 都定义」与
+ * 「壁纸模式下 .nav-rail 里有 backdrop-filter」,两条编码的都是**已被有意撤掉的
+ * 设计**,于是 `npm test` 在 HEAD 上恒红。判据红成常态就不再是判据 —— 该改的是
+ * 判据本身,而**不是**把缺陷写回代码。
+ *
+ * ① 深色导航撤掉了(用户「导航栏为什么还是黑色」)。根因不是令牌抄错,而是这个
+ * 应用**还没有深色主题**:`darkMode:'class'` 配着,却没有任何组件写 `dark:`
+ * 变体 ⇒ 只把导航压深就得到「导航黑、正文白」,比全浅更割裂。现在的契约是
+ * 导航跟随内容的实际形态(浅色玻璃),所以这里断言的是**「没有深色主题之前,
+ * .dark 不许单独给导航换色」**。真做深色主题时,这条要连同 dark: 变体一起改。
+ *
+ * ② 导航不再自己模糊:模糊只由壁纸层负责(用户「你又犯了模糊叠模糊的毛病……
+ * 整体的模糊是由壁纸那一层模糊确定的」)。导航浮在壁纸上,背后是**已经模糊过**
+ * 的壁纸,再 backdrop-filter 一次只会更脏更掉帧。所以断言是反向的:壁纸层有模糊,
+ * 而壁纸模式下的 .nav-rail 没有。
+ */
+ const darkBodies = [...css.matchAll(/\.dark\s*\{([^}]*)\}/g)].map(m => m[1]);
+ check(
+ '没有深色主题之前,.dark 不单独给导航换色',
+ darkBodies.length >= 1 && darkBodies.every(b => !/--nav-/.test(b)),
+ '.dark 里出现了 --nav-* 令牌 —— 要让导航变暗,必须同时给组件补 dark: 变体'
+ );
+ check(
+ '模糊只由壁纸层负责(壁纸模式下的导航不再自叠一层)',
+ /\.app-backdrop \{[\s\S]*?filter: blur\(/.test(css) &&
+ !/html\[data-bg='on'\] \.nav-rail[^{]*\{[^}]*backdrop-filter/.test(css),
+ '导航自己 backdrop-filter 会把已经模糊过的壁纸再糊一次'
+ );
check('内层面板自带圆角(不靠裁剪,否则与滚动冲突)',
/\.comm-pane > \*:not\(\[data-testid='comm-tabs'\]\) \{[\s\S]{0,80}border-radius: var\(--radius-card\)/.test(css));
/*