修复上传素材切换侧栏

上传到画布后保持当前左侧侧栏状态。

补充上传工作流和画布 drop 回归断言。

更新跟踪记录并完成浏览器回归。
This commit is contained in:
2026-06-17 14:59:02 +08:00
parent 84818f9bd5
commit 7a77ab4df7
5 changed files with 14 additions and 13 deletions

View File

@@ -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。

View File

@@ -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 () => {

View File

@@ -346,7 +346,6 @@ export function ImageCanvasEditorView() {
setAssets,
setLayers,
setGenerateDialog,
setActiveSidebarPanel,
appendCanvasLayersWithResources,
selectSingleLayer,
});

View File

@@ -67,8 +67,7 @@ function UploadWorkflowHarness({
const [layers, setLayers] = useState<CanvasLayer[]>([]);
const [generateDialog, setGenerateDialog] =
useState<GenerateDialogState | null>(null);
const [activeSidebarPanel, setActiveSidebarPanel] =
useState<SidebarPanel | null>('assets');
const [activeSidebarPanel] = useState<SidebarPanel | null>('assets');
const [selectedLayerId, setSelectedLayerId] = useState<string | null>(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',
);

View File

@@ -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<SetStateAction<EditorAsset[]>>;
setLayers: Dispatch<SetStateAction<CanvasLayer[]>>;
setGenerateDialog: Dispatch<SetStateAction<GenerateDialogState | null>>;
setActiveSidebarPanel: Dispatch<SetStateAction<SidebarPanel | null>>;
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,