Merge remote-tracking branch 'origin/master' into fix/agc-composer-layout
Project CI / AI game creator shell Rust crates (pull_request) Successful in 4m59s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 7m17s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 6m38s
Project CI / Frontend tests (pull_request) Successful in 3m1s
Project CI / Backend tests (pull_request) Successful in 7m16s
Project CI / AI game creator shell web tests (pull_request) Successful in 2m15s
Project CI / Repository checks (pull_request) Successful in 5m19s
Project CI / Native shell tests (pull_request) Successful in 7m2s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 4m59s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 7m17s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 6m38s
Project CI / Frontend tests (pull_request) Successful in 3m1s
Project CI / Backend tests (pull_request) Successful in 7m16s
Project CI / AI game creator shell web tests (pull_request) Successful in 2m15s
Project CI / Repository checks (pull_request) Successful in 5m19s
Project CI / Native shell tests (pull_request) Successful in 7m2s
合并 master 96859666f(AGC 运行画面点选 #624 等):保留双方条目 - docs/project-memory/shared-memory/pitfalls.md 冲突:本分支 2 条(2026-10-05 PR #607 复核修复 / 模型菜单共享样式面)在前,master 新增 6 条(运行画面点选 ×5、Phaser 4 ×2)紧随其后,其余各节原样保留 - 其余文件自动合并(master 侧改动与本分支不重叠)
This commit is contained in:
@@ -23,6 +23,53 @@
|
||||
- **后续(同一批收口)**:强度区与外壳锚点也一并抬到共享面——`view/home/index.tsx` 给首页那颗传了**同一个** `ComposerReasoningEffortSelect`(`presentation="section"`,读写仍是组件自己的 `read_game_creator_app_config` / `select_game_creator_reasoning_effort`),首页因此与工作台逐项同形:外壳 260×262.5、强度区 234×60.5、列表 234×170、滑块 222×26(447 与 1200 两档实测一致;工作台回归后逐值不变)。抬升时必须删掉工作台作用域里两条**死重**规则(给旧独立「推理档」控件排版的 `display: inline-flex` 一族),否则它们会以更高特异性盖掉共享面的 `display: grid`,把强度区重新算宽 24px。
|
||||
- **滑块两点(最终收口)**:档位圆点先是改成派生浅暖色、直径收到 6px(= 轨道高度),随后**整体删除**(见上面 D1 条)。「进度条高度与滑块高度一致」保留两种解释对照:**变体 A 落地**(填充段 = 轨道高度 6px),**变体 B**(填充段做成 20px = 圆钮高度)只在夹具里渲染出图、未提交。
|
||||
|
||||
## 2026-10-05 运行画面点选的高亮框被尺寸上报当成页面内容:点选后预览自己缩放
|
||||
|
||||
- **现象**:three 项目里点选 3D 对象后预览画面自己缩放、视角异常(相机 aspect 与重新适配的距离都变了),点选结束后视口仍停在放大后的尺寸。实测夹具里是**自激振荡**:10 秒内 1459 条尺寸消息、1457 次 iframe 尺寸/缩放变更、1454 次 `resize`,相机 aspect 在 1.607143 ↔ 1.421112 之间来回变。
|
||||
- **原因**:高亮框是 `document.body` 下的 `div[data-genarrative-preview-inspect]`(`position: fixed`、尺寸随命中对象),而尺寸上报的 `measureContentBounds` 用 `createTreeWalker(body, SHOW_ELEMENT)` 遍历所有元素并把 `rect.right/bottom` 计入内容尺寸。three 档的高亮矩形来自 `Box3` 八角投影,物体贴近相机时投影盒远超画布(实测 6607×4721、left/top 为负)→ 上报内容被撑到 3756×2643(视口只有 900×560)→ 宿主 `resolveLocalGamePreviewFitLayout` 把 iframe 改成 3756×2643 + scale 0.2119 → 游戏 `resize` 重算相机 → 视口变大又让投影盒更大,如此循环。
|
||||
- **结论(现行口径)**:① 桥自己的节点一律不进内容尺寸测量——`measureContentBounds` 的 TreeWalker 用 `acceptNode` 对 `data-genarrative-preview-fit` / `data-genarrative-preview-inspect` 返回 `FILTER_REJECT`;以后新增任何桥注入的 DOM 节点都要带上这两个标记之一,否则会重新引入这条反馈。② 引擎档命中矩形统一裁剪到画布可见范围(`clipInspectRect`;与画布无交集时退化为指针点矩形),不再出现比画面还大的高亮框。
|
||||
- **回归**:`apps/ai-game-creator-shell/tests/localPreviewInspectSizeStability.test.ts`(5 例:桥节点不参与测量、同尺寸普通节点仍计入的对照组、进入检查模式与 hover 后尺寸与宿主适配布局不变、投影盒超出画布时载荷被裁剪、与画布无交集时退化为 1×1)。
|
||||
- **关联**:`resources/preview/local-preview-fit.js`(`measureContentBounds` / `clipInspectRect` / `threeInspectTarget` / `phaserSelection`)、`features/project-workspace/LocalGamePreviewFrame.tsx`(`resolveLocalGamePreviewFitLayout`)。
|
||||
|
||||
## 2026-10-05 把整个 `THREE` 命名空间塞进预览句柄会让 tree-shaking 失效
|
||||
|
||||
- **现象**:three 项目的运行画面点选句柄写成 `{ engine: 'three', THREE, scene, camera, renderer }` 时,同一个 Vite 构建的产物从 517,682 B 涨到 730,831 B(+213,149 B ≈ +41%)。
|
||||
- **原因**:句柄引用整个 `import * as THREE` 命名空间,打包器无法证明未使用的导出可以剪掉;预览桥实际只用 `Raycaster` / `Vector2` / `Vector3` / `Box3` 四个构造器。
|
||||
- **结论(现行口径)**:句柄用瘦身形态 `three: { Raycaster, Vector2, Vector3, Box3 }`;该字段只给预览桥用,玩法代码不要引用它。旧形态仍被兼容读取(两种形态都有用例锁定),但新代码与模板一律用瘦身形态。
|
||||
|
||||
## 2026-10-05 运行画面点选 three 档的句柄少了构造器会整体失效
|
||||
|
||||
- **现象**:three 项目句柄里 `Raycaster` / `Vector2` / `Vector3` / `Box3` 只缺一个,桥就**不进引擎档**:芯片回落 `@canvas`、控制台不报错——症状与「根本没发句柄」完全一样,容易误判成「点选没生效」。
|
||||
- **原因**:引擎档入口先认句柄形态、再逐个校验这四个构造器是否为 function,缺任一即按未命中退化(不降级、不猜);射线需要 `Raycaster` + `Vector2`,对象矩形需要 `Box3` + `Vector3`,两组能力缺一方都点不出对象。
|
||||
- **排查顺序(现行口径)**:① 预览页有没有 `window.__GENARRATIVE_PREVIEW_GAME__`;② `engine` 是不是 `phaser` / `three`;③ 这四个构造器齐不齐(瘦身形态是 `three.Raycaster` 等,旧形态是 `THREE.Raycaster`);④ `scene` / `camera` / `renderer`,three 侧还要 `renderer.domElement` 带 `getBoundingClientRect`。
|
||||
- **关联**:`apps/ai-game-creator-shell/src-tauri/resources/preview/local-preview-fit.js`(`threeInspectTarget`)、`agc-web-game-development` 的「运行画面点选契约」、`tests/runtimeInspectEngines.test.ts`(瘦身句柄命中、缺构造器退化两例)。
|
||||
|
||||
## 2026-10-05 预览桥脚本是编译期内嵌的:改完必须重启 AGC 客户端
|
||||
|
||||
- **现象**:改了桥脚本(运行画面点选 / 尺寸上报逻辑),Vite HMR 与刷新预览页都不换——预览页仍跑旧桥,新加的判定与兜底完全不生效。
|
||||
- **原因**:`apps/ai-game-creator-shell/src-tauri/resources/preview/local-preview-fit.js` 由 `preview.rs` 用 `include_str!` **编译期**编进客户端 exe,预览服务器 `/__genarrative/local-preview-fit.js` 返回的就是 exe 里那份常量,重读磁盘不会发生。
|
||||
- **结论(现行口径)**:改桥后必须重启 AGC 客户端才生效——先确认没有在跑的 AGC Vite(3080 等端口空闲)再 `npm run agc`;只刷新页面、只重启后端或只重装 npm 依赖都无效。核实内嵌版本:在 exe 二进制里搜新代码标记,或比对 `resources/preview/local-preview-fit.js` 的 sha256。
|
||||
- **关联**:`apps/ai-game-creator-shell/src-tauri/src/preview.rs`(`PREVIEW_FIT_BRIDGE_SCRIPT`)、`apps/ai-game-creator-shell/src-tauri/resources/preview/local-preview-fit.js`、`genarrative-dev-stack-port-routing`(端口探测与 `npm run agc` 口径)。
|
||||
|
||||
## 2026-10-05 预览桥的注入与去重在 Rust 侧没有用例覆盖
|
||||
|
||||
- **现象 / 风险**:改动 `preview.rs` 的注入逻辑(`inject_preview_fit_bridge` / `build_preview_response` / `PREVIEW_FIT_BRIDGE_TAG`)时,没有自动化门禁会告诉你「标签没注入」或「注入了两次」。两种失效都只在运行时才暴露:没注入等于整套运行画面点选静默失效(桥脚本的尺寸上报与点选都不执行,页面看起来完全正常);注入两次会让桥的监听、尺寸上报与点选回调各注册一遍,页面同样看不出差别。
|
||||
- **现状**:`preview.rs` 的 `mod tests` 只有 4 个用例——`npm_preview_requires_build_and_prefers_bundled_assets`、`root_layout_serves_root_entry_and_keeps_legacy_paths_available`、`root_layout_does_not_expose_control_or_data_directories`、`legacy_layout_serves_root_ui_modules`;它们只断言预览路由的选取、状态行与页面自身文本,不涉及桥标签是否出现、出现几次,也不覆盖 `scan_preview_fit_bridge_html` 的「页面已带标签就不重复注入」分支(`inject_preview_fit_bridge` 里的 `if scan.has_bridge_script { return html.into_bytes(); }`)。
|
||||
- **结论(现行口径)**:这类「把常量原样返回 / 标签字符串存在」的转发型行为不固化成长用例(仓库口径:复制与源码文本断言不进测试)。改注入逻辑时按人工验证清单核对:① 响应 HTML 里 `<script src="/__genarrative/local-preview-fit.js"></script>` 出现在 `</body>` 前,且是经典脚本(不带 `type` / `nomodule`);② 页面自身已带该标签时,响应里的标签数量不增加;③ `/__genarrative/local-preview-fit.js` 返回 200,且内容与 `resources/preview/local-preview-fit.js` 逐字节一致(`include_str!` 内嵌,可比 sha256)。
|
||||
- **关联**:`apps/ai-game-creator-shell/src-tauri/src/preview.rs`(`PREVIEW_FIT_BRIDGE_SCRIPT` / `PREVIEW_FIT_BRIDGE_TAG` / `inject_preview_fit_bridge`)、`apps/ai-game-creator-shell/src-tauri/resources/preview/local-preview-fit.js`。
|
||||
|
||||
## 2026-10-04 Phaser 4 `hitTestPointer` 的返回顺序不是叠放顺序
|
||||
|
||||
- **现象**:运行画面点选 Phaser 4 画面时,按「数组第一个 / 最后一个」当最上层会点错对象——点的是上层精灵,引用却落到下层的素材上;对象越多越容易错。
|
||||
- **原因**:`hitTestPointer` 返回的是输入对象的注册 / 内部列表顺序,与真实叠放无关;叠放由命中相机 `renderList` 的索引决定(含 depth 与入序)。
|
||||
- **结论(现行口径)**:点选命中要按 `pointer.camera.renderList` 的索引取最上层;几何兜底也必须按 depth + 入序排序,不能沿用注册顺序取值。
|
||||
|
||||
## 2026-10-04 Phaser 4 默认 XHR 装载下贴图元素是 blob 地址
|
||||
|
||||
- **现象**:运行画面点选拿不到原始素材路径——贴图元素上的地址是会话内的 blob 地址,按文件名反推素材会得到无意义的临时名。
|
||||
- **原因**:Phaser 4 默认用 XHR 装载图片,地址只在当前会话内有效,不携带仓库 / manifest 里的路径信息。
|
||||
- **结论(现行口径)**:Phaser 4 项目的素材身份必须靠玩法显式标注(`setData`)发布,不要试图从 blob 地址反推文件名;没有标注就退化为不带素材的区域引用。与主规范「运行画面素材点选(2026-10-04)」的边界一致。
|
||||
|
||||
## 2026-10-04 把非素材任务塞进「生成任务」账本:taskType 维度、v1 兼容与 live 集合
|
||||
|
||||
- **现象**:首页「AI 项目命名」改成进「生成任务」列表后,第一次读账本(工作台打开项目就会读)就把那条还在跑的命名任务标成**失败**;随后前端的终态推进被拒(记录已终态)。
|
||||
@@ -6460,3 +6507,13 @@ Cocos Creator 根目录由 `package.json.creator.version` 与普通 `assets/`
|
||||
- **别踩**:不要写成裸前缀正则(`^/pay`)——它会吞掉 `/payment/x`、`/paycheckout/x` 这类同名邻居;也不要把深链塞进精确 allowlist 的 alternatives 里(`pay` 的 alternatives 只匹配 `/pay`)。
|
||||
- **判据/取证**:`node --test scripts/check-nginx-spa-routes.test.mjs`(正/反用例,含「写回精确匹配即红」)、`npm run check:nginx-spa-routes`、`npm run check:pingora-route-parity`、`cargo test -p pingora-gateway -- pay_checkout_deep_link matches_nginx_route_parity_matrix`;线上复验 `curl -s -o /dev/null -w '%{http_code}' https://<平台域名>/pay/<checkoutToken>` → 200 且正文与 `/` 同一份 `index.html`。
|
||||
- **关联**:`scripts/check-nginx-spa-routes.mjs`、`deploy/pingora/nginx-route-parity.matrix.json`、`server-rs/crates/pingora-gateway/src/main.rs`、`server-rs/crates/api-server/src/payment.rs`、`deploy/nginx/genarrative.conf`。
|
||||
|
||||
## 2026-10-05 在线游玩「一直黑屏」:加载面缺失 + 发行网关不压不发 ETag
|
||||
|
||||
- **现象**:用户反馈「进入游玩…加载有点慢,一直黑屏体验不好」。真实栈(真实发行包 + Chromium + 4 Mbps/100 ms 模拟链路)实测:网页游玩页点击「开始游戏」后 99.3% 像素亮度 < 24 的近黑面板 + 一行 `游戏正在启动…`,游戏画面 **3298 ms** 才出现;后台审核页点「试玩当前待审版本」后 iframe 直接以 `opacity:1` 出现、区域**全白空白** 4587 ms,页面**全程没有任何加载文案**。
|
||||
- **根因**:① 网页游玩页的加载态只有一行小字盖在 `#17131b` 近黑壳上,没有封面/进度/阶段文案,`ready` 判定又只看 iframe `onLoad`(文档加载完成 ≠ 游戏可玩);② 后台试玩只有 `<iframe class="admin-game-review-preview">`,没有加载态、没有超时、没有失败处理,会话过期或资源 404 会永远停在空白;③ 发行网关把 ZIP 解压后的原文直出,`phaser.min.js` 1,375,976 B 原样下发(`Content-Encoding: none`),也没有 ETag —— 4 Mbps 下光这一个文件就 ~2.7 s,且 60 秒 `max-age` 过后浏览器只能重下整包。
|
||||
- **处理(现行口径)**:新增共享组件 `PlatformGameLoadingSurface`(`packages/shared`,封面/标题 + 动效进度 + 按时长推进的阶段文案 + 慢加载提示),网页游玩页与后台试玩共用;两端的加载上限统一为 `PLATFORM_GAME_LOADING_TIMEOUT_MS`(20 秒),后台超时后给出「重新创建试玩会话」。发行网关对文本类资源(HTML/JS/CSS/JSON/SVG/WASM,≥1 KiB、客户端接受 gzip)下发 `Content-Encoding: gzip` + `Vary: Accept-Encoding`,并下发按 `versionId + 资源路径` 摘要的强 ETag,命中 `If-None-Match` 返回 304(无正文)。
|
||||
- **边界**:图片/音频/视频等已是压缩格式的资源不压;后台试玩会话仍是 `no-store` 且不给 ETag;`max-age=60, must-revalidate` 与撤销窗口不变(换版/下架仍最迟 60 秒对新请求生效,304 只是在窗口之后省掉重下)。边缘 gzip/Brotli 与本层不冲突:两边都以「响应已带 `Content-Encoding` 就跳过」收口;但 Pingora 网关在自身压缩关闭时会**移除** `accept-encoding`(`normalize_accept_encoding_for_gateway_compression`),那种部署形态下源站压缩不会生效,属于网关侧口径。
|
||||
- **CPU 边界**:发行网关是公开无鉴权端点,压缩按请求实时算。release 构建实测 level 6 为 1.3 MiB→10 ms、8 MiB→59 ms、64 MiB(单文件上限)→522 ms 纯 CPU,因此在 `RELEASE_COMPRESSION_FAST_ABOVE_BYTES`(2 MiB)以上改用 level 1(zlib 端实测 level 1 约为 level 6 的 1/3 耗时、压缩比只差约 3%)。若后续要再做减法,优先把压缩结果按 `(对象键, 资源路径)` 缓存,而不是放宽级别。
|
||||
- **验证**:`cargo test -p api-server --bin api-server -- game_distribution`(新增 ETag 作用域、`If-None-Match` 列表/弱校验命中、`gzip;q=0` 拒绝、文本压缩与二进制/小文件不压、304 无正文、`*` 对包内缺失路径仍 404 等用例);`npx vitest run packages/shared/src/components/PlatformGameLoadingSurface.test.tsx`、`src/components/game-distribution/GameDistributionPages.test.tsx`、`apps/admin-web/src/pages/AdminGameDistributionReviewPage.test.tsx`;`npm run check:game-distribution-ops-rollback-e2e` 47 项通过(含压缩/Vary/ETag/304/不接受 gzip 四条新断言)。真实栈同链路复跑:`phaser.min.js` 1,375,976 B → 353,336 B(gzip,4 Mbps 下 2724 ms → 774 ms),用户端游戏画面 3298 ms → 1543 ms、后台试玩 4587 ms → 2694 ms。
|
||||
- **关联**:`packages/shared/src/components/PlatformGameLoadingSurface.tsx`、`src/components/game-distribution/GamePlayPage.tsx`、`apps/admin-web/src/pages/AdminGameDistributionReviewPage.tsx`、`server-rs/crates/api-server/src/modules/game_distribution.rs`、`deploy/nginx/README.md`。
|
||||
|
||||
Reference in New Issue
Block a user