清理:删除零引用的 CoCreationHotDerived 及其 .co-creation-hot* 样式(热门衍生栏已并入图谱页子代块)
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
This commit is contained in:
@@ -482,7 +482,7 @@ A 路线里那个必须提前知道的互斥点已经按既有做法绕开:`cr
|
||||
- **移动端门限豁免(2026-10-06)**:平台原有的「移动端仅支持作品展示」欢迎弹窗只看 `!isDesktopLayout`、忽略路由,会把公开只读的族谱树 / 共创主题页首屏盖住。现在按**显式白名单**豁免这几个只读浏览 stage:`game-lineage` / `game-themes` / `game-theme` / `game-co-creation`(清单在 `PlatformActiveMobileWelcomeDialog.tsx` 的 `MOBILE_WELCOME_EXEMPT_STAGES`,键名即 stage 名,不做前缀/范围匹配)。**创作 / 项目 / 发布 / 我的 / 平台首页仍保持门限**——那些才是「移动端不支持体验创作工具」的部分;弹窗文案未改(与豁免后的事实不矛盾:族谱与主题页本身就是作品展示)。
|
||||
- **画布交互硬化(2026-10-07,用户口径)**:① **滚轮只缩放画布**——React 的 `onWheel` 挂在根容器上且是 **passive** 的(`preventDefault` 无效 ⇒ 页面跟着滚),因此改为在**画布元素本身**挂原生 `wheel` 监听并显式 `{ passive: false }`(不绑 `window`);② **拖动不选中/不拖走文本**——画布容器 `user-select: none` + `touch-action: none` + `overscroll-behavior: contain`,拖动期间把 `user-select: none` 压到 `document.body`(跨出画布也有效,松手恢复),并对 `selectstart` / `dragstart` 挂原生阻止、画布内图片 `-webkit-user-drag: none; pointer-events: none`;指针捕获(`setPointerCapture`)保证移出容器仍跟手;③ **平移边界**:`clampLineagePan`(纯函数)保证任何时刻至少 `LINEAGE_PAN_KEEP_PX = 96` 像素内容可见,拖到底/拖到顶不会把内容拖飞。真机实测(Chromium,1440×900):滚轮后缩放 60%→65%、`window.scrollY` 与平台滚动容器 `scrollTop` **都不变**;两次连续拖动各位移 +120px 且 `window.getSelection()` 始终为空字符串。
|
||||
- **「一键开始共创」位置与未安装检测(2026-10-07,用户口径)**:按钮从共创信息卡**搬到主操作行**(与「立即玩 / 收藏」并列,保持主操作视觉权重;卡内不再重复出现)。未安装检测:纯 web **拿不到系统级安装信息**,只能靠「唤起后浏览器是否被接管」间接判断(`blur` / `visibilitychange`,超时 `FORK_LAUNCH_FALLBACK_DELAY_MS = 1200ms`);**未接管 / 移动端视为「没有客户端」→ 打开与顶栏「下载客户端」同一个下载入口**(`requestClientDownload()` 事件 → `ClientDownloadEntry` 弹窗),并保留手动复制作品 ID 的降级路径与一句人话提示。**局限**:慢启动的客户端/被用户取消的系统确认框/某些不派发 blur 的环境都会误判成「未安装」,代价只是多打开一个下载入口(用户可关掉重试),不会阻断正常路径;老客户端(不响应深链)走同一条兜底。
|
||||
- **共创信息卡只留「标签 + 数据 + 入口」(2026-10-07,用户口径)**:卡里保留世代/授权标签、父代文案、数据概览(游玩合计与分解、累计衍生物、累计创作世代数)、「查看创作族谱」与所属主题 tag;**派生作品卡列表(含原「热门衍生栏」)整块移除**,改由族谱页**图下方**的「父代 / 子代」两块清单承担(父代 = 祖先链根→直接父;子代 = **直接子代**、按**热度(自身 `playCount`)降序**、同热度保持服务端创建序)。选「直接子代」的理由:与 `forkCount`/「被改编 N」同口径(数字与卡片数一致)、与画布「只看父代与子代」开关同义、更深的层级已由上面的树表达。
|
||||
- **共创信息卡只留「标签 + 数据 + 入口」(2026-10-07,用户口径)**:卡里保留世代/授权标签、父代文案、数据概览(游玩合计与分解、累计衍生物、累计创作世代数)、「查看创作族谱」与所属主题 tag;**派生作品卡列表(含原「热门衍生栏」)整块移除**(`CoCreationHotDerived` 组件与其 `.co-creation-hot*` 样式已删除,零引用),改由族谱页**图下方**的「父代 / 子代」两块清单承担(父代 = 祖先链根→直接父;子代 = **直接子代**、按**热度(自身 `playCount`)降序**、同热度保持服务端创建序)。选「直接子代」的理由:与 `forkCount`/「被改编 N」同口径(数字与卡片数一致)、与画布「只看父代与子代」开关同义、更深的层级已由上面的树表达。
|
||||
- **截断 / 空树 / 不可用父节点**在两种形态下都有降级:截断提示、空态、以及「原作品已不可用」的占位节点(可聚焦、不可点进详情、不展示被删作品标题与作者名)。
|
||||
- **「共创」页签口径(2026-10-07,用户口径)**:页签与「游戏」页签**复用同一个 `GameGalleryPage`**(`scope="forkable"`),数据来自公开目录并带 `forkable=1`(分页/游标/去重/末页/失败态全部沿用已实现的「加载更多」);**服务端尚未生效期间客户端再兜一层同义过滤**(`forkAuthorization !== 'forbidden'`,未知按禁止失败关闭),两条口径一致、服务端上线后等价幂等。页签不再指向共创主题列表(主题列表页与路由保留,无页签入口);页面标题 `game-co-creation` → 「共创 - 陶泥儿」,路由 `/games/co-creation`,移动端门限同样按「只读浏览 = 作品展示」豁免。空态为「暂时没有开放共创的作品」。
|
||||
- **初始适配口径(2026-10-07 复核)**:初次进入按**宽高同时 fit**(`min(scaleX, scaleY)` + `FIT_PADDING`)并**水平垂直都居中**;若这个比例低于可读下限 `READABLE_MIN_SCALE = 0.6`,就**不强行全览**,改用可读缩放 + 平移,并给出明确提示「树较大:已按可读缩放显示(其余代际在视口外),拖拽平移查看,或点「适应窗口」看全树。」——两种情况下都**不会**在没有提示的前提下把最深的代际丢在视口外。窄屏「图谱视图」另按**宽度**适配(见上文)。
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
import type { GameDistributionLineageNode } from '../../services/gameDistributionClient';
|
||||
import { coCreationGenerationShortLabel } from './coCreationCopy';
|
||||
import { GameCover } from './GameCover';
|
||||
import type { LineagePlayCountBreakdown } from './lineageTreeLayout';
|
||||
|
||||
/**
|
||||
* 热门衍生栏:当前作品下热度最高的 5 个分支作品(横向卡片)。
|
||||
*
|
||||
* 热度口径:**`playCount` 倒序**,同热度按创建顺序(服务端顺序)稳定兜底。
|
||||
* `/derived` 目前不保证服务端排序,所以排序在客户端做;后端若补了排序,
|
||||
* 这段逻辑仍然成立(只是排序变成幂等)。
|
||||
*
|
||||
* 显示口径(2026-10-06):「游玩次数」的**显示**一律是**子树合计**(父代展示自己 + 全部后代)。
|
||||
* 传了 `playCountTotals` 时排序与显示都用合计(这样列表顺序与卡片上的数字一致);
|
||||
* 传不进来(拿不到树)时回退服务端的**自身值**,不暗示含衍生。
|
||||
*/
|
||||
export const CO_CREATION_HOT_DERIVED_LIMIT = 5;
|
||||
|
||||
export function CoCreationHotDerived({
|
||||
nodes,
|
||||
playCountTotals,
|
||||
onOpenGameDetail,
|
||||
}: {
|
||||
nodes: readonly GameDistributionLineageNode[];
|
||||
/**
|
||||
* 每个节点的游玩次数分解(详情页用同一棵 `/lineage` 在前端算好的,零额外请求)。
|
||||
* 缺失时显示自身值。
|
||||
*/
|
||||
playCountTotals?: ReadonlyMap<string, LineagePlayCountBreakdown> | null;
|
||||
onOpenGameDetail: (gameId: string) => void;
|
||||
}) {
|
||||
const shownPlayCount = (gameId: string, own: number) =>
|
||||
playCountTotals?.get(gameId)?.total ?? own;
|
||||
const hottest = [...nodes]
|
||||
.sort(
|
||||
(left, right) =>
|
||||
shownPlayCount(right.gameId, right.playCount) -
|
||||
shownPlayCount(left.gameId, left.playCount),
|
||||
)
|
||||
.slice(0, CO_CREATION_HOT_DERIVED_LIMIT);
|
||||
if (hottest.length === 0) return null;
|
||||
|
||||
return (
|
||||
<div className="co-creation-hot" aria-label="热门衍生作品">
|
||||
{hottest.map((node) => (
|
||||
<button
|
||||
key={node.gameId}
|
||||
type="button"
|
||||
className="co-creation-hot__card"
|
||||
aria-label={`热门衍生《${node.title}》· ${coCreationGenerationShortLabel(node.generation)}`}
|
||||
onClick={() => onOpenGameDetail(node.gameId)}
|
||||
>
|
||||
<span className="co-creation-hot__cover">
|
||||
<GameCover
|
||||
game={gameForCover(node)}
|
||||
objectKey={node.coverObjectKey}
|
||||
/>
|
||||
</span>
|
||||
<span className="co-creation-hot__title">{node.title}</span>
|
||||
<span className="co-creation-hot__meta">
|
||||
{coCreationGenerationShortLabel(node.generation)} ·{' '}
|
||||
{node.authorName?.trim() || '未署名作者'} ·{' '}
|
||||
{shownPlayCount(node.gameId, node.playCount)} 次游玩
|
||||
</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/** `GameCover` 只读 title / coverColor / icon / coverObjectKey;族谱节点没有完整游戏行。 */
|
||||
function gameForCover(node: GameDistributionLineageNode) {
|
||||
return {
|
||||
title: node.title,
|
||||
coverColor: '#F3E4D0',
|
||||
icon: '🎮',
|
||||
coverObjectKey: node.coverObjectKey,
|
||||
} as never;
|
||||
}
|
||||
|
||||
export default CoCreationHotDerived;
|
||||
@@ -4818,7 +4818,8 @@ describe('GameDetailPage 共创模块(文档对齐)', () => {
|
||||
expect(
|
||||
within(card).getByRole('button', { name: '查看创作族谱' }),
|
||||
).toBeTruthy();
|
||||
// 卡里**不再有**派生作品卡列表(热门衍生栏 / 任何父代子代清单)。
|
||||
// 卡里**不再有**派生作品卡列表:`CoCreationHotDerived` 组件与它的 `.co-creation-hot*` 样式
|
||||
// 已于 2026-10-07 删除(零引用),这里钉住「它不会再回来」。
|
||||
expect(card.querySelector('.co-creation-hot')).toBeNull();
|
||||
expect(screen.queryByLabelText('热门衍生作品')).toBeNull();
|
||||
expect(within(card).queryByText('被改编出来的作品')).toBeNull();
|
||||
|
||||
@@ -812,50 +812,6 @@
|
||||
color: var(--platform-text-soft);
|
||||
cursor: not-allowed;
|
||||
}
|
||||
/* 热门衍生栏:横向卡片。 */
|
||||
.game-detail-info-card .co-creation-hot {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(min(7rem, 100%), 1fr));
|
||||
gap: 0.4rem;
|
||||
}
|
||||
.game-detail-info-card .co-creation-hot__card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.2rem;
|
||||
min-width: 0;
|
||||
border: 1px solid var(--platform-surface-border);
|
||||
border-radius: 0.6rem;
|
||||
padding: 0.3rem;
|
||||
background: var(--platform-surface, #fff);
|
||||
color: var(--platform-text-base);
|
||||
font: inherit;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
}
|
||||
.game-detail-info-card .co-creation-hot__cover {
|
||||
display: block;
|
||||
width: 100%;
|
||||
aspect-ratio: 16 / 9;
|
||||
overflow: hidden;
|
||||
border-radius: 0.4rem;
|
||||
background: var(--platform-surface-border);
|
||||
}
|
||||
.game-detail-info-card .co-creation-hot__cover .game-cover {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 0;
|
||||
}
|
||||
.game-detail-info-card .co-creation-hot__title {
|
||||
color: var(--platform-text-strong);
|
||||
font-size: 0.74rem;
|
||||
font-weight: 600;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
.game-detail-info-card .co-creation-hot__meta {
|
||||
color: var(--platform-text-soft);
|
||||
font-size: 0.68rem;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
/* 画布内悬浮控件(右下角):圆角胶囊、浅色底 + 毛玻璃、内边距统一、轻阴影。 */
|
||||
.game-lineage-controls {
|
||||
position: absolute;
|
||||
|
||||
Reference in New Issue
Block a user