沉淀 AGC 画布现状与取数约束到共享记忆

- pitfalls.md 追加「AGC 走不了网页端 /api/editor 的取数链」:Tauri asset 协议 + CSP connect-src 未放行平台域名,且 dev 的 vite /api 代理会造成「能跑」假象
- pitfalls.md 追加「ImageCanvasStageView 是硬组合容器」:132 props(57 数据 + 75 回调)、无条件渲染 8 个子视图、83/132 需宿主伪造编辑器状态,且不透传 supportedActions
- pitfalls.md 追加「AGC 资源画布真正自制的交互面」:resourceBookViewport.ts、resourceCanvasLayoutModel.ts 的 fit/normalize、index.tsx 3656-4090 指针段,并记录两条平移路径守卫不一致与右键菜单、chrome 样式缺口
- pitfalls.md 追加「资源画布工具条改造/角色动画按钮点了没反应」:空回调仍留在 supportedActions 里,按钮渲染但无人接
This commit is contained in:
2026-09-10 16:50:02 +08:00
parent 56c6e0fecc
commit 9ce31f905e
@@ -5068,3 +5068,41 @@
- 运行时结论(已核实,可放心):`hostBridge.ts` 的模块作用域只有常量与缓存声明,**没有导入即执行的副作用**;`window.wx` 只在函数体内访问,`ReactNativeWebView``typeof window !== 'undefined'` 守卫。所以这条链被编进 AGC bundle 是惰性的。
- 通用规则:**今后任何往 AGC 引 `src/components/image-editor/` 组件的改动,都要先检查这条链**(组件 → `ImageCanvasGenerationModel``apiClient``hostBridge`),并确认新增的跨端全局在 AGC 侧有声明。
- 关联:`apps/ai-game-creator-shell/src/vite-env.d.ts``src/vite-env.d.ts``src/components/image-editor/ImageCanvasGenerationModel.ts``src/services/apiClient.ts``src/services/host-bridge/hostBridge.ts`
## AGC 走不了网页端 `/api/editor/...` 的取数链(Tauri asset 协议 + CSP 双约束)(2026-09-10
- 现象:把网页端图片画布的服务模块(`src/services/image-editor/editorProjectClient.ts`)引到 AGC 后,`loadEditorProject` / `loadOrCreateRecentEditorProject` 一类调用在打包态(真机 Tauri 客户端)拉不到任何数据。
- 原因(两条独立约束,缺一不可绕过):
1. **相对路径**:该模块全部经 `src/services/apiClient.ts``requestJson` / `fetchWithApiAuth` 发请求,最终落到**裸 `window.fetch('/api/...')`**(相对路径 + `credentials: 'same-origin'`)。AGC 打包态页面来自 Tauri asset 协议(`frontendDist: ../dist`),相对 `/api/...` 打到 asset 协议自身,永远到不了 api-server。
2. **CSP**`apps/ai-game-creator-shell/src-tauri/tauri.conf.json``connect-src` 只放行 `'self'``https://agc-dev.oss-rg-china-mainland.aliyuncs.com``http://127.0.0.1:*``ws://127.0.0.1:*``devCsp` 同口径)。`https://dev.genarrative.world``https://www.genarrative.world` **都不在名单里**——即便改成绝对地址也会被 WebView 拒掉。
- 处理(结论):AGC 访问平台接口**只能**经 `apps/ai-game-creator-shell/src/services/clientHttp.ts::fetchClientHttp`——它把相对路径解析成当前选定服务器的绝对地址,并改走 `@tauri-apps/plugin-http`Rust 侧发请求,不受 WebView CSP 约束)。因此平台接口在 AGC 侧都要在 `src/services/clientApi.ts` / `src/services/clientAuth.ts` 里包一层宿主实现,例如 `loadEditorAssetLibrary()` 就是同一个 `/api/editor/assets/library` 端点另写的一份。
- **判据陷阱**`npm run dev` 下 AGC 的 vite 配了 `/api` 代理(`apps/ai-game-creator-shell/vite.config.ts``server.proxy`),那些裸 `fetch('/api/...')` 在 dev 里**看起来能跑**。判断一个网页端模块能不能被 AGC 复用,必须按打包态(asset 协议 + CSP)推演,不能拿 dev 的现象当证据。
- 关联:`src/services/image-editor/editorProjectClient.ts``src/services/apiClient.ts``apps/ai-game-creator-shell/src/services/clientHttp.ts``apps/ai-game-creator-shell/src-tauri/tauri.conf.json``apps/ai-game-creator-shell/vite.config.ts`
## `ImageCanvasStageView` 是硬组合容器,宿主取不到"只要画布"的那部分(2026-09-10
- 事实:`src/components/image-editor/ImageCanvasStageView.tsx`**132 个 props**(57 个数据/状态 + 75 个回调,可选仅 10 个),并**无条件渲染 8 个子视图**:`ImageCanvasWorldView``ImageCanvasSelectedLayerToolbarView``ImageCanvasUiAssetExtractionOverlayView`×2、`ImageCanvasContextMenusView``ImageCanvasPanelDockView``ImageCanvasTaskSidebarView``ImageCanvasBottomToolbarView`(另有开关控制的 `EditorAgentConversationPanelView`)。唯一消费者是 `ImageCanvasEditorShellView`
- 按 props 取值来源分桶:**(a) 纯本地画布状态 49 / (b) 需编辑器状态机 65 / (c) 需后端鉴权 16 / (d) 需宿主文件能力 2**,即 (b)+(c)+(d) = 83(约 63%)需要宿主造出编辑器状态或后端能力才能满足。典型项:`isInteractionPaused`(真身是钱包/充值弹窗组合态)、`editorAgentAssets`(素材库 API)、`taskListRefreshKey` / `isTaskSidebarOpen``listExternalGenerationTasks`)、`isAgentConversationEnabled``loadFrontendRuntimeConfig()`)、`onSwitchTool`OSS 上传)、`onExportContextLayer` / `onDownloadLayer`zip / 下载宿主能力)。
- 又一条事实:`ImageCanvasStageView` **不透传** `supportedActions` / `extraActions`(它在 `:408-437` 直接渲染子工具条,props 列表里没有这两个字段)。所以宿主想让「自己没接通的工具条动作」不渲染,**只能绕过 StageView 直接挂 `ImageCanvasSelectedLayerToolbarView`**——AGC 现在就是这么做的。
- 结论:AGC 侧复用画布能力按**单个现成件**取用(`ImageCanvasSelectedLayerToolbarView` / `ImageCanvasQuickEditPanelView` / `packages/image-canvas-react``useImageCanvasStageController` / `ImageCanvasContextMenusView` / `packages/image-canvas-core` 的纯函数),不要整机挂 StageView。
- 顺带两条死代码,读代码时别被误导:`persistingAssetKindLayerIds` 在编辑器里从未被传值(`isPersistingAssetKind` 恒 false);`ImageCanvasContextMenusView``canvasClipboard` 是必填 prop,但组件内解构为 `_canvasClipboard` 后完全不用。
- 关联:`src/components/image-editor/ImageCanvasStageView.tsx``ImageCanvasContextMenusView.tsx``ImageCanvasEditorShellView.tsx``ImageCanvasEditorView.tsx`
## AGC 资源画布真正自制的交互面(改画布 bug 前先看这里)(2026-09-10
- 已经接成共享件的部分:`view/project-development/index.tsx` 用了 `@genarrative/image-canvas-core``createCanvasMarqueeState` / `getPointerClient` / `selectLayersInsideMarquee` / `resolveSelectedToolbarStyle` / `resolveQuickEditPanelStyle` / `MIN_SCALE` / `MAX_SCALE` / `resolveViewportFromWheel`,以及 `@genarrative/image-canvas-react``CanvasChromeButton` / `SelectionOverlay`;工具条与快速编辑浮层直接复用 `ImageCanvasSelectedLayerToolbarView` / `ImageCanvasQuickEditPanelView`
- **仍然自制的部分**(都有自己的单测,改前先跑对应的 `apps/ai-game-creator-shell/tests/*`):
- `view/project-development/resourceBookViewport.ts``normalizeResourceBookViewport``zoomResourceBookViewport``panResourceBookViewport``keepResourceBookViewportAtReadableScale``ensureResourceBookContentVisible``resolveResourceBookWheelViewport`(最后一个只是给共享 `resolveViewportFromWheel` 加了一层 normalize)。
- `view/project-development/resourceCanvasLayoutModel.ts``normalizeInfiniteResourceCanvasViewport`(与 `resourceBookViewport.ts``normalizeResourceBookViewport` **是同一套语义的两份实现**)与 `fitResourceCanvasViewportToContent`:后者的 `maxScale` 默认放到 1.5**不**像 core 的 `fitViewportToBounds` 那样封顶 1:1——这是刻意差异,别照着 core 去"对齐"。
- `index.tsx` 的指针/滚轮/缩放段(约 `:3656-4090`):`resetResourceCanvasViewport``fitResourceBook``zoomResourceBookBy``handleResourceBookMainPointerMove``handleResourceCanvasPointerDown` / `PointerMove``stopResourceCanvasPan`
- 一处**行为不一致**(改平移相关 bug 时优先怀疑这里):主画布平移走 `panResourceBookViewport`(内部会 normalize),而分栏画布平移在 `index.tsx:4079-4087` 用**内联算式**直接算 `startViewport + delta`,不过 normalize。两条平移路径的守卫不一致。
- **右键菜单**AGC 全仓没有任何画布右键菜单(`ContextMenu` 只出现在 UI 编辑器里)。接共享 `ImageCanvasContextMenusView` 会撞两件事:① 该组件**没有** `supportedActions` 一类的 opt-in(门控只有 `canCopyContextLayers` / `isCanvasClipboardCopyBlocked` / `contextShouldShowLayer` / `contextShouldUnlockLayer` / `contextMenuLayer.mediaType === 'image-sequence'` / `imageContextMenuLayer.assetKind === 'character'`),宿主无法声明"哪些动作我真实支持",要新增能力只能按可选 prop opt-in 的既定口径给共享组件加;② `.image-canvas-editor__context-menu*` / `__context-submenu*` 这些类 AGC **一条都没有**
- **样式缺口量化**AGC 的宿主 chrome 样式集中在 `apps/ai-game-creator-shell/src/features/resource-canvas/resourceCanvasChrome.css`395 行,`main.tsx:3` 引入),目前只覆盖 **17 个** `image-canvas-editor__*` 类;而网页端 `src/index.css`15542 行)里这套类有 **451 个、910 处**。样式靠手抄是"样式不齐 / 工具条折行"反复复发的根源,补样式时只补实际用到的那几个类。
- 关联:`apps/ai-game-creator-shell/src/view/project-development/resourceBookViewport.ts``resourceCanvasLayoutModel.ts``index.tsx``apps/ai-game-creator-shell/src/features/resource-canvas/resourceCanvasChrome.css``apps/ai-game-creator-shell/tests/resourceBookViewport.test.ts``apps/ai-game-creator-shell/tests/resourceCanvasLayoutModel.test.ts`
## 资源画布工具条的「改造」「角色动画」按钮点了没反应(2026-09-10)
- 现象:选中一张可用资源卡后,浮出工具条上出现「改造」按钮(音频卡同样)或「角色动画」按钮,点击没有任何反应——属于"点了没反应的按钮"这一类。
- 原因:AGC 在 `view/project-development/index.tsx:5405-5417``ImageCanvasSelectedLayerToolbarView` 时把 `onOpenRedrawPanel``:5406`)与 `onOpenCharacterAnimationPanel``:5416`)传成了**空回调**(同一处另有 6 个空回调:`onOpenCropExpandPanel` / `onRemoveBackground` / `onPerfectPixel` / `onSplitIconSpritesheet` / `onExtractUiDesignAssets` / `onDownloadLayer`),但 `features/resource-canvas/resourceCanvasToolbarModel.ts``resolveResourceCanvasToolbarActions` **仍然把 `redraw` 放进了 supportedActions**:图片/视频/音频只要 `canDeriveFromResource` 就加 `redraw``assetKind === 'character'` 再加 `character-animation`。于是这两个动作**渲染出来了但没人接**;其余动作没进集合,按钮不渲染,所以线上只在这两处暴露。
- 处理方向(尚未修):要么给这两个动作接上真实链路,要么把它们从 `resolveResourceCanvasToolbarActions` 的返回集合里去掉。判据是「渲染出来的动作必须真的能跑通」,而不是「先把按钮显示出来」。
- 关联:`apps/ai-game-creator-shell/src/view/project-development/index.tsx``apps/ai-game-creator-shell/src/features/resource-canvas/resourceCanvasToolbarModel.ts``src/components/image-editor/ImageCanvasSelectedLayerToolbarView.tsx``:87-90` 判定、`:300-311` 图片分支「改造」、`:289-300` 「角色动画」)。