移除视频素材的快速编辑支持
清理相关逻辑 清理测试用例。
This commit is contained in:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user