修复上传素材切换侧栏
上传到画布后保持当前左侧侧栏状态。 补充上传工作流和画布 drop 回归断言。 更新跟踪记录并完成浏览器回归。
This commit is contained in:
@@ -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 前端拆分第二十五阶段:新增 `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 前端拆分第二十六阶段:新增 `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 前端拆分第二十七阶段:新增 `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。
|
||||||
|
|||||||
@@ -1608,9 +1608,17 @@ describe('ImageCanvasEditorView', () => {
|
|||||||
imageSrc: expect.stringMatching(/^data:image\/png;base64,/u),
|
imageSrc: expect.stringMatching(/^data:image\/png;base64,/u),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
expect(screen.getByRole('heading', { name: '素材' })).toBeTruthy();
|
||||||
expect(
|
expect(
|
||||||
screen.getByRole('button', { name: '选择图层测试上传.png' }),
|
screen.getByRole('button', { name: '打开素材' }).getAttribute(
|
||||||
).toBeTruthy();
|
'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 () => {
|
it('drops files into the asset panel only once without creating canvas layers', async () => {
|
||||||
|
|||||||
@@ -346,7 +346,6 @@ export function ImageCanvasEditorView() {
|
|||||||
setAssets,
|
setAssets,
|
||||||
setLayers,
|
setLayers,
|
||||||
setGenerateDialog,
|
setGenerateDialog,
|
||||||
setActiveSidebarPanel,
|
|
||||||
appendCanvasLayersWithResources,
|
appendCanvasLayersWithResources,
|
||||||
selectSingleLayer,
|
selectSingleLayer,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -67,8 +67,7 @@ function UploadWorkflowHarness({
|
|||||||
const [layers, setLayers] = useState<CanvasLayer[]>([]);
|
const [layers, setLayers] = useState<CanvasLayer[]>([]);
|
||||||
const [generateDialog, setGenerateDialog] =
|
const [generateDialog, setGenerateDialog] =
|
||||||
useState<GenerateDialogState | null>(null);
|
useState<GenerateDialogState | null>(null);
|
||||||
const [activeSidebarPanel, setActiveSidebarPanel] =
|
const [activeSidebarPanel] = useState<SidebarPanel | null>('assets');
|
||||||
useState<SidebarPanel | null>('assets');
|
|
||||||
const [selectedLayerId, setSelectedLayerId] = useState<string | null>(null);
|
const [selectedLayerId, setSelectedLayerId] = useState<string | null>(null);
|
||||||
const uploadIndexRef = useRef(0);
|
const uploadIndexRef = useRef(0);
|
||||||
|
|
||||||
@@ -88,7 +87,6 @@ function UploadWorkflowHarness({
|
|||||||
setAssets,
|
setAssets,
|
||||||
setLayers,
|
setLayers,
|
||||||
setGenerateDialog,
|
setGenerateDialog,
|
||||||
setActiveSidebarPanel,
|
|
||||||
appendCanvasLayersWithResources: (nextLayers) => {
|
appendCanvasLayersWithResources: (nextLayers) => {
|
||||||
setLayers((currentLayers) => [...currentLayers, ...nextLayers]);
|
setLayers((currentLayers) => [...currentLayers, ...nextLayers]);
|
||||||
},
|
},
|
||||||
@@ -290,7 +288,7 @@ describe('useImageCanvasUploadWorkflow', () => {
|
|||||||
expect(clickUploadInput).toHaveBeenCalledTimes(1);
|
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<{
|
const deferredAsset = createDeferred<{
|
||||||
assetId: string;
|
assetId: string;
|
||||||
folderId: string;
|
folderId: string;
|
||||||
@@ -315,7 +313,7 @@ describe('useImageCanvasUploadWorkflow', () => {
|
|||||||
'layer-upload-1:画布素材.png:upload-1:-160:-107.5',
|
'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(
|
expect(screen.getByTestId('selected-layer').textContent).toBe(
|
||||||
'layer-upload-1',
|
'layer-upload-1',
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import type {
|
|||||||
EditorAsset,
|
EditorAsset,
|
||||||
EditorAssetFolder,
|
EditorAssetFolder,
|
||||||
GenerateDialogState,
|
GenerateDialogState,
|
||||||
SidebarPanel,
|
|
||||||
UploadTarget,
|
UploadTarget,
|
||||||
} from './ImageCanvasEditorTypes';
|
} from './ImageCanvasEditorTypes';
|
||||||
import { isImageFile, readImageFileAsDataUrl } from './ImageCanvasFileModel';
|
import { isImageFile, readImageFileAsDataUrl } from './ImageCanvasFileModel';
|
||||||
@@ -45,7 +44,6 @@ type UseImageCanvasUploadWorkflowOptions = {
|
|||||||
setAssets: Dispatch<SetStateAction<EditorAsset[]>>;
|
setAssets: Dispatch<SetStateAction<EditorAsset[]>>;
|
||||||
setLayers: Dispatch<SetStateAction<CanvasLayer[]>>;
|
setLayers: Dispatch<SetStateAction<CanvasLayer[]>>;
|
||||||
setGenerateDialog: Dispatch<SetStateAction<GenerateDialogState | null>>;
|
setGenerateDialog: Dispatch<SetStateAction<GenerateDialogState | null>>;
|
||||||
setActiveSidebarPanel: Dispatch<SetStateAction<SidebarPanel | null>>;
|
|
||||||
appendCanvasLayersWithResources: (nextLayers: CanvasLayer[]) => void;
|
appendCanvasLayersWithResources: (nextLayers: CanvasLayer[]) => void;
|
||||||
selectSingleLayer: (layerId: string | null) => void;
|
selectSingleLayer: (layerId: string | null) => void;
|
||||||
};
|
};
|
||||||
@@ -78,7 +76,6 @@ export function useImageCanvasUploadWorkflow({
|
|||||||
setAssets,
|
setAssets,
|
||||||
setLayers,
|
setLayers,
|
||||||
setGenerateDialog,
|
setGenerateDialog,
|
||||||
setActiveSidebarPanel,
|
|
||||||
appendCanvasLayersWithResources,
|
appendCanvasLayersWithResources,
|
||||||
selectSingleLayer,
|
selectSingleLayer,
|
||||||
}: UseImageCanvasUploadWorkflowOptions) {
|
}: UseImageCanvasUploadWorkflowOptions) {
|
||||||
@@ -316,7 +313,6 @@ export function useImageCanvasUploadWorkflow({
|
|||||||
if (options.addToCanvas) {
|
if (options.addToCanvas) {
|
||||||
appendCanvasLayersWithResources([nextLayer]);
|
appendCanvasLayersWithResources([nextLayer]);
|
||||||
selectSingleLayer(nextLayer.id);
|
selectSingleLayer(nextLayer.id);
|
||||||
setActiveSidebarPanel('layers');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setAssets((currentAssets) =>
|
setAssets((currentAssets) =>
|
||||||
@@ -445,7 +441,6 @@ export function useImageCanvasUploadWorkflow({
|
|||||||
canvasSize.width,
|
canvasSize.width,
|
||||||
openEditorLoginModal,
|
openEditorLoginModal,
|
||||||
selectSingleLayer,
|
selectSingleLayer,
|
||||||
setActiveSidebarPanel,
|
|
||||||
setAssetFolders,
|
setAssetFolders,
|
||||||
setAssets,
|
setAssets,
|
||||||
setLayers,
|
setLayers,
|
||||||
|
|||||||
Reference in New Issue
Block a user