Skip to content

Commit 02032cf

Browse files
committed
refactor(shell): consume nav height token
1 parent 71ca860 commit 02032cf

11 files changed

Lines changed: 75 additions & 11 deletions

‎app/src/App.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ function MainShell() {
285285
{/* 顶部导航 */}
286286
<nav
287287
style={{
288-
height: 56,
288+
height: "var(--ed-nav-h)",
289289
flexShrink: 0,
290290
display: "flex",
291291
alignItems: "center",

‎app/src/components/AiConversationDock.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ export default function AiConversationDock({
239239
<div
240240
data-testid="ai-dock"
241241
style={{
242-
position: "fixed", top: 56, right: 0, bottom: 0, width: PANEL_W, zIndex: 900,
242+
position: "fixed", top: "var(--ed-nav-h)", right: 0, bottom: 0, width: PANEL_W, zIndex: 900,
243243
background: "#fff", borderLeft: "1px solid #e5e7eb", boxShadow: "-8px 0 24px rgba(0,0,0,0.08)",
244244
display: open ? "flex" : "none", flexDirection: "column", overflow: "hidden",
245245
fontSize: 12.5, color: "#1f2937",

‎app/src/pages/ActionPage.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export default function ActionPage({ active }: Props) {
3434
}, [active]);
3535

3636
return (
37-
<div style={{ height: "calc(100vh - 56px)", display: "flex", flexDirection: "column", minHeight: 0 }}>
37+
<div style={{ height: "calc(100vh - var(--ed-nav-h))", display: "flex", flexDirection: "column", minHeight: 0 }}>
3838
<ActionCenterPanel refreshToken={token} />
3939
</div>
4040
);

‎app/src/pages/ClassroomPage.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ export default function ClassroomPage({ onOpenSessions }: { onOpenSessions?: (se
203203
/** 素材流水线(v0.1.0):选素材/提取逻辑已下沉 MaterialInputPanel(审查硬拆) */
204204

205205
return (
206-
<div style={{ display: "flex", height: "calc(100vh - 56px)", minHeight: 0 }}>
206+
<div style={{ display: "flex", height: "calc(100vh - var(--ed-nav-h))", minHeight: 0 }}>
207207
{/* ── 左栏:配置面板(窗口选择 → 素材 → 启动按钮;v0.15 可拖拽/折叠) ── */}
208208
{/* 批 0-C2 Task 4 步 4:左栏整体抽出至 ClassroomSourceColumn(顶层恰好一个元素);
209209
R2:ColumnResizer 仍是本列的**兄弟**——留在本页、无条件渲染,不随之搬走 */}

‎app/src/pages/KnowledgePage.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ export default function KnowledgePage({ focusSystemId, onOpenNote, onOpenGroup,
236236
}
237237

238238
return (
239-
<div style={{ display: "flex", height: "calc(100vh - 56px)", minHeight: 0 }}>
239+
<div style={{ display: "flex", height: "calc(100vh - var(--ed-nav-h))", minHeight: 0 }}>
240240
{/* ── 左:体系列表(全局置顶固定 + 领域列表;v0.15 可拖拽/折叠为窄条) ── */}
241241
{leftCol.folded ? (
242242
<ColumnBar icon="🧭" title="知识体系" onClick={leftCol.expand} />

‎app/src/pages/NotesPage.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ export default function NotesPage({ focusNoteId, focusNoteSearch, focusGroupId,
188188
}, [visibleNotes, groupMap, list.tagColors]);
189189

190190
return (
191-
<div style={{ display: "flex", height: "calc(100vh - 56px)", minHeight: 0 }}>
191+
<div style={{ display: "flex", height: "calc(100vh - var(--ed-nav-h))", minHeight: 0 }}>
192192
{/* ── 左侧:组筛选侧栏(折叠窄条/列表切换 + 列拖拽手柄见
193193
components/notes/NotesGroupsColumn)── */}
194194
<NotesGroupsColumn

‎app/src/pages/ReviewPage.tsx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ export default function ReviewPage({ active, focusGroupId, onFocusGroupConsumed
114114

115115
if (session) {
116116
return (
117-
<div style={{ height: "calc(100vh - 56px)", display: "flex", flexDirection: "column", minHeight: 0, background: "#fff" }}>
117+
<div style={{ height: "calc(100vh - var(--ed-nav-h))", display: "flex", flexDirection: "column", minHeight: 0, background: "#fff" }}>
118118
<ReviewSessionPanel
119119
groupId={session.groupId}
120120
groupName={session.groupName}
@@ -137,7 +137,7 @@ export default function ReviewPage({ active, focusGroupId, onFocusGroupConsumed
137137
: `其余组共 ${totalDue} 张到期——切换上方范围即可复习。`;
138138

139139
return (
140-
<div style={{ height: "calc(100vh - 56px)", display: "flex", flexDirection: "column", minHeight: 0, background: "#fff" }}>
140+
<div style={{ height: "calc(100vh - var(--ed-nav-h))", display: "flex", flexDirection: "column", minHeight: 0, background: "#fff" }}>
141141
{/* 头部:标题 + 到期总数 + 开始复习 */}
142142
<div style={{ padding: "10px 16px", borderBottom: "1px solid #e5e7eb", flexShrink: 0 }}>
143143
<div style={{ display: "flex", alignItems: "center", gap: 8 }}>

‎app/src/pages/SessionsPage.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ export default function SessionsPage({ focusSessionId, focusRefineTaskId, onFocu
275275
// (映射实际位于 SessionListPanel.runBatchConvert——本层只收可转化 id 集合)
276276

277277
return (
278-
<div style={{ display: "flex", height: "calc(100vh - 56px)", minHeight: 0 }}>
278+
<div style={{ display: "flex", height: "calc(100vh - var(--ed-nav-h))", minHeight: 0 }}>
279279
{/* ── 左栏:会话管理台(v0.7.1 拆出 SessionListPanel;v0.15 可拖拽/折叠) ── */}
280280
{listCol.folded ? (
281281
<ColumnBar icon="🗂" title="会话列表" onClick={listCol.expand} />

‎app/src/pages/SettingsPage.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ interface Props {
5555

5656
export default function SettingsPage({ active = true }: Props) {
5757
return (
58-
<div style={{ height: "calc(100vh - 56px)", overflowY: "auto" }}>
58+
<div style={{ height: "calc(100vh - var(--ed-nav-h))", overflowY: "auto" }}>
5959
<div style={{ maxWidth: 720, padding: "12px 16px 24px" }}>
6060
<div style={{ fontSize: 13, color: "#6b7280", marginBottom: 4 }}>
6161
应用配置与管理。改动即时生效,课堂助手下次使用时读取最新值。
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/**
2+
* @ai-context 壳层纵向基准的**棘轮守卫**(规格 §1 决策 16 / §10 批 3 行「7 处魔数归零」第 1 处)。
3+
*
4+
* Why 用测试而不是脚本:本仓 `scripts/*.mjs` 不在 `line-limits` 扫描域(批 2 follow-up #16),
5+
* 而 `app/src/**` 在 ⇒ 把守卫放进扫描域内,它自己也被 300 行红线看着。
6+
*
7+
* Why 只扫壳层相关文件而不是全树:全树 `height: 56` 还有无关命中(如 `ReviewPage.tsx:204` 的
8+
* `padding: "56px 0"`)。本守卫的**扫描域是一份显式清单**(下方 SHELL_FILES),
9+
* 这样「漏了一个文件」是**清单的错**、不是正则的错 —— 口径可读、可评审。
10+
*
11+
* 口径:命中 = `height: 56` / `top: 56` / `100vh - 56px`(数字两边允许空白)。
12+
* `var(--ed-nav-h)` 与 `calc(100vh - var(--ed-nav-h))` **不匹配**、不被误伤。
13+
*/
14+
import { readFileSync } from "node:fs";
15+
import { dirname, join } from "node:path";
16+
import { fileURLToPath } from "node:url";
17+
import { describe, expect, it } from "vitest";
18+
19+
const HERE = dirname(fileURLToPath(import.meta.url));
20+
const SRC = join(HERE, "..");
21+
22+
/** 壳层纵向基准**必须**经由变量的文件(相对 `app/src`,正斜杠) */
23+
const SHELL_FILES = [
24+
"App.tsx",
25+
"components/AiConversationDock.tsx",
26+
"pages/ActionPage.tsx",
27+
"pages/ClassroomPage.tsx",
28+
"pages/KnowledgePage.tsx",
29+
"pages/NotesPage.tsx",
30+
"pages/ReviewPage.tsx",
31+
"pages/SessionsPage.tsx",
32+
"pages/SettingsPage.tsx",
33+
] as const;
34+
35+
/** 三种消费形态的**旧写法**(命中即违规) */
36+
const FORBIDDEN = [/height:\s*56\b/, /top:\s*56\b/, /100vh\s*-\s*56px/];
37+
/** 新写法的正样本(守卫必须能认出来) */
38+
const REQUIRED = /--ed-nav-h/;
39+
40+
function lines(rel: string): [number, string][] {
41+
return readFileSync(join(SRC, rel), "utf8")
42+
.split(/\r?\n/)
43+
.map((l, i) => [i + 1, l] as [number, string]);
44+
}
45+
46+
describe("壳层纵向基准 --ed-nav-h", () => {
47+
it("壳层文件里不得再出现裸 56(height / top / calc)", () => {
48+
const hits: string[] = [];
49+
for (const rel of SHELL_FILES) {
50+
for (const [n, l] of lines(rel)) if (FORBIDDEN.some((re) => re.test(l))) hits.push(`${rel}:${n} ${l.trim()}`);
51+
}
52+
expect(hits, `这些行仍硬编码 56(请改用 var(--ed-nav-h)):\n${hits.join("\n")}`).toEqual([]);
53+
});
54+
55+
it("每个壳层文件都**确实**消费了变量(防止用删代码的方式让上一条变绿)", () => {
56+
const missing = SHELL_FILES.filter((rel) => !lines(rel).some(([, l]) => REQUIRED.test(l)));
57+
expect(missing, `这些文件没有消费 --ed-nav-h(删掉旧写法不等于接上了变量):\n${missing.join("\n")}`).toEqual([]);
58+
});
59+
60+
it("扫描域自检:清单里的文件都真的存在且非空", () => {
61+
const empty = SHELL_FILES.filter((rel) => lines(rel).length < 10);
62+
expect(empty, `清单里的文件读不到或过短(路径写错了?):\n${empty.join("\n")}`).toEqual([]);
63+
});
64+
});

0 commit comments

Comments
 (0)