统一画布快速编辑白名单与入口行为 (#140)
Project CI / Repository checks (push) Successful in 56s
Project CI / Frontend tests (push) Successful in 4m1s
Project CI / Backend tests (push) Successful in 4m26s
Project CI / Native shell tests (push) Successful in 15m12s

- 使用白名单控制快速编辑功能的显示

- 同步浮动工具栏、右键菜单、打开入口和提交门禁
- 禁用角色动作序列帧(整体)、音频和单个图标(explain: backend reject)的快速编辑

- 把快速编辑api改为只接受resource Id | asset Id,删除 image_src 参数, 以保证校验

其中包含生产数据迁移。迁移用于清理历史普通图片错误持久化的 assetKind="image",覆盖 asset → project-resource → showcase → canvas 四个作用域,并包含 operator 鉴权、分批 dry-run、批次 SHA-256 绑定 apply、active 结构化画
  > 布迁移摘要同步及最终零残留复核。

---------

Co-authored-by: kdletters <kdletters@qq.com>
Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/140
Co-authored-by: 王德宇 <kvtodev@outlook.com>
Co-committed-by: 王德宇 <kvtodev@outlook.com>
This commit was merged in pull request #140.
This commit is contained in:
2026-08-12 14:46:24 +08:00
committed by 段舒康
parent 4c3c0e9cd4
commit 8abeb333bb
61 changed files with 11979 additions and 688 deletions
@@ -29,7 +29,7 @@ Prefer `scripts/genarrative_external_api.py` for runnable REST calls. It uses on
- Authenticate MCP and business API calls with `Authorization: Bearer <tnr_sk_...>`. Never ask the user to paste a key into chat or place one in repository files.
- All eight generation POST routes require `Idempotency-Key` and return HTTP `202`; `202` is durable acceptance, not a media result.
- Retry an uncertain submission only with the exact same body and the same idempotency key. A polling timeout is not permission to generate again.
- Use stable references such as `objectKey`, project resource ID, or asset ID in generation requests. Use `/assets/read-url` only for temporary preview/download access.
- Use stable references such as `objectKey`, project resource ID, or asset ID where each operation permits them. Image edit/redraw is stricter: `sourceReferenceId` accepts only a registered project resource ID or asset ID; upload confirmation alone is not enough. Use `/assets/read-url` only for temporary preview/download access.
- Preserve both warning channels after completion. A general `warning` can coexist with `sliceWarning`; do not discard either.
- Do not invent missing derivatives. A source-preserved warning means the main source remains usable but requested post-processing failed. A slice warning means the complete transparent sheet is usable but individual slices are absent.
- For successful `style="pixelArt"`, treat completed-result and nested resource/asset dimensions as the final logical-grid PNG dimensions. They may differ from `size`, `imageSize`, the provider image, and `canvasCompletion.placeholder`; do not rescale or reject the artifact to match those inputs.
@@ -50,7 +50,7 @@ Every generation row requires a stable `Idempotency-Key` header and returns HTTP
| Capability | POST path | Required body fields | Common optional body fields |
| --- | --- | --- | --- |
| Image generation | `/api/external/v1/editor/images/generations` | `prompt` | `kind`, `style`, `model`, `aspectRatio`, `imageSize`, `size`, `referenceImageSrcs`, `projectId`, `assetFolderId`, `assetLabel`, `canvasCompletion`, `generationInputs` |
| Image edit/redraw | `/api/external/v1/editor/images/edits` | `prompt`, `sourceImageSrc` | `referenceImageSrcs`, `model`, `size`, `projectId`, `assetFolderId`, `assetLabel`, `sourceResourceId`, `targetLayerId`, `canvasCompletion` |
| Image edit/redraw | `/api/external/v1/editor/images/edits` | `prompt`, `sourceReferenceId` | `referenceImageSrcs`, `model`, `size`, `projectId`, `assetFolderId`, `assetLabel`, `targetLayerId`, `canvasCompletion` |
| Icon spritesheet | `/api/external/v1/editor/icon-spritesheets/generations` | `referenceId`, `iconDescriptions` | `style`, `referenceImageSrcs`, `screenColor`, `model`, `aspectRatio`, `imageSize`, `projectId`, `assetFolderId`, `assetLabel`, `canvasCompletion` |
| UI asset extraction | `/api/external/v1/editor/ui-designs/assets/extractions` | `sourceImageSrc`, `aspectRatio`, `imageSize` | `screenColor`, `model`, `referenceImageSrcs`, `projectId`, `assetFolderId`, `spritesheetLabel`, `canvasCompletion` |
| Character animation | `/api/external/v1/editor/character-animations/generations` | `sourceLayerId`, `sourceImageSrc`, `sourceWidth`, `sourceHeight`, `promptText`, `resolution`, `ratio`, `frameCount`, `durationSeconds`, `model` | `projectId`, `sourceResourceId`, `assetFolderId`, `assetLabel`, `canvasCompletion` |
@@ -82,13 +82,13 @@ After confirming a local upload, pass its stable `objectKey` into operations tha
| Target capability | Field |
| --- | --- |
| Image generation | `referenceImageSrcs` |
| Image edit/redraw | `sourceImageSrc`; additional references in `referenceImageSrcs` |
| Image edit/redraw | `sourceReferenceId` must be a registered project resource ID or asset ID; additional references remain in `referenceImageSrcs` |
| Icon spritesheet | Register the primary spec as an `assetKind="icon-spec"` project resource or asset, then pass its returned ID as `referenceId`; additional style references remain in `referenceImageSrcs` |
| UI design extraction | `sourceImageSrc`; additional references in `referenceImageSrcs` |
| Character animation | `sourceImageSrc` |
| Video with image references | `referenceImageSrcs` |
Use video/audio reference arrays only with models that support them. Do not pass an expiring signed read URL as a generation reference.
For image edit/redraw, confirming an upload is not sufficient: create a project resource or asset-library record first, then pass that record's ID as `sourceReferenceId`. The main source never accepts objectKey, URL, Data URL, or Blob URL. Use video/audio reference arrays only with models that support them. Do not pass an expiring signed read URL as a generation reference.
The icon-spritesheet primary `referenceId` is intentionally stricter than ordinary image references: it accepts only a current-owner project resource ID or asset ID whose authoritative `assetKind` is `icon-spec`. It does not accept an `objectKey`, URL, Data URL, or Blob URL.
@@ -169,6 +169,8 @@ client.generate_image(
)
```
Image edit/redraw has a stricter main-source identity rule. After upload confirmation, create either a project resource or an asset-library record and pass its `resourceId` or `assetId` as `sourceReferenceId`. Do not pass the uploaded objectKey as the main source; objectKey remains valid only for auxiliary `referenceImageSrcs` where the OpenAPI permits it.
Icon spritesheet generation has a stricter primary-spec contract. After upload confirmation, create a project resource or asset record with `assetKind: "icon-spec"`, retain its returned `resourceId` or `assetId`, and pass that ID as `referenceId`. The primary spec does not accept the uploaded `objectKey` directly; only additional style references may continue to use stable object keys in `referenceImageSrcs`.
For character animation from a local-only source, use actual dimensions and a stable synthetic layer ID:
@@ -548,13 +548,16 @@ class GenarrativeExternalClient:
idempotency_key=idempotency_key,
)
def edit_image(self, prompt: str, source_image_src: str, **fields: Any) -> Any:
def edit_image(self, prompt: str, source_reference_id: str, **fields: Any) -> Any:
source_reference_id = source_reference_id.strip()
if not source_reference_id:
raise GenarrativeApiError("source_reference_id must be a registered resource or asset ID")
self._apply_canvas_session_fields(fields, prompt, 1024, 1024)
prompt = self._apply_art_spec(fields, prompt)
idempotency_key = fields.pop("idempotencyKey", None)
return self.submit_and_wait_generation(
"/api/external/v1/editor/images/edits",
{"prompt": prompt, "sourceImageSrc": source_image_src, **fields},
{"prompt": prompt, "sourceReferenceId": source_reference_id, **fields},
idempotency_key=idempotency_key,
)