统一快速编辑标签覆盖语义

后端按目标图层有效素材类型执行快速编辑门禁
补充 icon 默认资源覆盖 scene 和 spec 的前后端回归
同步图片画布与后端数据契约说明
This commit is contained in:
2026-08-08 15:39:49 +08:00
parent cd78077e95
commit 7927309cef
5 changed files with 68 additions and 4 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -4285,14 +4285,13 @@ async fn resolve_editor_image_edit_source(
})?;
let source_asset_kind =
normalize_editor_image_edit_resolved_source_kind(source_asset_kind.as_deref());
ensure_editor_image_edit_source_kind_allowed(source_asset_kind.as_deref(), Some("image"))?;
let Some(target_layer_id) = payload
.target_layer_id
.as_deref()
.map(str::trim)
.filter(|value| !value.is_empty())
else {
ensure_editor_image_edit_source_kind_allowed(source_asset_kind.as_deref(), Some("image"))?;
return Ok(EditorImageEditResolvedSource {
reference_kind,
source_reference_id: source_reference_id.to_string(),
@@ -20468,6 +20467,30 @@ mod tests {
"同一对象的来源与目标默认类型冲突时失败关闭"
);
for effective_asset_kind in ["scene", "spec"] {
let icon_override_target = EditorImageEditTargetLayerSource {
bucket: "bucket".to_string(),
object_key: "generated/icon.png".to_string(),
asset_object_id: Some("object-icon".to_string()),
resource_id: "resource-icon".to_string(),
default_asset_kind: Some("icon".to_string()),
effective_asset_kind: Some(effective_asset_kind.to_string()),
media_type: "image".to_string(),
};
assert!(
ensure_editor_image_edit_target_matches_source(
"resource-icon",
"bucket",
"generated/icon.png",
Some("object-icon"),
Some("icon"),
&icon_override_target,
)
.is_ok(),
"同媒体族覆盖 {effective_asset_kind} 必须在默认 icon 类型校验之后生效"
);
}
let mut forbidden_override = target.clone();
forbidden_override.effective_asset_kind = Some("icon".to_string());
assert!(
@@ -439,6 +439,7 @@ describe('ImageCanvasGenerationModel', () => {
isQuickEditSupportedLayer({
...buildSourceLayer(),
assetKind: 'scene',
resourceAssetKind: 'icon',
}),
).toBe(true);
expect(
@@ -943,6 +943,46 @@ describe('useImageCanvasGenerationSubmissionWorkflow', () => {
);
});
it.each(['scene', 'spec'] as const)(
'submits quick edits for icon resources overridden to %s',
async (assetKind) => {
editEditorImageMock.mockResolvedValueOnce(
createGenerated({ prompt: `修改${assetKind}` }),
);
render(
<SubmissionWorkflowHarness
initialLayers={[
createLayer({
resourceAssetKind: 'icon',
assetKind,
}),
]}
initialQuickEditPanel={{
mode: 'quick-edit',
sourceLayerId: 'layer-source',
prompt: `修改${assetKind}`,
size: '1024x1024',
aspectRatio: '1:1',
imageSize: '1K',
model: 'gpt-image-2',
status: 'idle',
}}
/>,
);
fireEvent.click(screen.getByRole('button', { name: '提交快速编辑' }));
await waitFor(() => {
expect(editEditorImageMock).toHaveBeenCalledWith(
expect.objectContaining({
sourceReferenceId: 'resource-source',
targetLayerId: 'layer-source',
}),
);
});
},
);
it('rejects unregistered quick-edit sources before uploading marked references', async () => {
render(
<SubmissionWorkflowHarness