修复自查发现的门禁与体验问题:格式、超时态语义、压缩 CPU 边界
Project CI / AI game creator shell Rust crates (pull_request) Successful in 4m48s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 7m3s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 6m33s
Project CI / Frontend tests (pull_request) Successful in 3m24s
Project CI / Backend tests (pull_request) Successful in 7m29s
Project CI / AI game creator shell web tests (pull_request) Successful in 2m55s
Project CI / Repository checks (pull_request) Successful in 6m33s
Project CI / Native shell tests (pull_request) Successful in 9m0s

- 新增 Rust 测试补齐 cargo fmt;ESLint 按仓库约定收口:常量与解析函数拆到 platformGameLoadingSurfaceModel.ts,删除 GAME_PLAY_STARTUP_TIMEOUT_MS 常量别名(测试直接用共享常量)
- 共享加载面新增 stalled:宿主判定等待不再推进时停掉进度动画并把 aria-busy 置回 false(去掉与 role=status 重复的 aria-live)
- 后台试玩超时时传入 stalled,避免「一边转圈一边说超时」
- Accept-Encoding 权重参数名改为大小写不敏感(RFC 9110 §12.5.3),GZIP;Q=0 视为拒绝并补用例
- 发行资源压缩分级:≥2 MiB 改用 level 1(zlib 端实测约 1/3 耗时、压缩比差约 3%),小文件仍用 level 6
- 文档:主规范补压缩与条件请求契约(并写明 60 秒窗口与撤销窗口不放宽)、nginx README 修正 Pingora 压缩关闭时的口径、pitfalls 补 CPU 边界与措辞修正
This commit is contained in:
2026-10-05 18:02:48 +08:00
parent f58f51052c
commit d7c4f82d7d
13 changed files with 138 additions and 69 deletions
@@ -3,13 +3,13 @@
import { act, render, screen } from '@testing-library/react';
import { afterEach, describe, expect, test, vi } from 'vitest';
import { PlatformGameLoadingSurface } from './PlatformGameLoadingSurface';
import {
PLATFORM_GAME_LOADING_ASSET_PHASE_MS,
PLATFORM_GAME_LOADING_ENGINE_PHASE_MS,
PLATFORM_GAME_LOADING_SLOW_PHASE_MS,
PlatformGameLoadingSurface,
resolvePlatformGameLoadingMessage,
} from './PlatformGameLoadingSurface';
} from './platformGameLoadingSurfaceModel';
function readMessage() {
return screen
@@ -84,6 +84,14 @@ describe('PlatformGameLoadingSurface', () => {
expect(screen.getByRole('img', { name: '封面' })).toBeTruthy();
});
test('宿主判定停止推进时不再声明 busy,并停掉进度动画', () => {
render(<PlatformGameLoadingSurface title="待审版本试玩" stalled />);
const surface = screen.getByRole('status');
expect(surface.getAttribute('aria-busy')).toBe('false');
expect(surface.className).toContain('platform-game-loading--stalled');
});
test('没有封面时渲染图标占位而不是空白区块', () => {
const { container } = render(
<PlatformGameLoadingSurface title="加载中" tone="player" />,
@@ -1,18 +1,10 @@
import { Gamepad2 } from 'lucide-react';
import { useEffect, useState, type ReactNode } from 'react';
import { type ReactNode, useEffect, useState } from 'react';
/** 阶段文案切换点:先「准备」,再「资源」,最后「引擎」。 */
export const PLATFORM_GAME_LOADING_ASSET_PHASE_MS = 1_500;
export const PLATFORM_GAME_LOADING_ENGINE_PHASE_MS = 6_000;
/** 超过这个时长仍未进入游戏,补一行「比平时久」的状态,避免用户以为卡死。 */
export const PLATFORM_GAME_LOADING_SLOW_PHASE_MS = 12_000;
/**
* 发行包首屏加载上限。
*
* 网页游玩页与后台审核试玩共用同一口径:超过上限就给出显式超时与重试,而不是让
* 用户对着加载面无限期等待。
*/
export const PLATFORM_GAME_LOADING_TIMEOUT_MS = 20_000;
import {
PLATFORM_GAME_LOADING_SLOW_PHASE_MS,
resolvePlatformGameLoadingMessage,
} from './platformGameLoadingSurfaceModel';
export type PlatformGameLoadingSurfaceTone = 'player' | 'panel';
@@ -30,32 +22,23 @@ export type PlatformGameLoadingSurfaceProps = {
message?: string;
/** 追加一行提示(例如失败的兜底说明)。 */
hint?: string;
/**
* 宿主已判定这次等待不再推进(例如已经给出超时结论):停掉进度动画并把
* `aria-busy` 置回 `false`,避免「一边转圈一边说超时」。
*/
stalled?: boolean;
className?: string;
};
const DEFAULT_COPY_BY_PHASE: Record<'prepare' | 'assets' | 'engine', string> = {
prepare: '正在准备游戏…',
assets: '正在加载游戏资源…',
engine: '正在启动游戏引擎…',
};
/** 按已等待时长给出阶段文案;与 `PlatformGameLoadingSurface` 内的计时同源。 */
export function resolvePlatformGameLoadingMessage(elapsedMs: number) {
if (elapsedMs >= PLATFORM_GAME_LOADING_ENGINE_PHASE_MS) {
return DEFAULT_COPY_BY_PHASE.engine;
}
if (elapsedMs >= PLATFORM_GAME_LOADING_ASSET_PHASE_MS) {
return DEFAULT_COPY_BY_PHASE.assets;
}
return DEFAULT_COPY_BY_PHASE.prepare;
}
/**
* 平台通用「游戏加载面」。
*
* 发行包(Phaser/Vite 产物)首屏要下完整个引擎与资源,网络稍慢就是好几秒;这段时间
* 网页游玩页与后台审核试玩都必须给出**看得见的**加载反馈,而不是近黑面板上的一行小字
* 或一片空白。阶段文案由组件自己按时长推进,宿主只负责在真正进入游戏后把它换掉。
*
* 组件按绝对定位铺满**最近的定位祖先**:宿主必须给它一个 `position: relative` 的容器
* (网页游玩页是 `.game-player-shell`,后台试玩是 `.admin-game-review-preview-shell`)。
*/
export function PlatformGameLoadingSurface({
title,
@@ -63,6 +46,7 @@ export function PlatformGameLoadingSurface({
tone = 'panel',
message,
hint,
stalled = false,
className,
}: PlatformGameLoadingSurfaceProps) {
const [elapsedMs, setElapsedMs] = useState(0);
@@ -84,12 +68,12 @@ export function PlatformGameLoadingSurface({
return (
<div
role="status"
aria-live="polite"
aria-busy="true"
aria-busy={stalled ? 'false' : 'true'}
aria-label={`${title} ${statusMessage}`}
className={[
'platform-game-loading',
`platform-game-loading--${tone}`,
stalled ? 'platform-game-loading--stalled' : null,
className,
]
.filter(Boolean)
+2 -2
View File
@@ -48,14 +48,14 @@ export type {
PlatformGameLoadingSurfaceProps,
PlatformGameLoadingSurfaceTone,
} from './PlatformGameLoadingSurface';
export { PlatformGameLoadingSurface } from './PlatformGameLoadingSurface';
export {
PLATFORM_GAME_LOADING_ASSET_PHASE_MS,
PLATFORM_GAME_LOADING_ENGINE_PHASE_MS,
PLATFORM_GAME_LOADING_SLOW_PHASE_MS,
PLATFORM_GAME_LOADING_TIMEOUT_MS,
PlatformGameLoadingSurface,
resolvePlatformGameLoadingMessage,
} from './PlatformGameLoadingSurface';
} from './platformGameLoadingSurfaceModel';
export type {
PlatformIconBadgeProps,
PlatformIconBadgeShape,
@@ -0,0 +1,29 @@
/** 阶段文案切换点:先「准备」,再「资源」,最后「引擎」。 */
export const PLATFORM_GAME_LOADING_ASSET_PHASE_MS = 1_500;
export const PLATFORM_GAME_LOADING_ENGINE_PHASE_MS = 6_000;
/** 超过这个时长仍未进入游戏,补一行「比平时久」的状态,避免用户以为卡死。 */
export const PLATFORM_GAME_LOADING_SLOW_PHASE_MS = 12_000;
/**
* 发行包首屏加载上限。
*
* 网页游玩页与后台审核试玩共用同一口径:超过上限就给出显式超时与重试,而不是让
* 用户对着加载面无限期等待。
*/
export const PLATFORM_GAME_LOADING_TIMEOUT_MS = 20_000;
const DEFAULT_COPY_BY_PHASE: Record<'prepare' | 'assets' | 'engine', string> = {
prepare: '正在准备游戏…',
assets: '正在加载游戏资源…',
engine: '正在启动游戏引擎…',
};
/** 按已等待时长给出阶段文案;与 `PlatformGameLoadingSurface` 内的计时同源。 */
export function resolvePlatformGameLoadingMessage(elapsedMs: number) {
if (elapsedMs >= PLATFORM_GAME_LOADING_ENGINE_PHASE_MS) {
return DEFAULT_COPY_BY_PHASE.engine;
}
if (elapsedMs >= PLATFORM_GAME_LOADING_ASSET_PHASE_MS) {
return DEFAULT_COPY_BY_PHASE.assets;
}
return DEFAULT_COPY_BY_PHASE.prepare;
}
@@ -1677,6 +1677,14 @@ textarea.genarrative-ui-text-field__control {
background: var(--platform-button-primary-fill, #c7653d);
}
/* 宿主已判定这次等待不再推进(例如已经给出超时结论):进度条停下并压暗,
避免出现「一边转圈一边说超时」的自相矛盾。 */
.platform-game-loading--stalled .platform-game-loading__fill {
animation: none;
transform: none;
opacity: 0.45;
}
.platform-game-loading__hint {
margin: 0;
font-size: 0.72rem;