移除视频素材的快速编辑支持
Project CI / Repository checks (pull_request) Failing after 43s
Project CI / Backend tests (pull_request) Successful in 3m48s
Project CI / Frontend tests (pull_request) Successful in 4m0s
Project CI / Native shell tests (pull_request) Successful in 16m16s

清理相关逻辑
清理测试用例。
This commit is contained in:
2026-08-10 12:00:10 +08:00
parent 8abeb333bb
commit 8f2b889160
15 changed files with 374 additions and 392 deletions
@@ -371,6 +371,11 @@ describe('ImageCanvasContextMenusView', () => {
mediaType: 'audio' as const,
assetKind: 'background-music' as const,
},
{
title: '视频',
mediaType: 'video' as const,
assetKind: 'video' as const,
},
])('hides quick edit from both menus for $title', (overrides) => {
const layer = createLayer(overrides);
renderContextMenus({
@@ -964,9 +964,16 @@ describe('ImageCanvasGenerationModel', () => {
isQuickEditSupportedLayer({
...buildSourceLayer(),
mediaType: 'video',
assetKind: null,
}),
).toBe(false);
expect(
isQuickEditSupportedLayer({
...buildSourceLayer(),
mediaType: 'image',
assetKind: 'video',
}),
).toBe(true);
).toBe(false);
});
it('adds reference image semantics and snapshots for spec generation references', () => {
@@ -712,7 +712,6 @@ export function buildQuickEditModelOptions(currentModel: string) {
const QUICK_EDIT_SUPPORTED_MEDIA_TYPES = new Set<CanvasLayer['mediaType']>([
undefined,
'image',
'video',
]);
const QUICK_EDIT_SUPPORTED_ASSET_KINDS = new Set<CanvasLayer['assetKind']>([
@@ -725,7 +724,6 @@ const QUICK_EDIT_SUPPORTED_ASSET_KINDS = new Set<CanvasLayer['assetKind']>([
'publication-material',
'ui-design',
'scene',
'video',
]);
export function isQuickEditSupportedLayer(layer: CanvasLayer) {
@@ -13,23 +13,6 @@ import type {
} from './ImageCanvasEditorTypes';
import { ImageCanvasQuickEditPanelView } from './ImageCanvasQuickEditPanelView';
function createLayer(): CanvasLayer {
return {
id: 'layer-a',
resourceId: 'resource-a',
title: '源图',
src: 'data:image/png;base64,c291cmNl',
x: 0,
y: 0,
width: 320,
height: 180,
originalWidth: 320,
originalHeight: 180,
zIndex: 1,
sourceType: 'uploaded',
};
}
function QuickEditPanelHarness({
initialPanel,
onSubmit = vi.fn(),
@@ -45,12 +28,27 @@ function QuickEditPanelHarness({
const [isReferenceMenuOpen, setIsReferenceMenuOpen] = useState(false);
const [isPickingReference, setIsPickingReference] = useState(false);
const referenceButtonRef = useRef<HTMLButtonElement | null>(null);
const sourceLayer: CanvasLayer = {
id: 'layer-a',
resourceId: 'resource-a',
title: '源图片',
src: '/source.png',
mediaType: 'image',
originalWidth: 1024,
originalHeight: 1024,
width: 512,
height: 512,
x: 0,
y: 0,
zIndex: 1,
sourceType: 'uploaded',
};
return panel ? (
<div>
<ImageCanvasQuickEditPanelView
panel={panel}
sourceLayer={createLayer()}
sourceLayer={sourceLayer}
style={{ left: 10, top: 20 }}
referenceButtonRef={referenceButtonRef}
isReferenceMenuOpen={isReferenceMenuOpen}
@@ -51,8 +51,9 @@ function resetFailedPanelStatus<
function createQuickEditDialog(
panel: QuickEditPanelState,
): GenerateDialogState {
const isQuickEdit = (panel.mode ?? 'quick-edit') === 'quick-edit';
return {
mode: 'quick-edit',
mode: isQuickEdit ? 'quick-edit' : 'generate',
prompt: panel.prompt,
status: panel.status,
errorMessage: panel.errorMessage,
@@ -106,11 +107,16 @@ export function ImageCanvasQuickEditPanelView({
hasPendingImageReferenceUploads = false,
onSubmit,
}: ImageCanvasQuickEditPanelViewProps) {
const isImageQuickEdit = sourceLayer.mediaType !== 'video';
const isRedraw = panel.mode === 'redraw';
const referenceCount = panel.quickEditReferences?.length ?? 0;
const canAddReferences =
!isRedraw &&
referenceCount <
resolveExtraImageReferenceLimit(panel.model, QUICK_EDIT_REFERENCE_LIMIT, 1);
resolveExtraImageReferenceLimit(
panel.model,
QUICK_EDIT_REFERENCE_LIMIT,
1,
);
const quickEditDialog = createQuickEditDialog(panel);
return (
@@ -147,19 +153,21 @@ export function ImageCanvasQuickEditPanelView({
onRememberImageModel={onRememberImageModel}
hasPendingImageReferenceUploads={hasPendingImageReferenceUploads}
onSubmit={onSubmit}
dialogLabel="快速编辑图片"
includeDimensions={isImageQuickEdit}
dialogLabel={isRedraw ? '重绘图片' : '快速编辑图片'}
includeDimensions={true}
includeModel={true}
includeReferences={false}
promptLabel="快速编辑提示词"
promptPlaceholder="你希望素材如何修改?"
optionLabelPrefix="快速编辑"
promptLabel={isRedraw ? '重绘提示词' : '快速编辑提示词'}
promptPlaceholder={
isRedraw ? '把画面重绘成什么样?' : '你希望素材如何修改?'
}
optionLabelPrefix={isRedraw ? '重绘' : '快速编辑'}
dimensionRatioAriaLabelPrefix="比例"
dimensionSizeAriaLabelPrefix="尺寸"
referenceUploadTarget="quick-edit-reference"
referenceButtonLabel="添加快速编辑参考图"
referenceButtonAriaLabel="添加快速编辑参考图"
referenceMenuLabel="快速编辑参考图来源"
referenceButtonLabel={isRedraw ? '' : '添加快速编辑参考图'}
referenceButtonAriaLabel={isRedraw ? undefined : '添加快速编辑参考图'}
referenceMenuLabel={isRedraw ? '' : '快速编辑参考图来源'}
referenceSlotTone="quick-edit"
referenceSlotDisabled={!canAddReferences}
showReferenceAddButton={false}
@@ -171,9 +179,9 @@ export function ImageCanvasQuickEditPanelView({
promptClassName="image-canvas-editor__generation-prompt image-canvas-editor__quick-edit-prompt"
referenceSlotClassName="image-canvas-editor__quick-edit-reference"
submitButtonClassName="image-canvas-editor__generation-submit image-canvas-editor__quick-edit-submit"
submitLabel="修改"
submitAriaLabel="修改"
submittingStatusLabel="修改中"
submitLabel={isRedraw ? '生成' : '修改'}
submitAriaLabel={isRedraw ? '生成' : '修改'}
submittingStatusLabel={isRedraw ? '生成中' : '修改中'}
renderEditorPortal={renderEditorPortal}
buildPortalMenuStyle={buildPortalMenuStyle}
/>
@@ -444,7 +444,7 @@ describe('ImageCanvasSelectedLayerToolbarView', () => {
within(videoToolbar)
.getAllByRole('button')
.map((button) => button.getAttribute('aria-label')),
).toEqual(['快速编辑', '改造', '下载按钮']);
).toEqual(['改造', '下载按钮']);
cleanup();
renderSelectedToolbar({
@@ -337,14 +337,12 @@ function SubmissionWorkflowHarness({
quickEditPanel,
quickEditSourceLayer,
quickEditSelectionState: initialQuickEditSelectionState,
canvasGenerationDialogs: dialogs.canvasGenerationDialogs,
setQuickEditPanel,
characterAnimationPanel,
characterAnimationDialog: activeCanvasDialog,
characterAnimationSourceLayer,
setCharacterAnimationPanel,
setGenerateDialog: dialogs.setGenerateDialog,
openCanvasGenerationDialog: dialogs.openCanvasGenerationDialog,
updateCanvasGenerationDialogById: dialogs.updateCanvasGenerationDialogById,
hasCanvasGenerationDialogById: dialogs.hasCanvasGenerationDialogById,
getCanvasGenerationDialogById: dialogs.getCanvasGenerationDialogById,
@@ -1496,47 +1494,15 @@ describe('useImageCanvasGenerationSubmissionWorkflow', () => {
expect(refreshWalletBalance).toHaveBeenCalledTimes(1);
});
it('submits video quick edits through the video API with the source video', async () => {
const applyProjectSnapshot = vi.fn();
const upsertGeneratedAsset = vi.fn();
generateEditorVideoMock.mockResolvedValueOnce(
createVideoGenerated({
prompt: '把当前图改成雨天',
project: {
projectId: 'editor-project-1',
title: '项目',
coverImageSrc: null,
viewport: { x: 0, y: 0, scale: 1 },
layers: [],
resources: [],
createdAt: '2026-06-27T00:00:00.000Z',
updatedAt: '2026-06-27T00:00:00.000Z',
},
asset: {
assetId: 'asset-video-quick-edit',
folderId: 'user-1:asset-folder:project',
label: '源视频 快速编辑',
imageSrc: 'https://assets.example.test/generated-video.mp4',
thumbnailSrc: 'https://assets.example.test/generated-video-cover.png',
width: 854,
height: 480,
sourceType: 'generated',
assetKind: 'video',
},
}),
);
it('rejects direct video quick-edit submission without calling generation APIs', () => {
render(
<SubmissionWorkflowHarness
projectId="editor-project-1"
applyProjectSnapshot={applyProjectSnapshot}
upsertGeneratedAsset={upsertGeneratedAsset}
initialLayers={[
createLayer({
title: '源视频',
src: 'data:video/mp4;base64,c291cmNlLXZpZGVv',
src: 'https://assets.example.test/source-video.mp4',
mediaType: 'video',
assetKind: 'video',
model: 'gpt-image-2',
originalWidth: 854,
originalHeight: 480,
width: 427,
@@ -1549,66 +1515,19 @@ describe('useImageCanvasGenerationSubmissionWorkflow', () => {
size: '854x480',
model: 'gpt-image-2',
status: 'idle',
quickEditReferences: [
{
id: 'ref-style',
label: '风格参考',
src: 'https://assets.example.test/style.png',
},
],
}}
/>,
);
fireEvent.click(screen.getByRole('button', { name: '提交快速编辑' }));
await waitFor(() => {
expect(generateEditorVideoMock).toHaveBeenCalledWith(
expect.objectContaining({
prompt: '把当前图改成雨天',
model: 'seedance2.0-fast',
aspectRatio: '16:9',
durationSeconds: 4,
resolution: '480p',
mode: 'std',
sound: 'off',
referenceVideoSrcs: [
'generated-character-drafts/editor/generation-references/reference.png',
],
generationInputs: expect.objectContaining({
version: 2,
action: 'video.generate',
references: [
{
id: 'videoReference',
title: '参考视频 1',
label: '源视频',
refType: 'project-resource',
refId: 'resource-source',
},
],
}),
assetFolderId: 'project',
assetLabel: '源视频 快速编辑',
}),
);
});
expect(generateEditorVideoMock.mock.calls[0]?.[0]).not.toHaveProperty(
'referenceImageSrcs',
);
expect(editEditorImageMock).not.toHaveBeenCalled();
expect(generateEditorImageMock).not.toHaveBeenCalled();
expect(generateEditorCharacterAnimationMock).not.toHaveBeenCalled();
await waitFor(() => {
expect(applyProjectSnapshot).toHaveBeenCalledWith(
expect.objectContaining({ projectId: 'editor-project-1' }),
);
expect(upsertGeneratedAsset).toHaveBeenCalledWith(
expect.objectContaining({
assetId: 'asset-video-quick-edit',
thumbnailSrc: 'https://assets.example.test/generated-video-cover.png',
}),
);
});
expect(generateEditorVideoMock).not.toHaveBeenCalled();
expect(screen.getByTestId('quick-edit').textContent).toContain(
'failed:把当前图改成雨天:当前素材类型不支持快速编辑',
);
});
it('uploads inline video references and keeps image objectKey and asset URLs', async () => {
File diff suppressed because it is too large Load Diff
@@ -1493,6 +1493,28 @@ describe('useImageCanvasGenerationWorkflow', () => {
);
});
it('does not open quick edit programmatically for video layers', () => {
render(
<GenerationWorkflowHarness
initialLayers={[
createLayer({
title: '源视频',
src: '/generated/video.mp4',
mediaType: 'video',
assetKind: 'video',
}),
]}
/>,
);
fireEvent.click(screen.getByRole('button', { name: '打开快速编辑' }));
expect(screen.getByTestId('tool').textContent).toBe('select');
expect(screen.getByTestId('selected').textContent).toBe('-');
expect(screen.getByTestId('quick-edit').textContent).toBe('-');
expect(screen.getByTestId('quick-edit-selection').textContent).toBe('-');
});
it('calculates character animation button price from the model pricing config', () => {
render(<GenerationWorkflowHarness />);
@@ -3829,14 +3829,12 @@ export function useImageCanvasGenerationWorkflow({
quickEditPanel,
quickEditSourceLayer,
quickEditSelectionState,
canvasGenerationDialogs,
setQuickEditPanel,
characterAnimationPanel: effectiveCharacterAnimationPanel,
characterAnimationDialog,
characterAnimationSourceLayer,
setCharacterAnimationPanel,
setGenerateDialog,
openCanvasGenerationDialog,
updateCanvasGenerationDialogById,
hasCanvasGenerationDialogById,
getCanvasGenerationDialogById,