diff --git a/apps/ai-game-creator-shell/src/view/project-development/index.tsx b/apps/ai-game-creator-shell/src/view/project-development/index.tsx index a788cf4c3..7d7c8105f 100644 --- a/apps/ai-game-creator-shell/src/view/project-development/index.tsx +++ b/apps/ai-game-creator-shell/src/view/project-development/index.tsx @@ -4236,7 +4236,9 @@ export default function ProjectDevelopmentView({ if (!category) { return; } - const viewport = resourceCanvasViewportRef.current; + const viewport = resourceBookOpensAllResources + ? resourceBookAllViewportRef.current + : resourceCanvasViewportRef.current; const next = zoomResourceBookViewport(viewport, factor, { x: anchorX, y: anchorY, @@ -4312,7 +4314,9 @@ export default function ProjectDevelopmentView({ return; } const result = resolveResourceBookWheelViewport({ - viewport: resourceCanvasViewportRef.current, + viewport: resourceBookOpensAllResources + ? resourceBookAllViewportRef.current + : resourceCanvasViewportRef.current, deltaX: event.deltaX, deltaY: event.deltaY, shiftKey: event.shiftKey, @@ -4422,7 +4426,10 @@ export default function ProjectDevelopmentView({ ) { return; } - const scale = resourceCanvasViewportRef.current.scale; + const scale = (resourceBookOpensAllResources + ? resourceBookAllViewportRef.current + : resourceCanvasViewportRef.current + ).scale; if (!Number.isFinite(scale) || scale <= 0) { return; } @@ -4580,7 +4587,9 @@ export default function ProjectDevelopmentView({ category: canvasTarget, startClientX: event.clientX, startClientY: event.clientY, - startViewport: resourceCanvasViewportRef.current, + startViewport: resourceBookOpensAllResources + ? resourceBookAllViewportRef.current + : resourceCanvasViewportRef.current, }; return; } diff --git a/apps/ai-game-creator-shell/tests/appSurface/project-development.suite.ts b/apps/ai-game-creator-shell/tests/appSurface/project-development.suite.ts index 6be8210f6..6c2581e50 100644 --- a/apps/ai-game-creator-shell/tests/appSurface/project-development.suite.ts +++ b/apps/ai-game-creator-shell/tests/appSurface/project-development.suite.ts @@ -9620,6 +9620,23 @@ export function registerProjectAgentStatusTests() { ), ).not.toBeNull(), ); + // 「所有资源」展开态必须复用自己的内存视口:缩放按钮不能拿当前栏目页的旧 ref, + // 否则按钮看起来按了但缩放落点/倍率会回到另一份视口状态。 + const allSceneWorld = document.querySelector( + '[data-resource-book-view="child"] .game-resource-book-scene-world', + ); + expect(allSceneWorld).not.toBeNull(); + const allTransformBeforeZoom = allSceneWorld?.style.transform; + fireEvent.click(screen.getByRole('button', { name: '放大画布' })); + await waitFor(() => + expect(allSceneWorld?.style.transform).not.toBe(allTransformBeforeZoom), + ); + // 旧实现第二次点击仍可能使用栏目页 ref;连续点击必须继续推进 all 视口。 + const allTransformAfterZoom = allSceneWorld?.style.transform; + fireEvent.click(screen.getByRole('button', { name: '放大画布' })); + await waitFor(() => + expect(allSceneWorld?.style.transform).not.toBe(allTransformAfterZoom), + ); // 旧的分组网格宿主与网格容器一个都不该再存在;改回旧分支时这三条会立刻红。 expect( document.querySelector('[data-resource-book-all-host="true"]'), diff --git a/docs/project-memory/shared-memory/decision-log.md b/docs/project-memory/shared-memory/decision-log.md index e0d6051f5..c701ced8f 100644 --- a/docs/project-memory/shared-memory/decision-log.md +++ b/docs/project-memory/shared-memory/decision-log.md @@ -8478,3 +8478,11 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在 - 验证方式:① 单测 `resourceBookLayout.test` 钉住"展开态真实栏目 `titlebar === false`、`all` 为 `true`",并删掉只服务旧标题栏矩形的断言与那个已不存在的导出;② appSurface 两条「所有资源」用例各加一条反向守卫 `document.querySelectorAll('.game-resource-book-scene-titlebar:not([data-resource-book-category="all"])')` 长度为 0(空项目那条正是用户现场);③ **变异验证(已实测红灯)**:把展开态真实栏目的 `titlebar` 改回 `true` → 两条守卫同时红,报 `expected … to have a length of +0 but got 7`——这个 `7` 就是七个真实栏目叠在原点的实证。 - 影响范围:`apps/ai-game-creator-shell/src/view/project-development/{resourceBookLayout.ts,index.tsx}`、`apps/ai-game-creator-shell/tests/{resourceBookLayout.test.ts,appSurface/project-development.suite.ts}`、`docs/technical/【测试用例】AGC资源工作台V3端到端验收-2026-09-11.md`(S7 行措辞)。 - 关联:本条**收窄**(不推翻)2026-09-11 的「所有资源」页画本场景决策:那一条定的是"同一套场景 + 同一颗 renderCard + 分带偏移",本条只改"展开态出不出栏目标题栏"。 + +## 2026-09-12 「所有资源」交互读取独立内存视口:缩放、滚轮、平移不能复用栏目页 ref + +- 背景:上一条决策把「所有资源」定义成一张平铺画布,并明确它没有独立持久化空间;因此它仍需要一份只存在内存中的 `resourceBookAllViewport`,不能塞进按真实栏目键的 `resourceCanvasViewports`。 +- 决策:展开态所有需要读取当前子画布视口的动作(按钮缩放、滚轮缩放/平移、卡片拖动的缩放换算、画布平移起点)统一读取 `resourceBookAllViewportRef`;普通栏目页继续读取 `resourceCanvasViewportRef`。写侧继续通过 `setResourceCanvasViewport('all', ...)` 进入 all 内存状态,不增加 sidecar 分区或第二套坐标空间。 +- 原因:如果读侧沿用栏目 ref,动作会基于最近栏目页的旧缩放/平移值计算,再写入 all;数值碰到缩放边界时用户看到的就是「按钮按了但不动」,且拖动距离也会按错误倍率换算。 +- 验证:appSurface 的多栏目「所有资源」用例连续点击两次放大;四处 ref 选择全部变异回旧实现后第二次点击断言红灯,恢复后通过。定向类型检查与 ESLint 均通过。 +- 影响范围:`apps/ai-game-creator-shell/src/view/project-development/index.tsx`、`apps/ai-game-creator-shell/tests/appSurface/project-development.suite.ts`、`pitfalls.md` 本条。 diff --git a/docs/project-memory/shared-memory/pitfalls.md b/docs/project-memory/shared-memory/pitfalls.md index e1f01182e..01740fb41 100644 --- a/docs/project-memory/shared-memory/pitfalls.md +++ b/docs/project-memory/shared-memory/pitfalls.md @@ -5363,3 +5363,11 @@ - **处理**:让"该不该有这个节点"显式化,而不是靠"矩形取不到就兜底":`ResourceBookSceneCategoryPlan` 增 `titlebar: boolean`,展开态的真实栏目为 `false`(那一页只留 `all` 一条钉住的标题栏),渲染层按它决定是否产出节点;同时删掉只服务旧形态的 `RESOURCE_BOOK_ALL_BAND_TITLE_HEIGHT` / `resourceBookAllTitlebarRects`。**`?? 0` 兜底本身不是错,错在它同时承担"没有布局信息"和"布局信息恰好是原点"两种语义**——凡是一个坐标有多路来源(测量 / 计算 / 静态),缺某一路时要么显式不渲染,要么让缺路成为可观测的信号,不要静默给默认值。 - **验证**:appSurface 两条「所有资源」用例加反向守卫 `querySelectorAll('.game-resource-book-scene-titlebar:not([data-resource-book-category="all"])')` 长度为 0(含空项目那条);变异验证:把展开态真实栏目的 `titlebar` 改回 `true` → 两条同时红,报 `expected … to have a length of +0 but got 7`(7 = 七个真实栏目,实证是"一摞"而不是"一个错位框")。 - **关联**:`apps/ai-game-creator-shell/src/view/project-development/{resourceBookLayout.ts,index.tsx}`、`apps/ai-game-creator-shell/tests/{resourceBookLayout.test.ts,appSurface/project-development.suite.ts}`、`decision-log.md` 2026-09-12 条。 + +## 2026-09-12 「所有资源」缩放拿错视口 ref:按钮能触发但倍率被另一份栏目视口覆盖 + +- **现象**:「所有资源」展开页点击放大/缩小后看起来不动,或倍率跳回栏目页当前值;滚轮缩放、画布平移和卡片拖动也可能出现同类落点异常。 +- **原因**:展开页的视口是独立的内存态 `resourceBookAllViewportRef`,但缩放/滚轮/拖动起算仍读取普通栏目页的 `resourceCanvasViewportRef`。两份 ref 在首次进入或最近切过栏目后数值可能不同,导致 all 页动作以另一张画布的旧值计算,再写回 all 视口;当旧值已在边界时就表现为「按不动」。 +- **处理**:所有需要读取当前子画布视口的交互都按目标选择 ref:`resourceBookOpensAllResources` 时读取 `resourceBookAllViewportRef`,栏目页才读取 `resourceCanvasViewportRef`;写回仍统一经过 `setResourceCanvasViewport`,不新增持久化空间。 +- **验证**:appSurface 的「所有资源」用例新增连续两次点击放大断言;把四处 all ref 选择变异还原为旧的 `resourceCanvasViewportRef` 后,该用例在第二次点击处红,显示 transform 未变化;恢复后用例通过。 +- **关联**:`apps/ai-game-creator-shell/src/view/project-development/index.tsx`、`apps/ai-game-creator-shell/tests/appSurface/project-development.suite.ts`、`decision-log.md` 2026-09-12 条。