+ /* `cal-panes`:这一层是"拼成一张卡的两块面板",圆角规则在 index.css 里
+ (根节点自己没有底色,见那里的说明)*/
+
{gridPane}
{sidePane}
diff --git a/client/electron/src/index.css b/client/electron/src/index.css
index 57c9845..53f7505 100644
--- a/client/electron/src/index.css
+++ b/client/electron/src/index.css
@@ -1127,6 +1127,38 @@ html.view-switch .glass-control,
min-height: 0;
}
+/*
+ * ★ 日历:网格栏与右栏拼成**一张卡**(中间是分隔线,不是外壳间距)
+ * 用户(2026-09-14):「日历页面还没有添加圆角」。
+ *
+ * 为什么它一直没圆角:外壳那条 `.app-shell > *` 的圆角加在日历**根节点**上,
+ * 而根节点是 `flex-1 min-w-0 flex min-h-0` —— **它自己没有底色**。
+ * 真正白底的是里面那两块面板,直角从透明外壳里戳出来,看上去就是没圆角。
+ *
+ * 两个不能照搬收件箱做法的地方:
+ *
+ * ① 这两块面板**不是两张卡**(中间只有 1px 分隔线、没有外壳间距),
+ * 所以只能给**外沿**:左边那块给左两角、右边那块给右两角。
+ * 内侧要是也圆了,分隔线两边会露出底色缺口。
+ * ② 右栏外面还套了一层容器(管宽度 `lg:w-[400px]` 与 `border-l`,自己不上色),
+ * 真正上色的是它里面的 DayAgendaPane / CalendarEventEditor。
+ * 圆角只加在容器上会被内层的直角**原地盖掉** —— 所以要再往下给一层。
+ * (左栏本身就是白底面板,不能再往下给:它的第一个子元素是工具条,
+ * 给下去变成"工具条圆角",那不是我们要的东西。)
+ */
+.cal-panes > :first-child {
+ border-start-start-radius: var(--radius-card);
+ border-end-start-radius: var(--radius-card);
+ background-clip: padding-box;
+}
+
+.cal-panes > :last-child,
+.cal-panes > :last-child > * {
+ border-start-end-radius: var(--radius-card);
+ border-end-end-radius: var(--radius-card);
+ background-clip: padding-box;
+}
+
/*
* 桌面侧栏:无论有没有壁纸都做深色玻璃 —— 否则它是全页唯一一块实心黑。
diff --git a/client/electron/test/manual/wide-regression.mjs b/client/electron/test/manual/wide-regression.mjs
index f882bb0..1443b26 100644
--- a/client/electron/test/manual/wide-regression.mjs
+++ b/client/electron/test/manual/wide-regression.mjs
@@ -19,7 +19,14 @@ const chk = (n, ok, note = '') => {
console.log('宽屏回归(1280px):');
const cols = await page.evaluate(() => {
- const root = document.querySelector('#root > div');
+ /*
+ * 选 `.app-shell`,不是 `#root > div`。
+ *
+ * 壁纸层(`app-backdrop`)后来挂成了 `#root` 的第一个子节点,`#root > div`
+ * 从此指向**幕布本身**(它没有子节点)—— 这条判据于是一直报「栏数=0」的假失败。
+ * 假失败比没有判据更贵:看的人会去查一个本来好好的三栏布局。
+ */
+ const root = document.querySelector('.app-shell');
return { n: root?.children.length, first: root?.children[0]?.className?.toString().slice(0, 30) };
});
chk('仍是三栏并排', cols.n === 3, `栏数=${cols.n} 首栏=${cols.first}`);
@@ -124,6 +131,53 @@ await page.locator('[data-testid="reply-fab"]').click();
await page.waitForTimeout(500);
chk('宽屏点悬浮球能展开输入框(带焦点)', await page.evaluate(() => document.activeElement?.tagName === 'TEXTAREA'));
+/*
+ * 日历的圆角(用户 2026-09-14:「日历页面还没有添加圆角」)。
+ *
+ * 为什么在这里验、而不是只验 CSS:那一族的形态是**「computed style 里
+ * border-radius 就是 14px,看上去却是方角」** —— 圆角加在一个**没有底色**的
+ * 外层上,白底的内层面板用直角原地把它盖掉了。
+ * 光看 `getComputedStyle(panel).borderRadius` 也区分不了:真正决定观感的是
+ * **角上那 2px 到底是谁在画**。所以从 elementFromPoint 起往上走,找第一个
+ * 自带底色的元素 —— 它必须就是那个圆角面板本身。
+ */
+await page.locator('button:has-text("日历")').first().click();
+await page.waitForSelector('.cal-panes', { timeout: 20000 });
+await page.waitForTimeout(600);
+const cal = await page.evaluate(() => {
+ const w = document.querySelector('.cal-panes');
+ if (!w) return { found: false };
+ const first = w.firstElementChild;
+ const last = w.lastElementChild;
+ const lastInner = last.firstElementChild;
+ const painterAt = (x, y) => {
+ let e = document.elementFromPoint(x, y);
+ while (e) {
+ const bg = getComputedStyle(e).backgroundColor;
+ if (bg && bg !== 'rgba(0, 0, 0, 0)' && bg !== 'transparent') return e;
+ e = e.parentElement;
+ }
+ return null;
+ };
+ const rf = first.getBoundingClientRect();
+ const rl = last.getBoundingClientRect();
+ return {
+ found: true,
+ firstRadius: getComputedStyle(first).borderRadius,
+ lastRadius: lastInner ? getComputedStyle(lastInner).borderRadius : null,
+ tl: painterAt(rf.x + 2, rf.y + 2) === first,
+ bl: painterAt(rf.x + 2, rf.bottom - 3) === first,
+ tr: painterAt(rl.right - 3, rl.y + 2) === lastInner,
+ br: painterAt(rl.right - 3, rl.bottom - 3) === lastInner
+ };
+});
+chk('日历布局挂上了面板栈标记', cal.found);
+chk(
+ '日历四个外角由**自带底色的那层面板**画(不是透明外壳)',
+ cal.found && cal.tl && cal.bl && cal.tr && cal.br,
+ `左栏 radius=${cal.firstRadius} 右栏 radius=${cal.lastRadius} 角=${JSON.stringify({ tl: cal.tl, bl: cal.bl, tr: cal.tr, br: cal.br })}`
+);
+
console.log('\nissues:', issues.length ? issues : '无');
console.log(failed.length === 0 ? '\n宽屏回归:全部通过' : `\n宽屏回归:${failed.length} 项失败`);
diff --git a/client/electron/test/narrow-layout.test.mjs b/client/electron/test/narrow-layout.test.mjs
index 83550eb..fc05bbe 100644
--- a/client/electron/test/narrow-layout.test.mjs
+++ b/client/electron/test/narrow-layout.test.mjs
@@ -213,6 +213,43 @@ check(
(maskRule.match(/-webkit-mask-image:/g) || []).length === 1 &&
(maskRule.match(/[^-]mask-image:/g) || []).length === 1
);
+
+// 5.10) 面板圆角必须给到**有底色的那一层**。
+//
+// 用户(2026-09-14):「日历页面还没有添加圆角」。
+//
+// 这一族的形态最骗人:`getComputedStyle` 里 `border-radius` **就是 14px**
+// (外壳那条 `.app-shell > *` 确实加上了),可**看上去是方角** ——
+// 因为外壳节点是 `flex-1 min-w-0 flex min-h-0`、**自己没有底色**,
+// 真正白底的是里面那两块面板,它们的直角从透明外壳里戳出来。
+// 所以判据不能只查「有没有 border-radius」,要钉住:
+// ① 规则与 JSX 是否真接上了(两张表按**类名**联接,不按相邻关系);
+// ② 给的是**哪几条边**;
+// ③ 中间那层容器自己不上色时,圆角得**再往下给一层**。
+const cal = read('../src/components/CalendarView.tsx');
+const calFirst = (css.match(/\.cal-panes\s*>\s*:first-child\s*\{[\s\S]*?\n\}/) || [''])[0];
+const calLast = (css.match(/\.cal-panes\s*>\s*:last-child[\s\S]*?\n\}/) || [''])[0];
+check(
+ '日历根节点挂上了面板栈标记(CSS 选择器与 JSX 类名接得上)',
+ calFirst !== '' && /className="cal-panes[ "]/.test(cal),
+ 'CSS 里有规则但 JSX 没挂类名(或反之)—— 两边各自看都“有”,圆角却不生效'
+);
+check(
+ '日历外沿两角分两侧给:首元素给起始两角、末元素给结束两角',
+ /border-start-start-radius/.test(calFirst) && /border-end-start-radius/.test(calFirst) &&
+ /border-start-end-radius/.test(calLast) && /border-end-end-radius/.test(calLast),
+ '这两块面板中间是**分隔线**不是外壳间距:四角全给会在分隔线两边露出底色缺口'
+);
+check(
+ '右栏容器里面的面板也拿到圆角(否则内层直角把圆角原地盖掉)',
+ /\.cal-panes\s*>\s*:last-child\s*>\s*\*/.test(css),
+ '右栏外面那层只管宽度与分隔线、自己不上色 —— 只给这层加圆角=无效果'
+);
+check(
+ '左栏规则直接落在面板本身(不再往下给)',
+ calFirst !== '' && !/\.cal-panes\s*>\s*:first-child\s*>\s*\*/.test(css),
+ '左栏本身就是白底面板;再往下就给到了第一个子元素(工具条),圆到不该圆的东西上'
+);
check('根视口使用 100dvh 跟随软键盘', css.includes('@supports (height: 100dvh)'));
// 6) 横向内边距在窄屏收窄(px-6 在 375px 屏上白吃 48px)
diff --git a/client/electron/test/run-all.mjs b/client/electron/test/run-all.mjs
index 58a34f5..0118c64 100644
--- a/client/electron/test/run-all.mjs
+++ b/client/electron/test/run-all.mjs
@@ -58,7 +58,7 @@ for (const must of ['配对/解析', 'allow-list', '变异验证', '剥掉注释
const SUITE = [
['test/markdown-xss.test.mjs', [], 9],
- ['test/narrow-layout.test.mjs', [], 58],
+ ['test/narrow-layout.test.mjs', [], 62],
['test/nav-merge.test.mjs', [], 8],
['test/theme.test.mjs', [], 30],
['test/background.test.mjs', [], 42],