diff --git a/apps/ai-game-creator-shell/src/styles.css b/apps/ai-game-creator-shell/src/styles.css
index 86fb836d4..74d8e5e41 100644
--- a/apps/ai-game-creator-shell/src/styles.css
+++ b/apps/ai-game-creator-shell/src/styles.css
@@ -1332,6 +1332,19 @@ textarea {
overflow: hidden;
}
+/*
+ * 页面高度契约:`.launcher-main` 是纵向 flex 列,页面根节点自己 `flex: 1 1 auto` 吃掉
+ * 舞台剩余高度(有横幅时自动少掉横幅那一份),页面内部的 `height: 100%`、内层滚动容器
+ * 才有确定高度可用。
+ *
+ * 页面根节点上不要再写 `100vh` / `100dvh` / `calc(100vh - 32px)`:整窗高度含自绘标题栏,
+ * 比真实舞台高一个标题栏(窗口 100vh=800、舞台 750),底部会被裁掉,横幅也会被重复扣一次。
+ * 窗口外壳用 `height: 100%` 覆盖这里的 `100dvh`,让页面贴合 `.window-chrome__content`
+ * 的真实舞台;没有窗口外壳时 `100dvh` 才是正确值。
+ */
+.launcher-main:has(.launcher-home-page),
+.launcher-main:has(.launcher-help-page),
+.launcher-main:has(.launcher-template-library),
.launcher-main:has(.launcher-projects-page) {
display: flex;
flex-direction: column;
@@ -1340,6 +1353,20 @@ textarea {
padding-bottom: 0;
}
+.launcher-main > .launcher-home-page,
+.launcher-main > .launcher-help-page,
+.launcher-main > .launcher-template-library {
+ flex: 1 1 auto;
+ height: auto;
+ min-height: 0;
+}
+
+/* 帮助页没有内层滚动容器:占满剩余高度后自己滚动,底部留出与外壳一致的呼吸位。 */
+.launcher-main > .launcher-help-page {
+ overflow-y: auto;
+ padding-bottom: 42px;
+}
+
.launcher-page header {
display: flex;
align-items: flex-end;
diff --git a/apps/ai-game-creator-shell/src/view/home/index.tsx b/apps/ai-game-creator-shell/src/view/home/index.tsx
index 3d605d066..ff0e1810a 100644
--- a/apps/ai-game-creator-shell/src/view/home/index.tsx
+++ b/apps/ai-game-creator-shell/src/view/home/index.tsx
@@ -208,11 +208,7 @@ export default function HomeView({
}
return (
-
+
(null);
const viewportRef = useRef(null);
- const sectionRef = useRef(null);
- const [sectionHeight, setSectionHeight] = useState(null);
const [viewportSize, setViewportSize] = useState({ width: 0, height: 0 });
const showEmptyLibrary = status === 'ready' && templates.length === 0;
@@ -162,35 +160,10 @@ export default function TemplateLibraryView({
const showGrid = status === 'ready' && visibleTemplates.length > 0;
/**
- * 页面高度按**父级实测高度**定,不用百分比也不用 100vh。
- *
- * 外壳样式 `.launcher-main > .platform-theme { height: 100% }` 特异性高于 Tailwind
- * 工具类,而它的百分比在 `.launcher-shell { min-height: 100vh }` 这条链上是不定高,
- * 页面会退化成内容高度(虚拟网格视口高度 0、卡片区空白);`100vh` 又比真实舞台高
- * 一个标题栏(窗口 100vh=800、舞台 750),底部会被裁掉。这里直接量父级。
+ * 页面高度不再自己量:`.launcher-main:has(.launcher-template-library)` 把外壳改成纵向
+ * flex 列,`flex: 1 1 auto` 让这一页拿到确定高度(有横幅时自动少掉横幅那一份),
+ * 下面的网格视口才能用 `flex-1` 量出真实尺寸。
*/
- useEffect(() => {
- const section = sectionRef.current;
- const parent = section?.parentElement;
- if (!section || !parent) {
- return;
- }
- const apply = () => {
- const height = Math.round(parent.getBoundingClientRect().height);
- if (height > 0) {
- setSectionHeight((current) => (current === height ? current : height));
- }
- };
- apply();
- if (typeof ResizeObserver === 'undefined') {
- window.addEventListener('resize', apply);
- return () => window.removeEventListener('resize', apply);
- }
- const observer = new ResizeObserver(apply);
- observer.observe(parent);
- return () => observer.disconnect();
- }, []);
-
useEffect(() => {
const element = viewportRef.current;
if (!element) {
@@ -279,9 +252,7 @@ export default function TemplateLibraryView({
return (
diff --git a/docs/project-memory/shared-memory/decision-log.md b/docs/project-memory/shared-memory/decision-log.md
index e1c5c9503..8b51f9a89 100644
--- a/docs/project-memory/shared-memory/decision-log.md
+++ b/docs/project-memory/shared-memory/decision-log.md
@@ -1,5 +1,13 @@
# 决策记录
+## 2026-10-02 launcher 页面高度契约:外壳分高度,页面不再自己算窗口高度
+
+- 背景:PR #228(`6d2c275d3`)只给项目页补了「外壳纵向 flex + 页面 `flex: 1 1 auto`」的高度修复;其余页面仍各自算高度——帮助页没写高度也没有内层滚动容器,内容一长就被外壳 `overflow: hidden` 裁掉且无法滚动;首页用 `h-screen` / `h-[calc(100vh-32px)]`,模板库用 JS 量父级高度写内联 `height`。
+- 决策:把 #228 的修复提升为 `apps/ai-game-creator-shell/src/styles.css` 里的通用契约——`.launcher-main:has(<页面钩子>)` 改纵向 flex 列(`height: 100dvh`,窗口外壳 `height: 100%` 命中时贴合真实舞台;`min-height: 0`、`padding-bottom: 0`),`.launcher-main > <页面根节点>` 统一 `flex: 1 1 auto; height: auto; min-height: 0`;帮助页额外 `overflow-y: auto; padding-bottom: 42px`(它没有内层滚动容器)。契约钩子:`launcher-home-page`、`launcher-help-page`、`launcher-template-library`、`launcher-projects-page`。
+- 边界:页面根节点不再写 `100vh` / `100dvh` / `calc(100vh - Npx)`;横幅(`.launcher-promo`)是外壳里的真实行,有横幅就靠 flex 自动少一份,不手算偏移。工作台页(`.game-project-workbench`)在窗口外壳里已有等价契约(`.window-chrome__content ...` 那几条),移动端媒体查询里遗留的 `100dvh` / `calc(100dvh - 32px)` 由外层 `flex: 1 1 auto` 收缩兜住,本次不动。
+- 影响范围:`apps/ai-game-creator-shell/src/styles.css`、`src/view/home/index.tsx`、`src/view/template-library/index.tsx`。
+- 验证:Chromium 真机量测(1440x800 / 1440x560 / 390x844 / 390x560,带与不带横幅)——首页 / 模板库 / 项目页改动前后高度一致,帮助页从「被裁 112~150px 且无可滚动祖先」变为正好等于 `.window-chrome__content` 高度并可滚动到底;`npm run typecheck`、`npm run check:encoding`、`git diff --check`、`eslint` 通过。
+
## 2026-10-01 游戏广场评分展示边界
- 用户确认广场卡片增加一位小数的 10 分制平均分与评分人数,无有效评价显示“暂无评分”;保留现有排序、筛选、卡片打开详情及返回上下文。
diff --git a/docs/project-memory/shared-memory/pitfalls.md b/docs/project-memory/shared-memory/pitfalls.md
index ef8834e21..072d05269 100644
--- a/docs/project-memory/shared-memory/pitfalls.md
+++ b/docs/project-memory/shared-memory/pitfalls.md
@@ -2,6 +2,13 @@
这里只记录对当前开发仍有用的症状、根因、排查方法和风险边界。同一事实保留一个当前口径;退役对象的专属过程与单轮测试结果由 Git 历史追溯。遇到旧路径或版本时,以现行代码和专题文档为准。
+## 2026-10-02 AGC 页面在自绘标题栏外壳里自己算 `100vh`:底部被裁而且没得滚
+
+- **现象**:帮助页(使用指南 / 联系客服 / 更新日志)在矮窗口里底部卡片看不到,把窗口拉高才出现;外壳 `.launcher-main { overflow: hidden }` 之下没有任何可滚动祖先,页面既滚不动也裁得干净。首页在通知横幅出现时用 `h-[calc(100vh-32px)]`,同样把窗口高度当成了舞台高度。
+- **原因**:AGC 桌面外壳是自绘标题栏(`--window-chrome-height`;窗口 100vh=800 时舞台只有 750),页面根节点写 `100vh` / `100dvh` / `calc(100vh - Npx)` 就比真实舞台高一整个标题栏,差额被外壳裁掉;横幅是 `.launcher-main` 里的真实行,再写 `-32px` 等于重复扣一次。帮助页还没有内层滚动容器,连「内容超高就在内部滚动」这条兜底也不存在。
+- **处理(现行口径)**:页面高度只由外壳分配——`apps/ai-game-creator-shell/src/styles.css` 里 `.launcher-main:has(<页面钩子>)` 是纵向 flex 列(`height: 100dvh`,窗口外壳命中 `height: 100%` 时贴合真实舞台),`.launcher-main > <页面根节点>` 统一 `flex: 1 1 auto; height: auto; min-height: 0`,帮助页这类没有内层滚动容器的再加 `overflow-y: auto`。页面根节点一律不再写 `100vh` / `100dvh` / `calc(100vh - Npx)`;有横幅就靠 flex 自动少一份,不要手算偏移。
+- **验证**:真机判据是 Vite + Chromium 量页面根节点是否正好等于 `.window-chrome__content` 的高度(1440x800 / 1440x560 / 390x844 / 390x560,带与不带横幅),帮助页应可滚动到底。
+
## 2026-10-01 Rust 分片编译失败只剩汇总错误
- **原因**:`--message-format=json` 把编译诊断写到 stdout;只读取 `compiler-artifact` 的运行器会丢弃 `compiler-message`,CI 只能看到「due to 1 previous error」。