diff --git a/docs/【技术方案】游戏共创与作品Fork-2026-10-03.md b/docs/【技术方案】游戏共创与作品Fork-2026-10-03.md
index af8e6c4bf..796c3830a 100644
--- a/docs/【技术方案】游戏共创与作品Fork-2026-10-03.md
+++ b/docs/【技术方案】游戏共创与作品Fork-2026-10-03.md
@@ -467,6 +467,7 @@ A 路线里那个必须提前知道的互斥点已经按既有做法绕开:`cr
- **「只看父代与子代」开关**在筛选行,默认**关**(= 以根为作用域的整棵树)。打开后只留 **祖先链(父代)+ 自己 + 直接子代**(`collectLineageImmediateScope`,**不含孙代**),语义是「看这个作品的上一步与下一步」;**再点一次取消**回到整棵树。作用域里找不到锚点时不筛选(避免空图);切换后仍重新定位到当前作品。反馈有三重:`aria-pressed`、按钮文案变「(已开启)」、旁边一行说明。
- 侧栏共创卡文案按同一口径:`{N 代…} · 改编自父代《…》` + `父代作者 …`;子代侧沿用「累计衍生作品 N」(全部世代)与「累计创作世代数」。
- 面包屑横向可滚动且**当前项常驻可见**;尊重 `prefers-reduced-motion`;底部留 `env(safe-area-inset-bottom)`。
+ - **筛选行口径**(2026-10-06 追加):`高亮整棵子树`复选框**已删除**——它恒为真、没有用户可见意义,子树高亮改为**恒定开启**(`collectLineageHighlight` 默认即含子树;页面不再持有该开关状态,也不再有恒为 `true` 的布尔往下传)。`改动类型`下拉**保留但禁用**并在旁边写明原因(`changeSummary` 是自由文本、服务端没有可枚举分类),不编造值域。
- **移动端门限豁免(2026-10-06)**:平台原有的「移动端仅支持作品展示」欢迎弹窗只看 `!isDesktopLayout`、忽略路由,会把公开只读的族谱树 / 共创主题页首屏盖住。现在按**显式白名单**豁免这三个 stage:`game-lineage` / `game-themes` / `game-theme`(清单在 `PlatformActiveMobileWelcomeDialog.tsx` 的 `MOBILE_WELCOME_EXEMPT_STAGES`,键名即 stage 名,不做前缀/范围匹配)。**创作 / 项目 / 发布 / 我的 / 平台首页仍保持门限**——那些才是「移动端不支持体验创作工具」的部分;弹窗文案未改(与豁免后的事实不矛盾:族谱与主题页本身就是作品展示)。
- **截断 / 空树 / 不可用父节点**在两种形态下都有降级:截断提示、空态、以及「原作品已不可用」的占位节点(可聚焦、不可点进详情、不展示被删作品标题与作者名)。
diff --git a/src/components/game-distribution/GameDistributionPages.test.tsx b/src/components/game-distribution/GameDistributionPages.test.tsx
index d9ec46d4b..38880b860 100644
--- a/src/components/game-distribution/GameDistributionPages.test.tsx
+++ b/src/components/game-distribution/GameDistributionPages.test.tsx
@@ -2631,6 +2631,44 @@ describe('GameLineagePage 可视化与交互', () => {
).toBe('false');
});
+ it('子树高亮恒生效、开关已删除(不保留恒为 true 的控件)', async () => {
+ getGameLineageMock.mockResolvedValueOnce(relationTree);
+ render(
+ ,
+ );
+
+ await cardOf('当前作品');
+ // ⑥ 复选框不存在(不是禁用、不是隐藏文案)。
+ expect(screen.queryByText('高亮整棵子树')).toBeNull();
+ expect(screen.queryByLabelText('高亮整棵子树')).toBeNull();
+ // 其他筛选控件还在,说明删的是这一个而不是整行。
+ expect(screen.getByLabelText('按热度排序')).toBeTruthy();
+
+ // 行为恒生效:hover 一个中间节点 → 它的**后代**(孙代)不降透明,旁支才降透明。
+ // 没有开关可关掉它,因为这段行为不再依赖任何状态。
+ const midCard = await cardOf('中间代');
+ fireEvent.mouseEnter(midCard);
+ expect((await cardOf('孙代作品')).className).not.toContain(
+ 'game-lineage-node--dimmed',
+ );
+ expect((await cardOf('旁支叔伯')).className).toContain(
+ 'game-lineage-node--dimmed',
+ );
+ // 子树里的节点带上「子树」强化类,与祖先路径(path)区分开。
+ expect((await cardOf('孙代作品')).className).toContain(
+ 'game-lineage-node--subtree',
+ );
+ fireEvent.mouseLeave(midCard);
+ expect((await cardOf('旁支叔伯')).className).not.toContain(
+ 'game-lineage-node--dimmed',
+ );
+ });
+
it('节点是真实 DOM 卡片:主体点击=选中、标题/打开作品=进详情、当前作品有标记且父在上子在下', async () => {
getGameLineageMock.mockResolvedValueOnce(treeWithSibling);
const onOpenDetail = vi.fn();
diff --git a/src/components/game-distribution/GameLineagePage.tsx b/src/components/game-distribution/GameLineagePage.tsx
index b7ed67c1b..5d8c6c727 100644
--- a/src/components/game-distribution/GameLineagePage.tsx
+++ b/src/components/game-distribution/GameLineagePage.tsx
@@ -247,7 +247,6 @@ export function GameLineagePage({
null,
);
const isMobileViewport = useIsMobileViewport();
- const [includeSubtree, setIncludeSubtree] = useState(true);
/**
* 形态:移动端默认**大纲式缩进树**(无横向滚动、触控友好),可用「图谱视图」切回画布;
* 桌面默认画布。`null` = 跟随当前断点的默认值。
@@ -392,11 +391,11 @@ export function GameLineagePage({
const highlighted = useMemo(
() =>
highlightTargetId
- ? collectLineageHighlight(layout.nodes, highlightTargetId, {
- includeSubtree,
- })
+ ? // 子树高亮**恒定开启**(2026-10-06 口径:原来那个「高亮整棵子树」复选框没有意义,
+ // 已删除,也不再保留一个恒为 true 的开关状态到处传)。
+ collectLineageHighlight(layout.nodes, highlightTargetId)
: null,
- [highlightTargetId, includeSubtree, layout.nodes],
+ [highlightTargetId, layout.nodes],
);
const pathIds = useMemo(
() =>
@@ -860,16 +859,10 @@ export function GameLineagePage({
-
+ {/*
+ 这里原来有个「高亮整棵子树」复选框:它恒为真、没有任何用户可见意义(2026-10-06
+ 口径——删除并**默认开启**)。子树高亮现在是唯一行为,不再有开关状态。
+ */}
{fitHint && embedded ? (
{fitHint}