Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions desktop/ui/src/navigation.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@ afterEach(() => {
vi.unstubAllGlobals();
});

describe("一级导航栏的栏宽归属", () => {
// 栏宽随窗宽收窄、底色与右分隔线都在 CSS(styles.css .mc-nav-rail 与
// ::after),写进内联样式会静默盖掉这些规则。mac 自绘红绿灯也依赖
// 62px 栏宽收纳整组圆点,宽度被内联锁死的话窄窗下会挤压变形。
describe("双层侧栏的栏宽归属", () => {
// 栏宽随窗宽收窄(--railW/--sideW)、底色与右分隔线都在 CSS(styles.css
// .mc-nav-rail 与 ::after、.mc-sidebar-panel),写进内联样式会静默盖掉这些
// 规则:mac 自绘红绿灯依赖 62px 栏宽收纳整组圆点,窄窗下会挤压变形;
// Windows 自绘标题栏同列也读同一组令牌,内联写死则两层竖线会错开。
it("栏宽、右边线与底色不写在内联样式里,留给 CSS 按平台与窗宽决定", () => {
const html = renderToStaticMarkup(
<Sidebar
Expand Down Expand Up @@ -55,6 +56,10 @@ describe("一级导航栏的栏宽归属", () => {
expect(rail![1]).not.toMatch(/(^|;)\s*width:/);
expect(rail![1]).not.toMatch(/border-right/);
expect(rail![1]).not.toMatch(/background/);

const panel = /<aside class="mc-sidebar-panel" style="([^"]*)"/.exec(html);
expect(panel, "找不到 mc-sidebar-panel").not.toBeNull();
expect(panel![1]).not.toMatch(/(^|;)\s*width:/);
});
});

Expand Down
5 changes: 3 additions & 2 deletions desktop/ui/src/settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1894,8 +1894,9 @@ export function SettingsView({

return (
<div style={{ flex: 1, display: "flex", minHeight: 0, animation: "mcin .25s ease" }}>
{/* 左侧分类导航(设置态占满主窗口,此为最左栏) */}
<div style={{ width: 168, flex: "none", background: "var(--side)", borderRight: "1px solid var(--line)", display: "flex", flexDirection: "column", gap: 2, padding: "0 10px 12px" }}>
{/* 左侧分类导航(设置态占满主窗口,此为最左栏)。栏宽走 --setNavW 令牌:
Windows 自绘标题栏的设置态同列共用它,两层的竖分隔线才不会错开 */}
<div style={{ width: "var(--setNavW)", flex: "none", background: "var(--side)", borderRight: "1px solid var(--line)", display: "flex", flexDirection: "column", gap: 2, padding: "0 10px 12px" }}>
{/* macOS 壳:自绘红绿灯落在最左栏顶部(与主侧栏同一组件,切换不跳动) */}
<MacWindowControls />
<div
Expand Down
4 changes: 3 additions & 1 deletion desktop/ui/src/sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1234,7 +1234,9 @@ export function Sidebar({
</button>
</div>

<aside className="mc-sidebar-panel" style={{ width: 232, flex: "none", display: "flex", flexDirection: "column", minHeight: 0, background: "var(--side)", borderRight: "1px solid var(--line)" }}>
{/* 栏宽在 styles.css(.mc-sidebar-panel + --sideW):窄窗要收窄,且要与
Windows 自绘标题栏同列共用同一个令牌,写死在这里两层就会错开 */}
<aside className="mc-sidebar-panel" style={{ flex: "none", display: "flex", flexDirection: "column", minHeight: 0, background: "var(--side)", borderRight: "1px solid var(--line)" }}>
<MacBrandBand />
<PanelHeader title={panel.title} detail={panel.detail}>{panel.actions}</PanelHeader>
<SearchBox value={query} placeholder={panel.placeholder} onChange={setQuery} />
Expand Down
27 changes: 22 additions & 5 deletions desktop/ui/src/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -595,20 +595,34 @@ textarea::placeholder, input::placeholder { color: var(--t6); }
background: var(--card) !important;
}

/* 左侧各栏的栏宽令牌:一处定义,多处消费。
* 消费方分两层且必须逐列对齐——下层是内容区的栏(.mc-nav-rail、
* .mc-sidebar-panel、设置页左导航),上层是 Windows 自绘标题栏里与它们
* 同列的几段(titlebar.tsx)。栏宽随窗宽收窄(下方媒体查询),数值一旦在
* 标题栏那侧另写一份,窗口缩到 1080px 以下时两层的竖分隔线就会错开
* ——所以两层只认这组令牌,不写字面值。 */
:root {
--railW: 62px; /* 一级空间导航 */
--sideW: 232px; /* 二级内容侧栏 */
--setNavW: 168px; /* 设置页左导航(设置态占满主窗口,此为最左栏) */
}

/* 一级空间导航的栏宽、底色与右分隔线。
*
* mac 的红绿灯已是 UI 自绘的 10px 替身(titlebar.tsx MacWindowControls,
* 原生按钮由壳隐藏),整组收在 62px 栏内——两档底色与右分隔线因此可以
* 贯通到顶,三端同构。曾为原生红绿灯越界做过"顶部 50px 染色"和"整面
* 同色去线"两轮让位,均随自绘方案退役;若将来恢复原生红绿灯,这段历史
* 见 git log(缩放键会压在栏边界上,须重新处理)。
* 栏宽随窗宽收窄(下方媒体查询),写进内联样式会盖掉规则,
* navigation.test 有守护断言。data-platform 在 ui/src/main.tsx 落到根节点。 */
* 栏宽走令牌,写死进内联样式会盖掉规则,navigation.test 有守护断言。
* data-platform 在 ui/src/main.tsx 落到根节点。 */
.mc-nav-rail {
width: 62px;
width: var(--railW);
position: relative;
background: var(--rail);
}
/* 二级内容侧栏的栏宽同样归 CSS(其余外观仍在 sidebar.tsx 内联) */
.mc-sidebar-panel { width: var(--sideW); }
.mc-nav-rail::after {
content: "";
position: absolute;
Expand Down Expand Up @@ -650,9 +664,12 @@ textarea::placeholder, input::placeholder { color: var(--t6); }
.mac-light-glyph { opacity: 0; display: block; }
.mac-lights:hover .mac-light-glyph { opacity: 1; }

/* 窄窗收窄:只改令牌,两层(内容区栏 + Windows 自绘标题栏同列)一起跟着走 */
@media (max-width: 1080px) {
.mc-nav-rail { width: 58px; }
.mc-sidebar-panel { width: 220px !important; }
:root {
--railW: 58px;
--sideW: 220px;
}
}

/* 代码预览高亮(highlight.js 输出;注释复用文字灰阶令牌) */
Expand Down
21 changes: 16 additions & 5 deletions desktop/ui/src/titlebar.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,30 @@ describe("Windows 标题栏", () => {
expect(html).toContain(">work<");
// 页面上下文不再入栏(唯一标题在 ViewHeader;窗口级上下文走原生标题)
expect(html).not.toContain("新建任务");
expect(html).toContain("width:62px");
expect(html).toContain("width:232px");
// 两段栏宽与下方的双层侧栏共用 styles.css 的令牌
expect(html).toContain("width:var(--railW)");
expect(html).toContain("width:var(--sideW)");
expect(html).toContain('title="最小化"');
});

it("设置页使用与设置导航一致的左侧宽度", () => {
const html = renderToStaticMarkup(<TitleBar layout="settings" />);

expect(html).toContain("width:168px");
expect(html).not.toContain("width:232px");
// 168px 是三处品牌位里最窄的一格,徽标同样要在
expect(html).toContain("width:var(--setNavW)");
expect(html).not.toContain("width:var(--sideW)");
// 设置导航是三处品牌位里最窄的一格,徽标同样要在
expect(html).toContain(">work<");
});

// 窄窗(≤1080px)时 styles.css 的媒体查询会收窄栏宽令牌。标题栏若把栏宽
// 写成字面像素,下方内容区收窄而标题栏不动,两层的竖分隔线就会错开——
// 用户看到的就是"窗口一缩小,header 的线错位了"。
it("左侧各段栏宽只认令牌,不写死像素", () => {
for (const layout of ["sidebar", "settings"] as const) {
const html = renderToStaticMarkup(<TitleBar layout={layout} />);
expect(html, `${layout} 布局把栏宽写死了`).not.toMatch(/width:(62|58|232|220|168)px/);
}
});
});

describe("mac 自绘小红绿灯", () => {
Expand Down
6 changes: 3 additions & 3 deletions desktop/ui/src/titlebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -182,21 +182,21 @@ export default function TitleBar({ layout = "sidebar" }: { layout?: "sidebar" |
<>
<span
data-tauri-drag-region=""
style={{ width: 62, height: "100%", flex: "none", display: "flex", alignItems: "center", justifyContent: "center", background: "var(--rail)", borderRight: "1px solid var(--line2)" }}
style={{ width: "var(--railW)", height: "100%", flex: "none", display: "flex", alignItems: "center", justifyContent: "center", background: "var(--rail)", borderRight: "1px solid var(--line2)" }}
>
<img src={logoUrl} alt="" draggable={false} style={{ width: 19, height: 19, borderRadius: 5, pointerEvents: "none" }} />
</span>
<span
data-tauri-drag-region=""
style={{ width: 232, height: "100%", flex: "none", display: "flex", alignItems: "center", gap: 6, padding: "0 14px", overflow: "hidden", background: "var(--side)", borderRight: "1px solid var(--line)" }}
style={{ width: "var(--sideW)", height: "100%", flex: "none", display: "flex", alignItems: "center", gap: 6, padding: "0 14px", overflow: "hidden", background: "var(--side)", borderRight: "1px solid var(--line)" }}
>
<Brand />
</span>
</>
) : (
<span
data-tauri-drag-region=""
style={{ width: 168, height: "100%", flex: "none", display: "flex", alignItems: "center", gap: 6, padding: "0 14px", overflow: "hidden", background: "var(--side)", borderRight: "1px solid var(--line)" }}
style={{ width: "var(--setNavW)", height: "100%", flex: "none", display: "flex", alignItems: "center", gap: 6, padding: "0 14px", overflow: "hidden", background: "var(--side)", borderRight: "1px solid var(--line)" }}
>
<Brand logo />
</span>
Expand Down
Loading