diff --git a/TRACKING.md b/TRACKING.md index 459a9aa6b..dcd44e2f8 100644 --- a/TRACKING.md +++ b/TRACKING.md @@ -143,3 +143,4 @@ - 2026-06-17 前端拆分第二十五阶段:新增 `ImageCanvasStageControllerModel` 和 `useImageCanvasStageController`,把舞台派生状态、生成 / 选中浮层位置、右键菜单目标、空白画布右键、图层右键和清空画布焦点从主视图抽出;主视图继续保留工具切换、上传 / 生成入口、图层命令、项目持久化和舞台 pointer 状态机。新增模型和 hook 单测覆盖选中 / 生成锚定、右键菜单位置、显示 / 解锁判断、清空焦点和空白 / 图层右键菜单;主视图从 1086 行降至 993 行。验证命令:`npm run test -- src/components/image-editor/ImageCanvasStageControllerModel.test.ts src/components/image-editor/useImageCanvasStageController.test.tsx`、`npm run test -- src/components/image-editor/ImageCanvasEditorView.test.tsx src/components/image-editor/useImageCanvasEditorChrome.test.tsx src/components/image-editor/useImageCanvasUploadWorkflow.test.tsx src/routing/appPageRoutes.test.ts`、`npm run typecheck`;浏览器回归:`http://127.0.0.1:10007/editor/canvas` 清空会话后未登录直接显示 `账号入口`,关闭登录后 `画布背景色` 打开完整 `画布背景设置` dialog,包含色相、自定义颜色、预设、HEX 和恢复默认;点击 `暖灰` 后 viewport 背景为 `rgb(243, 240, 234)` 且 `background-image: none`,`AI画布工具栏` 保持可见,截图留存于 `output/playwright/editor-stage-controller-smoke-20260617.png`。 - 2026-06-17 前端拆分第二十六阶段:新增 `ImageCanvasTopbarView`,把返回项目入口、项目标题展示 / 重命名表单、下载画布素材按钮和导出状态提示从主视图抽出;主视图继续保留 chrome hook、项目持久化、导出工作流和实际导出副作用。新增组件单测覆盖返回入口、标题编辑入口、重命名提交 / 取消、导出按钮禁用 / 启用和导出状态提示;主视图从 993 行降至 905 行。验证命令:`npm run test -- src/components/image-editor/ImageCanvasTopbarView.test.tsx src/components/image-editor/ImageCanvasEditorView.test.tsx src/components/image-editor/useImageCanvasAssetExportWorkflow.test.tsx src/components/image-editor/useImageCanvasEditorChrome.test.tsx`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`;浏览器回归:`http://127.0.0.1:10007/editor/canvas` 未登录直接显示 `账号入口`,顶栏返回项目入口、项目名、`画布` 标签和下载按钮均可见;关闭登录后打开 `画布背景设置`,点击 `暖灰` 后 viewport 背景为 `rgb(243, 240, 234)` 且 `background-image: none`,`AI画布工具栏` 保持可见,截图留存于 `output/playwright/editor-topbar-smoke-20260617.png`。 - 2026-06-17 前端拆分第二十七阶段:新增 `useImageCanvasGenerationSurface`,把生成 Composer JSX、生成工具切换分流、普通生图 / 图标生成 / 快速编辑 / 角色动画浮层定位从主视图抽出;`useImageCanvasGenerationWorkflow` 继续负责生成状态机和真实 API 提交。同步移除 `ImageCanvasStageControllerModel` 中重复的生成锚点 / Composer 位置派生,避免舞台控制器和生成表面重复持有生成浮层职责;主视图从 905 行降至 793 行。rebase 到远端 `支持规范参考图输入` 后,生成表面继续透传角色形象规范和常规参考图入口,并把左下 dock / 底部工具栏层级提到 Composer 之上,避免生成输入框盖住常用工具和背景面板。验证命令:`npm run test -- src/components/image-editor/useImageCanvasGenerationSurface.test.tsx src/components/image-editor/ImageCanvasStageControllerModel.test.ts src/components/image-editor/useImageCanvasStageController.test.tsx src/components/image-editor/useImageCanvasGenerationWorkflow.test.tsx src/components/image-editor/ImageCanvasEditorView.test.tsx`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`;浏览器回归:`http://127.0.0.1:10007/editor/canvas` 未登录直接显示 `账号入口`,关闭登录后 `画布背景设置` 保持完整面板,点击 `暖灰` 后 viewport 背景为 `rgb(243, 240, 234)` 且 `background-image: none`;点击 `生成工具` 后 `Image Generator`、`生成图片` 对话框和 `AI画布工具栏` 均可见,再点击 `生成角色形象` 能打开包含 `角色形象规范` 和 `常规参考图` 的对话框,控制台仅有未登录 refresh 401。 +- 2026-06-17 上传侧栏回归修正:上传工作流移除上传到画布后强制切换 `图层` 侧栏的副作用,保留新增素材卡、创建画布图层和选中新图层。验证命令:`npm run test -- src/components/image-editor/useImageCanvasUploadWorkflow.test.tsx src/components/image-editor/ImageCanvasEditorView.test.tsx`、`npm run typecheck`、`npm run check:encoding`、`git diff --check`;浏览器回归:`http://127.0.0.1:10007/editor/canvas` 登录后点击 `上传到项目素材` 上传图片,左侧仍显示 `素材` 且 `打开素材` 为 pressed;把图片文件 drop 到 `画布工作区` 后素材库和画布图层均出现 `canvas-drop-sidebar-smoke.png`,新图层被选中,`打开素材=true`、`打开图层=false`,`AI画布工具栏` 保持可见,登录后控制台无 error。 diff --git a/src/components/image-editor/ImageCanvasEditorView.test.tsx b/src/components/image-editor/ImageCanvasEditorView.test.tsx index 82e98f073..3ce1795b9 100644 --- a/src/components/image-editor/ImageCanvasEditorView.test.tsx +++ b/src/components/image-editor/ImageCanvasEditorView.test.tsx @@ -1608,9 +1608,17 @@ describe('ImageCanvasEditorView', () => { imageSrc: expect.stringMatching(/^data:image\/png;base64,/u), }), ); + expect(screen.getByRole('heading', { name: '素材' })).toBeTruthy(); expect( - screen.getByRole('button', { name: '选择图层测试上传.png' }), - ).toBeTruthy(); + screen.getByRole('button', { name: '打开素材' }).getAttribute( + 'aria-pressed', + ), + ).toBe('true'); + expect( + screen + .getByRole('button', { name: '选择测试上传.png' }) + .className.includes('image-canvas-editor__layer--selected'), + ).toBe(true); }); it('drops files into the asset panel only once without creating canvas layers', async () => { diff --git a/src/components/image-editor/ImageCanvasEditorView.tsx b/src/components/image-editor/ImageCanvasEditorView.tsx index 3a7e99ec6..a6fed6261 100644 --- a/src/components/image-editor/ImageCanvasEditorView.tsx +++ b/src/components/image-editor/ImageCanvasEditorView.tsx @@ -346,7 +346,6 @@ export function ImageCanvasEditorView() { setAssets, setLayers, setGenerateDialog, - setActiveSidebarPanel, appendCanvasLayersWithResources, selectSingleLayer, }); diff --git a/src/components/image-editor/useImageCanvasUploadWorkflow.test.tsx b/src/components/image-editor/useImageCanvasUploadWorkflow.test.tsx index 5a6dbc729..3696420e0 100644 --- a/src/components/image-editor/useImageCanvasUploadWorkflow.test.tsx +++ b/src/components/image-editor/useImageCanvasUploadWorkflow.test.tsx @@ -67,8 +67,7 @@ function UploadWorkflowHarness({ const [layers, setLayers] = useState([]); const [generateDialog, setGenerateDialog] = useState(null); - const [activeSidebarPanel, setActiveSidebarPanel] = - useState('assets'); + const [activeSidebarPanel] = useState('assets'); const [selectedLayerId, setSelectedLayerId] = useState(null); const uploadIndexRef = useRef(0); @@ -88,7 +87,6 @@ function UploadWorkflowHarness({ setAssets, setLayers, setGenerateDialog, - setActiveSidebarPanel, appendCanvasLayersWithResources: (nextLayers) => { setLayers((currentLayers) => [...currentLayers, ...nextLayers]); }, @@ -290,7 +288,7 @@ describe('useImageCanvasUploadWorkflow', () => { expect(clickUploadInput).toHaveBeenCalledTimes(1); }); - it('creates an uploading asset card, adds a canvas layer, and patches the layer with the persisted asset id', async () => { + it('creates an uploading asset card, adds a canvas layer, keeps the sidebar, and patches the layer with the persisted asset id', async () => { const deferredAsset = createDeferred<{ assetId: string; folderId: string; @@ -315,7 +313,7 @@ describe('useImageCanvasUploadWorkflow', () => { 'layer-upload-1:画布素材.png:upload-1:-160:-107.5', ); }); - expect(screen.getByTestId('sidebar').textContent).toBe('layers'); + expect(screen.getByTestId('sidebar').textContent).toBe('assets'); expect(screen.getByTestId('selected-layer').textContent).toBe( 'layer-upload-1', ); diff --git a/src/components/image-editor/useImageCanvasUploadWorkflow.ts b/src/components/image-editor/useImageCanvasUploadWorkflow.ts index c6cfec1a6..882752e72 100644 --- a/src/components/image-editor/useImageCanvasUploadWorkflow.ts +++ b/src/components/image-editor/useImageCanvasUploadWorkflow.ts @@ -17,7 +17,6 @@ import type { EditorAsset, EditorAssetFolder, GenerateDialogState, - SidebarPanel, UploadTarget, } from './ImageCanvasEditorTypes'; import { isImageFile, readImageFileAsDataUrl } from './ImageCanvasFileModel'; @@ -45,7 +44,6 @@ type UseImageCanvasUploadWorkflowOptions = { setAssets: Dispatch>; setLayers: Dispatch>; setGenerateDialog: Dispatch>; - setActiveSidebarPanel: Dispatch>; appendCanvasLayersWithResources: (nextLayers: CanvasLayer[]) => void; selectSingleLayer: (layerId: string | null) => void; }; @@ -78,7 +76,6 @@ export function useImageCanvasUploadWorkflow({ setAssets, setLayers, setGenerateDialog, - setActiveSidebarPanel, appendCanvasLayersWithResources, selectSingleLayer, }: UseImageCanvasUploadWorkflowOptions) { @@ -316,7 +313,6 @@ export function useImageCanvasUploadWorkflow({ if (options.addToCanvas) { appendCanvasLayersWithResources([nextLayer]); selectSingleLayer(nextLayer.id); - setActiveSidebarPanel('layers'); } setAssets((currentAssets) => @@ -445,7 +441,6 @@ export function useImageCanvasUploadWorkflow({ canvasSize.width, openEditorLoginModal, selectSingleLayer, - setActiveSidebarPanel, setAssetFolders, setAssets, setLayers,