diff --git a/apps/ai-game-creator-shell/src/features/project-workspace/SupervisorChatOnlyView.tsx b/apps/ai-game-creator-shell/src/features/project-workspace/SupervisorChatOnlyView.tsx index 0a53d307b..a78af79e3 100644 --- a/apps/ai-game-creator-shell/src/features/project-workspace/SupervisorChatOnlyView.tsx +++ b/apps/ai-game-creator-shell/src/features/project-workspace/SupervisorChatOnlyView.tsx @@ -9,7 +9,7 @@ import type { SetStateAction, UIEventHandler, } from 'react'; -import { useEffect, useMemo, useState } from 'react'; +import { useEffect, useLayoutEffect, useMemo, useState } from 'react'; import type { GameCreationAppManifest } from '../../../../../packages/shared/src/contracts/gameCreationApp'; import { PROJECT_SUPERVISOR_AGENT_ID } from '../../app/constants'; @@ -1142,7 +1142,7 @@ export function SupervisorChatOnlyView({ url.searchParams.set('genarrativeRevision', String(previewRevision)); return url.toString(); })(); - useEffect(() => { + useLayoutEffect(() => { setShowRuntimeDetails(false); }, [projectPath, runtime?.runId]); useEffect(() => { diff --git a/docs/project-memory/shared-memory/decision-log.md b/docs/project-memory/shared-memory/decision-log.md index b42d1a4b5..01eed9173 100644 --- a/docs/project-memory/shared-memory/decision-log.md +++ b/docs/project-memory/shared-memory/decision-log.md @@ -164,6 +164,17 @@ --- +## 2026-08-04 画布生成输入 V2 原地收紧并统一回落当前默认值 + +- 背景:画布 `generationInputs.version=2` 从持久化 JSON 恢复时只校验通用结构,未知、非法、已下线或与当前模型能力不兼容的模型、比例、尺寸、清晰度、声音、时长及角色动作档位仍可通过 TypeScript 断言进入 UI 和再次提交。前端已隐藏但后端仍兼容的历史 Veo 也不再属于当前可选模型。 +- 决策:不新增 V3,不迁移数据库,不保留旧值再次执行;V2 在读取时原地按 `action` 解码。所有已存在但未知、非法、已下线或与当前模型不兼容的参数统一回落到该 action 的当前默认值,历史 Veo 同样回落到当前默认视频模型。图片比例 / 尺寸按回落后的模型联动校验,视频参数按当前模型能力校验,角色动作 `frameCount / durationSeconds` 按完整档位成对校验。发生回落时必须向用户显示“部分原生成参数已使用当前默认值”告警;再次提交只使用规范结果并保存为仍是 `version: 2` 的新快照。缺失字段继续由当前 action 默认值补齐,不为此单独升级版本。 +- 实现边界:单一 action 级 runtime decoder 是持久化 V2 的读取真相,恢复 UI、改造入口和再次提交链不得各自解释原始 JSON;canonical 选项从当前编辑器模型 / 参数注册表派生,不新增平行旧模型清单。通用结构不合法时整份配方不支持改造;必需 `source` 缺失时仍按 action capability 保留改造按钮,点击后在恢复路径拒绝改造,不用默认值伪造引用;运行期来源变化时同样必须复检并拒绝。该策略只改变画布配方的运行时恢复和后续重存,不修改 SpacetimeDB schema、BFF DTO 或已有资产原始 JSON。 +- 影响范围:`ImageCanvasGenerationModel` 的 V2 decoder、`ImageCanvasGenerationDialogModel` 的恢复入口、`useImageCanvasGenerationWorkflow` 的回落告警、生成输入回归测试和编辑器 Lovart 统一方案。 +- 验证方式:fixture 覆盖当前合法 V2、历史 Veo、未知模型、模型不兼容尺寸、非法视频参数、非法音效参数、角色动作错配档位和缺失必需来源;断言 UI、价格与提交使用规范值,回落显示告警,再次生成仍保存 V2。运行图片画布定向 Vitest、`npm run typecheck`、定向 ESLint、`npm run check:encoding` 和 `git diff --check`。 +- 关联文档:`docs/【编辑器】生成类面板Lovart统一改造方案-2026-06-17.md`、`review.txt`。 + +--- + ## 2026-07-30 抠图实际后端作为 generationInputs 顶层内部元数据保存 - 背景:角色、图标图集和 UI 图集抠图派生资产需要保留最终实际执行的处理后端,供后台诊断 BgFilter、阿里云通用抠图和本地键色的降级结果;把抠图模型写成 `generationInputs.fields` 的“处理模型”会进入图片信息,与用户可见输入快照语义冲突,而覆盖正式资产 `model` 又会丢失源生图模型。 @@ -177,10 +188,10 @@ ## 2026-07-31 修正抠图内部元数据的普通用户读取边界 - 背景:2026-07-30 的记录误把素材 owner 与后台审计并列为原始抠图执行信息的读取方。owner 是普通用户,前端不展示字段不能阻止其从项目资源、素材库、精选提交 / 点赞回包、画布布局或任务完成响应的网络 payload 读取 BgFilter、阿里云、本地键色、具体分割模型或背景色。 -- 决策:本条取代 2026-07-30 决策中“素材 owner、精选提交响应仍可读取原始值”的表述。普通用户(包括素材 owner)和匿名公开读取必须共同过滤素材顶层 `provider`、内部处理 `model`,以及 `generationInputs` 顶层 `screenColorHex`、`mattingProvider`、`mattingModel`;正常用户可见生成 `model` 和其他合法功能性顶层字段(例如 `characterAnimation`)保持不变。User/Owner mapper 先完成该清理,public mapper 在其基础上叠加公开字段规则;后台管理与服务端审计继续使用 raw mapper 和持久化原值。历史数据不迁移,统一在读取边界清理。 +- 决策:本条取代 2026-07-30 决策中“素材 owner、精选提交响应仍可读取原始值”的表述。普通用户(包括素材 owner)读取时必须过滤素材顶层 `provider`、内部处理 `model`,以及 `generationInputs` 顶层 `screenColorHex`、`mattingProvider`、`mattingModel`;正常用户可见生成 `model` 和其他合法功能性顶层字段(例如 `characterAnimation`)保持不变。匿名公开素材 payload 不包含整个 `generationInputs`。User/Owner mapper 完成普通用户清理,public mapper 在其基础上移除该 owner-only 配方字段;后台管理与服务端审计继续使用 raw mapper 和持久化原值。历史数据不迁移,统一在读取边界清理。 - 入站与持久化:客户端提交的 `generationInputs` 不得伪造上述内部键,服务端在实际处理完成后才写入可信值。手动去背景的正式素材 `model` 必须继承经服务端验证的正常源生图模型;若来源或祖先链不存在正常模型则为 `null`,不得写入 `BgFilter complex` 等内部处理模型。内部抠图 provider / model 可继续持久化供后台审计,普通用户完成响应和用户可见错误文本均不得暴露它们;手动去背景与角色动作透明化失败在 Owner HTTP / 任务状态边界统一替换为稳定业务文案,原始错误只留在任务记录、tracing 和后台审计。 - 影响范围:项目资源、素材库、精选提交 / 点赞、图片 / 图标 / 视频 / 音频 / 角色动画生成完成、Agent 紧凑结果和识别出的画布资源 / 图层快照的 User/Public mapper;手动去背景持久化和完成响应;External Editor API 创建素材 / 资源时的保留键入站清理;相应响应 / OpenAPI 契约、前端搜索 / 详情 / ZIP 过滤测试与后台 raw 审计测试。同源画布 BFF 的角色、图标和 UI 请求继续由前端自动提交默认 `segModel=birefnet`,后端继续校验并在缺失时回落默认值;该请求控制字段不进入 `generationInputs`、普通用户响应、搜索、详情、导出或错误详情。角色动作的 `seg_model` 继续由后端固定。不修改 SpacetimeDB schema、迁移或 bindings。 -- 验证方式:Owner 和匿名响应覆盖无 `provider`、无内部 `model`、无三个内部 `generationInputs` 键,且正常 `model` 与 `characterAnimation` 仍保留;Admin raw payload 保持完整。覆盖历史 `BgFilter complex`、三类入站伪造键、手动去背景源模型回溯和用户错误文本过滤;运行 api-server 定向测试、前端定向测试、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`、`npm run typecheck`、`npm run check:encoding` 与 `git diff --check`。 +- 验证方式:Owner 响应覆盖无 `provider`、无内部 `model`、无三个内部 `generationInputs` 键,同时保留正常 `model` 与 `characterAnimation`;匿名公开素材响应完全不含 `generationInputs`;Admin raw payload 保持完整。覆盖历史 `BgFilter complex`、三类入站伪造键、手动去背景源模型回溯和用户错误文本过滤;运行 api-server 定向测试、前端定向测试、`cargo check -p api-server --manifest-path server-rs/Cargo.toml`、`npm run typecheck`、`npm run check:encoding` 与 `git diff --check`。 - 关联文档:`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`。 --- @@ -1181,7 +1192,7 @@ ## 2026-06-21 图片画布参考图元数据只保存项目内行引用 - 背景:参考图如果把 Data URL、signed URL 或 `objectKey` 写入 `generationInputs` 或生成器布局快照,会撑大资源 / 素材 / 画布 JSON,也无法稳定索引到项目内用户可见行数据。 -- 决策:`generationInputs.references` 只保存 `{ title, label, refType, refId }`,其中 `refType="project-resource"` 指向 `editor_project_resource.resourceId`,`refType="asset"` 指向 `editor_asset.assetId`。生成器 `itemType="generation-dialog"` 布局快照中的参考图也只保存 `resourceId/sourceAssetId` 和展示 label,不保存图片 Data URL、signed URL 或 `objectKey`;提交生成请求前的内存态可以临时持有 `src/objectKey`,刷新恢复时从 `editor_project_resource` / `editor_asset` 行补回请求所需图片源。不兼容旧 `src` 型参考图元数据。 +- 决策:`generationInputs.references` 只保存稳定行指针,不保存媒体本身。2026-08-03 起 V2 新写入结构为 `{ id, title, label?, refType, refId }`;`refType="project-resource"` 和 `refType="asset"` 只用于匹配当前画布中已 hydrate 图层的 `resourceId/sourceAssetId`,媒体类型取匹配图层的运行时数据,不新增 owner-only 工程资源 / 素材库 resolver。生成器 `itemType="generation-dialog"` 布局快照中的参考图也只保存 `resourceId/sourceAssetId` 和展示 label,不保存图片 Data URL、signed URL 或 `objectKey`;提交生成请求前的内存态可以临时持有 `src/objectKey`。面板直接上传引用不是画布图层,不承诺刷新或复用恢复;已移出画布的引用同样不恢复。不兼容旧 `src` 型参考图元数据。 - 影响范围:图片画布生成输入快照、生成器布局保存 / 恢复、参考图上传工作流、元数据弹窗和图片画布技术文档。 - 验证方式:运行图片画布生成模型、生成提交、上传工作流、项目持久化、元数据弹窗相关前端测试,执行 `npm run typecheck`、`npm run check:encoding` 和 `git diff --check`。 - 关联文档:`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`。 @@ -6618,6 +6629,15 @@ - CSS `url(...)` 的资产路径保持原始大小写解析,stylesheet 证据必须同时命中实际可见元素;未命中 selector、元素自身或祖先 hidden、以及匹配隐藏规则的节点均不作证。 - 关联:`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_protocol/autonomous_completion.rs`、`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md`。 +## 2026-08-03 图片画布生成产物统一“改造”契约 + +- 背景:画布生成结果统一显示“改造”,但部分动画 / 音频结果无法恢复面板;另一些非生成型派生结果继承最近生成输入,产生错误可执行动作。中文标题和素材类别被同时当成显示文案、参数键和路由键,改名后容易漂移。 +- 决策:用户动作 `改造` 的语义是恢复原生成输入、编辑并生成新产物。沿用 `generation_inputs_json`,V2 以稳定 `action`、`fields[].id`、`references[].id/refType/refId` 作为唯一执行契约,`title` / `label` 只用于展示,字段值保留基础类型。引用只匹配当前已 hydrate 的画布图层,媒体类型取匹配图层的运行时数据,不重复写入快照,也不新增 owner-only 工程资源 / 素材库 resolver。面板直接上传引用和已移出画布的引用不恢复:可重新选择的槽位留空并提示,提交门禁继续校验必填槽位;必须依赖原 `source` 图层才能构造面板的 action 仍按 capability 保留改造按钮,source 缺失时点击后显示明确错误并拒绝改造,运行期来源变化时仍必须复检。有效 V2 不因引用缺失降级到 legacy adapter。提交前参数只归一一次,请求与持久快照共用同一归一值。 +- 兼容:恢复优先级为有效 V2 → 完整历史生成对话框 → legacy adapter。legacy 允许使用 `assetKind/mediaType`、历史标题别名、资源模型 / 尺寸 / 时长 / `sourceResourceId` 和当前默认值,但必须显示恢复告警;不回填存量数据,不做 SpacetimeDB schema 迁移。 +- 边界:独立裁扩、手动去背景和手动图集拆分结果不继承生成输入,不显示 `改造`;原生成任务内自动后处理产物可保留原输入。Owner 读取保留 V2 执行字段;匿名公开素材 payload 暂不返回 `generationInputs`,不沿用 owner 可执行配方 DTO。 +- 影响范围:图片、规范、角色、图标、UI、宣发、视频、音效、背景音乐、角色动作、生成型图片编辑和 UI 素材提取;不影响作品详情“作品改造”、`AI重绘` 或常规 `快速编辑`。 +- 关联文档:`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`、`docs/【编辑器】生成类面板Lovart统一改造方案-2026-06-17.md`、`docs/【编辑器】画板音乐生成入口设计-2026-06-18.md`。 + ## 2026-08-05 画布图层元数据以资源行为准,读边界补齐 sourceType - 背景:结构化画布保存要求图层布局项里的资源权威字段与 `editor_project_resource` 行逐字相等,否则整次 PATCH 报「与项目资源不一致」,而该 400 属于 non-retryable,会被前端保存队列静默吞掉。但读边界并不把这些值原样下发:`sanitize_editor_user_model` 会脱敏内部处理模型、`provider` 被无条件省略(见 2026-07-31 修正抠图内部元数据的普通用户读取边界),`sourceType` 则在结构化保存校验通过后被归还资源行、图层列置空,读回时整个键不存在。客户端拿不到权威值只能自己补——`resolveHydratedLayerModel` 沿来源链推导出展示用生图模型,`hydrateLayer` 把缺失的 `sourceType` 猜成 `uploaded`——再原样回写,判等于是必然失败。前者命中含 2026-07-30 之前抠图派生资源的画布,后者命中所有 generated 图层;两者都在项目重新加载后的首次保存触发,用户侧表现为「改动悄悄没保存」,完美像素因为提交前是严格保存才把服务端原文暴露出来。 @@ -6854,6 +6874,21 @@ - 影响范围:所有现役编辑器生成类型、`spacetime-module` / `spacetime-client` 结果提交契约、queue worker 终态写回、schema / migration / generated bindings 与对应故障注入测试。完美像素保留现有专用原子 procedure;手动图集拆分保留现有批量事务,其 canvas completion 并入批量事务另行收口。 - 关联:`docs/technical/【后端架构】编辑器生成结果原子提交与幂等重放方案-2026-08-06.md`、Issue #134。 +## 2026-08-07 确定性派生配方与改造 capability 分离 + +- 决策:`generationInputs` 是持久化配方 / 来源账本,不直接代表“允许改造”。完美像素、裁扩、所有手动与自动图集切片、手动去背景分别写 `image.perfect-pixel`、`image.crop-expand`、`spritesheet.split`、`image.remove-background`,固定 `fields: []`;有正式来源行时只保存服务端权威 `references[id="source"]`,没有正式行时为空。这四个 action 不进入改造 allowlist,历史 `pixel-art-snap-*` 同样失败关闭;整张生成图集继续保留生成 action,自动抠图仍是生成流程内部后处理。 +- V2 水合统一按 `version/action/fields[].id/references[].id` 严格识别,保留有限数字、布尔值与无标签引用;出现 V2 标记但结构无效时不得降级 legacy。完美像素账本复用相同 V2 白名单,外层仍为 version 1,并继续原形接受旧 legacy 请求以维持 exact retry fingerprint。 +- 安全边界:站内已迁移队列只保留客户端 references 的安全槽位 `id`,真实 `title/label/refType/refId` 全部按 owner-scoped 记录重建;External API 和直接不可信写入仍删除整段 references。队列幂等比较忽略该冗余展示槽位,但继续严格比较实际媒体来源与其它参数。 +- 不改 SpacetimeDB schema、路由或 External OpenAPI;不回填历史记录。 + +## 2026-08-08 游戏场景接入 V2 改造配方 + +- 背景:`72f268e0` 新增 `assetKind = scene` 和场景专用生成入口,但场景生成输入仍是依赖中文标题的 legacy 快照,无法通过现役 V2 action allowlist 稳定恢复“改造”。 +- 决策:新增稳定 action `scene.generate`,字段 ID 固定为 `prompt/stylePreset/customStyle/model/aspectRatio/imageSize`,参考槽 ID 固定为 `reference`。新产物由服务端按结构化场景请求重建权威 V2 fields,并从真实 `referenceImageSrcs` 生成安全引用槽位,后续继续沿用 owner-scoped provenance 重建;前端 action decoder 恢复场景 composer,并让请求与持久快照共用规范化参数。 +- capability 边界:`assetKind` 只表示素材类别,不直接授予“改造”。`scene.generate` 显式进入已知与可改造 action allowlist;V2 上线前的场景仅在 `assetKind === scene` 且 legacy 字段包含“画面内容”时兼容恢复并告警,不把“画面内容”加入全局 legacy 标题路由,其他类别同名字段继续拒绝改造。 +- 影响范围:图片画布场景提交、生成输入解码、改造入口、场景 composer 恢复、api-server 场景配方重建与对应前后端测试;不修改 SpacetimeDB schema、migration、bindings、External v1 路由或 OpenAPI。 +- 关联文档:`docs/technical/【技术方案】图片画布游戏场景生成链路-2026-08-04.md`、`docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md`、`docs/【编辑器】生成类面板Lovart统一改造方案-2026-06-17.md`。 + ## 2026-08-08 AGC Vite 纳入统一用户端口段 - 背景:Linux 主开发栈已按用户分配 `100` 端口段,但后加入的 AGC Tauri 壳仍固定监听全机共享的 `3080`。同机任一用户的旧客户端都会阻塞其它用户,且 marker 中出现的动态 API 端口无法解决 Vite 本身的跨用户冲突。 diff --git a/docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md b/docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md index 1fdb9bd7a..f1b999b56 100644 --- a/docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md +++ b/docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md @@ -19,7 +19,10 @@ - 鼠标中键拖拽始终平移画布;长按 Space 临时进入抓手模式,松开后恢复原工具。 - 图片拖拽时显示水平 / 垂直吸附参考线,吸附到其它图层、生成占位框或画板的边缘与中心线;当移动元素接近两个同轴元素形成的等距位置时,支持横向或纵向等距吸附。 - 生成资源右上角显示元数据按钮,点击打开独立元数据窗口。图片信息页不展示后端组装后的生图 Prompt,也不提供复制 Prompt;只展示该图片生成时用户在面板里提交的输入快照,包括普通生成提示词、规范表单字段、角色设定、图标素材描述、快速编辑提示词、重绘提示词,以及角色规范 / 常规参考图 / 图标规范 / 编辑参考图等参考图卡片,并提供“复制信息”复制当前可见字段。参考图输入快照只保存 `refType/refId` 行引用,其中 `refType="project-resource"` 指向 `editor_project_resource.resourceId`,`refType="asset"` 指向 `editor_asset.assetId`;不得把图片 Data URL、普通 URL 或 `objectKey` 写入 `generationInputs.references`。旧数据或上传图片没有输入快照时显示 `-`,禁止回退展示内部 Prompt。 -- 对生成资源执行重绘时,在右侧创建新的生成结果图层,并自动调整视图显示原图和新图;重绘面板不因提交成功自动关闭,便于连续改提示词。重绘 / 改造输入框只允许从 `generationInputs.fields` 中恢复用户可见输入快照,例如普通生成提示词、视频描述、音效 `prompt`、背景音乐 `gpt_description_prompt`、角色设定、UI 用户输入、图标素材描述、规范表单和宣发素材字段;禁止回退展示资源 `prompt` / `actualPrompt` 中可能存在的后端拼接 Prompt、固定生成模板或模型默认提示词。当前 BGM 是字段语义上的例外:其新记录中的 `prompt` 与 `actualPrompt` 必须等于输入框已经写回的 canonical `gpt_description_prompt`,不得包含隐藏内容;但改造输入仍只从 `generationInputs.fields` 恢复,不能因此放宽为从资源审计字段回退。没有用户输入快照的旧图层打开改造时保持空输入,等待用户重新填写。 +- 生成类产物的工具栏动作 `改造` 不在原产物上原地修改,而是把当次生成输入恢复到对应面板,允许编辑后生成新产物。按钮显隐只按 action capability 判断:确定性派生等不支持改造的 action 隐藏;支持改造的 action 即使参数损坏或必需来源丢失也保留按钮,点击后再显示准确错误,禁止用恢复状态静默隐藏入口。`image.edit` 明确不属于可改造 action:图片快速编辑使用 `targetLayerId` 原位替换画布层,结果层就是被覆盖后的原层,其配方 source 指向替换前的编辑目标,不能再可靠恢复为“改造”新产物;这类结果继续显示“快速编辑”,不显示“改造”。 +- V2 恢复严格按顶层 `action`、`fields[].id` 和 `references[].id/refType/refId` 定位,`title` / `label` 只是展示快照,不作为参数键。引用只在当前已 hydrate 的画布图层中按 `refType/refId` 匹配,媒体类型取匹配图层的运行时数据,不重复写入 `generationInputs.references`,也不新增 owner-only 工程资源 / 素材库查询。面板直接上传的参考素材不进入素材库、不是画布图层,因此改造时不恢复;原参考图层已移出画布时同样不恢复。**引用在提交时是否必填,不是恢复时拒绝打开面板的依据**:凡目标面板提供重新选择入口的槽位,无论可选还是必填,缺失时都必须 best effort 打开面板、留空槽位并提示用户重新选择,最终由提交门禁校验;例如角色生成缺少必填角色规范、图标生成缺少必填图标规范、普通图片 / 视频缺少参考图时,都必须打开原 action 面板让用户补选。只有已支持改造但目标面板没有手动替换入口、缺少 source 就无法构造请求时才允许拒绝,当前严格限定为两个例外:`character-animation.generate` 的 source 决定角色动作面板绑定的原角色图层,面板内部不能换角色;`ui-design.extract-assets` 的 source 同时决定框选覆盖层的坐标系,素材提取面板不能改绑另一张设计图。两个例外仍按 action capability 保留改造按钮;source 缺失时点击按钮才显示“无法改造”并分别说明不可替换原因,点击后 source 变化也必须在恢复路径再次校验并拒绝;该失败规则不得用于任何可手动补选的槽位。 +- 改造恢复顺序为:有效 V2;完整的历史生成对话框;按 `assetKind/mediaType`、历史中英文标题别名、资源模型 / 尺寸 / 时长 / `sourceResourceId` 的 legacy adapter,最后缺失值使用当前默认值并显示旧版恢复告警;一旦识别为有效 V2,即使引用缺失也不得降级到 legacy adapter。禁止从中文标题、产物尺寸或 prompt 反推 V2 参数;legacy fallback 除外,但不得回退展示 `actualPrompt` 中的后端拼接 Prompt。当前 BGM 是字段语义上的例外:其新记录中的 `prompt` 与 `actualPrompt` 必须等于输入框已经写回的 canonical `gpt_description_prompt`,不得包含隐藏内容;但改造输入仍只从 `generationInputs.fields` 恢复,不能因此放宽为从资源审计字段回退。 +- V2 资源 / 素材 hydrate 为前向兼容读取:顶层、`fields[]` 和 `references[]` 出现未来可选属性时只复制当前已知字段,不得让整份配方消失;完美像素操作快照等精确重放路径仍执行递归白名单,并保留历史空字符串的字节级兼容。hydrate 结果必须保留 `absent | valid | invalid-versioned` 运行时来源状态:只有 `absent` 可走 legacy adapter;结构损坏或版本不支持的版本化配方标为 `invalid-versioned`,所有恢复入口都必须拒绝自动降级,并提示“保存的生成参数版本不受支持或数据损坏,无法可靠恢复”。该标记不属于持久化 generationInputs 契约,也不得把原始损坏 JSON 回写。对模型、比例、尺寸、时长、开关等具有明确运行默认值的 V2 参数,缺失与非法值统一补写默认值并告警;允许为空的提示词 / 表单自由文本不因缺失产生参数失效告警。 - 图片生成 / 修改统一经 api-server BFF 接入 VectorEngine。普通生成、生成规范和重绘保留既有 `gpt-image-2` 路径;图片快速编辑统一打开框选区域 + 单提示词 + 模型选择面板,默认沿用原图模型,不展示参考图或比例 / 尺寸控件;其中生成规范类图片固定 `16:9`、`2K`、`gpt-image-2`,面板底部用与可编辑面板一致的比例 / 尺寸 / 模型胶囊按钮展示固定参数,但按钮为禁用态,不允许在该面板改比例、尺寸或模型。`生成角色形象` 与 `生成图标素材` 支持 `nanobanana2`(`gemini-3.1-flash-image-preview`)和 `gpt-image-2`,默认 `nanobanana2`,并在两类面板之间沿用用户上次选择的模型;两类面板不展示抠图背景色或抠图模型选择;前端用户路径固定提交 `screenColor=auto` 和 `segModel=birefnet`,由后端自动决策具体抠图背景色,`anime-seg` 作为内部保留能力不在用户界面暴露。`nanobanana2` 走 `/v1beta/models/{model}:generateContent`,请求体写入 `generationConfig.imageConfig.aspectRatio/imageSize`;`gpt-image-2` 走 `/v1/images/generations` 或 `/v1/images/edits`,请求体按 VectorEngine 文档映射 `size`。宣发素材三个工作流(游戏首图、详情五图、运营海报)固定使用 `gpt-image-2`,面板模型胶囊为禁用态,不提供 `nanobanana2` 入口;前端按 workflow 同时提交 `outputSize`、`aspectRatio` 和 `imageSize`,其中游戏首图为 `720x540 / 4:3`、详情单图为 `720x1280 / 9:16`、运营海报为 `1280x720 / 16:9`;后端收到 `kind: "publication-material"` 时也强制归一为 `gpt-image-2` 生成和计费,生成回填图层优先使用生成占位的 `originalWidth/originalHeight`,即使上游回包尺寸漂移也不得把宣发素材卡片变成随机 `1:1` 或 `4:3`。纯文本生成走 `/api/editor/images/generations`,重绘在前端优先复用当前图层 objectKey;尚未登记的本地图片先上传 OSS,再把 objectKey 交给同一图片生成 BFF,并在原图右侧生成一张新图;普通图层重绘作为 `quick-edit` 参考图提交,角色图层重绘必须按 `kind: "character"` 提交,继续套用角色生成器提示词限定、透明 PNG 后处理和角色资产持久化。`生成视频` 走 `/api/editor/videos/generations`,前端模型入口仅展示 Seedance 2.0 Fast / Seedance 2.0 / Kling 3.0 / Kling 3.0 Omni,不展示 Veo 入口,默认 Seedance 2.0 Fast;视频参数按当前正式面板支持的比例、时长、清晰度和声音开关提交,且 Seedance Fast 与 Seedance 标准版必须按各自真实模型 ID 独立映射,不得混用。生成结果以视频图层加入画布。纯文本生成入口采用 Lovart 式画布内占位图 + 锚定生成输入框:点击生成图片后以当前视口世界中心为目标,经统一 placement 避让后创建选中的灰色占位框,输入框跟随占位框显示;普通图片、角色、图标图集、UI 设计图及其重绘 / 改造入口必须在比例或清晰度恢复、切换时同步把占位框 `width/height/originalWidth/originalHeight` 更新为目标像素尺寸,生成中不得继续显示默认 1K 框;UI 素材提取的 1K / 2K 图集占位和旧图片修改入口也分别使用本次目标尺寸与源图真实尺寸。待生成、生成中和失败后保留的占位图都必须继续支持拖动,生成完成时真实生成图或视频落在最新占位框位置,输入框继续跟随新生成图层;占位图失焦时隐藏高亮边框、左上角生成器名称和右上角原始尺寸,重新聚焦时再显示,且名称 / 尺寸在画布缩小时按 viewport 反向缩放保持屏幕尺寸稳定;点击所有图片 / 视频生成入口并确认请求开始后,必须隐藏对应设置面板,只保留画布内占位图或原图预览,并在预览上显示 Lovart 式生成中遮罩,避免“面板仍占屏”或“预览一起消失”。图片快速编辑和重绘在调用图片 BFF 前必须把当前图层图片解析为已上传的 objectKey 或资源 ID;浏览器临时图片需先上传 OSS;视频素材快速编辑走视频生成 BFF,不允许走图片模型;角色动作的 `生成动画` 仍固定使用 `seedance2.0-fast` 动作 / 视频模型,角色动作素材的 `快速编辑` 按当前帧图片走图片编辑。前端不持有 provider 密钥;上游失败或配置缺失时恢复当前生成设置面板展示失败,不创建 mock 成功图。 - 图片画布抠图统一通过唯一、只监听 loopback 的 `bgfilter-worker` 调用 BgFilter provider。手动去除背景面向用户任意图片,仍走登录态同源 BFF `POST /api/editor/images/background-removals` 和外部生成队列;API 在入队前拒绝 `data:` / `blob:` 内联媒体,父流程将稳定引用解析为当前账号已登记且归属已校验的私有 OSS object key,并在同一轮账号项目 / 素材快照读取中同时恢复用户可见源模型,禁止为 object key、所有权和源模型分别重复拉取全量快照;随后只通过一次内部 HTTP RPC 传递 object key、排队预算 `maxQueueWaitMs`、调用预算 `callBudgetMs` 和固定的 `background_mode=complex`、`seg_model=birefnet`、`cross_check=off`,不传源图字节、签名 URL、`file` 或 `screen_color`。子 worker 在每次真实 provider attempt 前签发 600 秒 URL,承担默认 `Q=2048` admission 保险丝、provider 并发 `N=16`、严格最多两次顺序 attempt、响应字节与图片尺寸校验,并把成功图片作为内部 HTTP 二进制 body 直接返回;父流程同步等待该响应且不重试已被 worker 接收的内部 RPC(连接从未建立时按调度方案 §5.1 有界重连)。排队只消耗 `maxQueueWaitMs`,取得 provider permit 后才启动 `callBudgetMs`;attempt 按 `N × est × 2`、调用预算按 `2 × attempt + 1s` 派生,冻结 `est=5000ms` 时分别为 `160s / 321s`。complex 的真实 provider 失败会累计并打开自身熔断,但与 flat 状态隔离;complex 任意失败或熔断仍直接返回父流程失败,不接入阿里云 / 本地键色降级。provider 配置继续统一使用 `GENARRATIVE_EDITOR_BGFILTER_BASE_URL` 和 `GENARRATIVE_EDITOR_BGFILTER_TOKEN`,父子共同使用 `GENARRATIVE_BGFILTER_WORKER_CONCURRENCY` 与 `GENARRATIVE_EDITOR_BGFILTER_SINGLE_IMAGE_ESTIMATE_MS` 派生预算;旧 `GENARRATIVE_EDITOR_BACKGROUND_REMOVAL_TOKEN` 只作为 provider token 兼容别名;内部调用另使用 `GENARRATIVE_BGFILTER_WORKER_BASE_URL` 和独立内部 Token。所有令牌只在服务端注入,前端不持有令牌。成功字节返回父流程后,仍由父流程完成最终处理、OSS / asset object 持久化、结果图层与最新项目快照写回;接口只向前端返回 `queueState`,有项目上下文时前端同时创建去背景生成占位并把 `canvasCompletion` 交给后端。 - 编辑器自己生成的标准纯色背景抠图资产在保存源图后统一以 `background_mode=flat` 调用内部 `bgfilter-worker`。角色形象生成、图标 spritesheet 生成、UI 设计图素材提取和角色动作的用户路径都固定使用 `screenColor=auto`,但用户可见 `generationInputs.fields` 不记录 `抠图背景色` 或 `抠图模型`;api-server 在组装 prompt 前调用背景决策模块,从 12 个候选色中选择具体 hex,最多重试 3 次,失败后兜底 `#CFEFFF`。后端仍保留手动 hex 解析能力供内部兼容。最终生图 prompt、动作视频实色背景和子 worker 发往 provider 的 `screen_color` multipart 字段只接收解析后的具体 hex,不透传 `auto`。角色、图标 spritesheet 和 UI 设计图素材提取的同源画布请求由前端自动提交默认 `segModel=birefnet`,api-server 负责 allowlist 校验并在缺失时回落默认值;角色动作逐帧去背的 `seg_model` 由后端固定。四条 flat 路径再由 api-server 向 worker 显式传递 `background_mode=flat` 与 `cross_check`:角色形象生成、图标 spritesheet 和角色动作逐帧去背传 `on`,UI 设计图素材提取传 `off`,不依赖 BgFilter 服务端默认值。前端不展示抠图模型选择,`segModel` 不进入 `generationInputs`、响应、搜索、详情或导出;`background_mode`、`cross_check` 只存在于 api-server 到 worker 的内部 RPC。子 worker 为 flat / complex 分别维护独立进程级熔断,并对一次逻辑调用严格最多执行两次顺序 provider attempt;两种模式共享 `GENARRATIVE_EDITOR_BGFILTER_CIRCUIT_FAILURE_THRESHOLD=3` 和 `GENARRATIVE_EDITOR_BGFILTER_CIRCUIT_COOLDOWN_SECONDS=120` 默认值,但失败和成功只更新当前模式;父侧至多让 worker 接收一次内部 RPC(连接从未建立时按调度方案 §5.1 有界重连)。flat 两次失败、熔断、overload、内部 deadline 或断连后,只要父业务预算仍有效,父流程才按同一 object key 进入“阿里云通用抠图 → 本地 `editor_green_screen` 键色”降级;阿里云 fallback 不属于 `bgfilter-worker`。角色动作生成的序列帧背景色已与生图统一:后端把源角色图合成到视觉决策出的具体 hex 后再图生视频;抽帧后逐帧进入同一条 `内部 bgfilter-worker(background_mode=flat,cross_check=on)→ 父侧阿里云 → 父侧本地键色` 链路。 @@ -35,13 +38,13 @@ - 画布底部工具栏 / 面板 Dock 提供“画布 Agent”入口。点击后打开右侧独立 Agent 对话面板;桌面端为右侧窄面板,移动端占满可用宽度。该面板只与右上角任务侧栏互斥;素材 / 图层侧栏允许与 Agent 同时展开,切换左侧栏不得关闭 Agent。Agent 面板不得在当前画布内容下方追加内联内容,也不默认展示大段功能说明文案。 - 所有会新建画布生成占位的入口必须先创建 draft,再统一经过 `ImageCanvasGenerationPlacementModel` 计算落点,禁止各入口自行使用当前视口中心裸坐标或原图右侧固定偏移。当前覆盖入口包括 `生成图片`、`生成规范`、`生成角色形象`、`生成图标素材`、`生成视频`、`生成UI设计图` 和 `生成角色动作`。placement 模型的避让对象为所有未隐藏画布图层,以及当前 active / inactive generation dialogs 中仍存在的 placeholder;每个避让矩形按 32px 画布世界坐标间距外扩。候选落点以当前视口世界中心为距离目标,优先选择离视口中心最近且不重叠的占位位置;若中心被占用,会按上下左右和环形候选继续寻找。打开生成面板时必须把避让后的 placeholder 写入 `openCanvasGenerationDialog(...)`,并立即调用 `centerViewportOnPlacement(...)` 居中到新占位中心,保持原 viewport scale 不变;图片快速编辑不属于新建占位入口,提交后覆盖源图。 -- `generationInputs.references` 的 `refType/refId` 是服务端权威行引用:客户端提交的 references 只属于非权威展示候选,api-server 入队及直接创建资源 / 素材时删除,生成执行时按真实参考图和当前 owner 的资源 / 素材记录重建后再持久化;裸 owned objectKey 找不到正式资源或素材行时可以参与生成,但不得制造伪引用。`title/label` 只作为展示快照,不提升为资源身份。 +- `generationInputs.references` 的 `refType/refId` 是服务端权威行引用:客户端提交的 `title/label/refType/refId` 都不可信;站内已迁移生成队列只允许在入队 payload 中保留按位置对应的非 provenance 槽位 `id`,直接创建资源 / 素材及 External API 仍删除整段客户端 references。生成执行时按真实参考图和当前 owner 的资源 / 素材记录重建 `id + title/label/refType/refId` 后再持久化;裸 owned objectKey 找不到正式资源或素材行时可以参与生成,但不得制造伪引用。滚动部署的队列幂等比较只在新旧 payload 恰有一侧缺少 `references` 时忽略该字段;两侧都存在时槽位 ID / 数量 / 顺序必须严格一致,两侧都缺少时其它差异同样不得被归一,实际 `sourceImageSrc/referenceImageSrcs` 或其它参数变化始终必须冲突。 - 普通图片生成最多选择 5 张参考图;带主图 / 规范图的图片修改、图标素材和 UI 素材提取需要从打开面板起预留这 1 张主引用,再与 provider 容量取最小值(GPT-image-2 总计 5 张,nanobanana2 总计 14 张)。画布选择、上传和最终提交都必须阻止第 N+1 张进入请求;并发上传要把在途批次计入容量,并在创建项目资源 / 账号素材前按最新模型复验。reservation 必须绑定发起上传的 dialog / 快速编辑 / UI 提取上下文;任一参考图上传批次在途时,所有图片模型切换、从画布添加主图 / 规范图 / 参考图、提交生成、删除 / 剪切关联源图、删除其来源素材以及生成面板切换 / 关闭都要明确拒绝,批次持久化前后还要复验上下文未变化。批次部分失败时要保留并挂接已经成功持久化的引用,不能因其中一项失败而丢弃整批成功项;完成或失败并释放 reservation 后才允许继续操作。模型降容或后补主图若在操作当下已经超限,应保留原模型 / 原参考图并明确提示用户先删除,不得用 `slice` 静默丢弃;旧 dialog 或直接 API 请求由后端返回明确超限错误。 - 主站编辑器生成 POST 在浏览器端不自动重试;队列模式仍按同一 `x-request-id` 幂等重放,External v1 使用显式 `Idempotency-Key`。inline 模式没有结果级幂等时,不得因 408 / 429 / 5xx 或传输异常自动再次调用 provider。 ### 静态图片风格与像素规整边界 -- 普通 `生成图片`、`生成角色形象` 和 `生成图标素材` 三个面板增加紧凑的 `像素艺术` 勾选项;移动端可独占一行,但不增加功能说明文案。当前生成对象以 `style: "none" | "pixelArt"` 保存选择并随现有请求 / 队列 payload 传递;该字段不写入用户可见 `generationInputs`,也不新增素材元数据字段。其它生成、编辑、UI 素材提取、角色动画及画布 Agent 入口不展示或设置该选项。 +- 普通 `生成图片`、`生成角色形象` 和 `生成图标素材` 三个面板增加紧凑的 `像素艺术` 勾选项;移动端可独占一行,但不增加功能说明文案。当前生成对象以 `style: "none" | "pixelArt"` 归一后同时写入请求 / 队列 payload 和 V2 `generationInputs.fields[id="style"]`,确保后续改造恢复与当次实际提交一致;不新增素材元数据字段。其它生成、编辑、UI 素材提取、角色动画及画布 Agent 入口不展示或设置该选项。 - `style` 是可选字符串兼容边界。省略、`null`、空字符串和 `"none"` 统一归一为内部 `None`,不返回告警;`"pixelArt"` 仅允许普通图片(`kind` 省略)与 `kind="character"`,图标图集请求单独允许该值。未知字符串或在 `spec / quick-edit / ui-design / publication-material` 等不支持的图片 `kind` 上请求 `"pixelArt"` 时,按 `None` 完成原管线并通过既有通用 `warning` 返回 `unsupported-image-style`;非字符串 JSON 仍是畸形请求并返回 `400`。旧 payload 缺少字段时等价于 `None`。 - `None` 必须保持现有生成、尺寸处理、BgFilter、上传、资源和画布链路不变,包括提交给 provider 的提示词必须与未带该字段时逐字一致。`PixelArt` 在父流程内做两件事:向提交给 provider 的提示词追加一行像素风约束,以及在 provider 回图后执行纯内存 Rust 像素规整。两者都不启动 Python 或独立服务,也不改变 BgFilter 的 `flat` 参数、Alpha 回贴、`cross_check`、fallback 或默认关闭 despill 的现有行为。 - 2026-08-01 修订:`PixelArt` 增加提示词端约束。snapper 是几何对齐器,横纵两轴都检测不到网格步长时会退到 `min(width,height)/64` 统一网格兜底,产出马赛克而非像素画;因此在提示词端要求 provider 本身就输出块状结构。注入点固定为 `generate_editor_image_for_owner` 与 `generate_editor_icon_spritesheet_for_owner` 构造提交提示词的位置,包住既有 builder 的返回值,builder 签名和既有输出契约不变,三个入口(登录态路由、外部 API v1、异步 job worker)一处覆盖。约束句按链路分三条、追加在末尾并独立成行:普通图片用「画面为像素风格」;角色形象和图标图集生成后都要按纯色抠像,绿幕底必须保持平整,分别用「角色主体为像素风格」和「每个图标素材均为像素风格」,不得出现「画面」级别的像素化要求。实测只提「像素风格」已可接受,不注入网格密度、色板色数和抗锯齿等约束。约束句是否进入 `editor_project_resource` 的 prompt 列按链路而定:普通图片会进;角色形象在抠图成功后 `output_prompt` 被无条件覆盖为 `"去除纯色背景"`,其原图 resource 存的是用户原文,因此约束句不进角色的任何 project resource;图标图集的原图 spritesheet resource 会进,透明结果和切片则分别存 `"去除纯色背景"` 与 `"自动拆分图集"`。角色链路的完整提交提示词是否留存取决于 provider——asset object 元数据写的是 `actual_prompt.unwrap_or(prompt)`,provider 回了 `actualPrompt` 就存 provider 改写后的文本,此时 `submitted_prompt` 在系统内一处都不落(审计只记 `promptChars`)。响应体也不一致:普通图片和角色形象返回用户原文,前端显示不变;图标图集返回的是含约束句的工程化 `prompt`,调用方可直接看到模板内容。不新增 OSS PUT、项目资源、素材记录或画布图层。以上 prompt 列写入、asset object 元数据和响应字段规则全部是既有行为,本次未改动。 @@ -53,6 +56,8 @@ - 像素模式的持久化增量必须为零:普通图片仍只上传原有一张最终主图;角色仍只保留原有 provider 原图与透明主图;图标仍只保留原有 provider 原图、透明图集和实际成功的切片。禁止保存逻辑低分辨率图、像素化前后双份主图、预览图、网格诊断图或报告,禁止新增 asset / resource 类型、项目资源、画布 item、队列 job kind 或数据库字段。 - 像素后处理属于 best-effort:失败时保留进入该步骤前的图片,继续原有最终上传与画布完成,并通过既有通用 `warning` 返回非阻断原因,不把任务改为失败或退款。BgFilter 自身失败时仍按原 source-only fallback 收口,像素处理不运行;图标后处理成功后再执行原有自动拆分,拆分告警继续使用现有 `sliceWarning` 语义。 - 选中已有静态栅格图层后的 `完美像素` 是独立的一键派生操作,不等同于生成请求上的 `style="pixelArt"`。它不打开参数面板,只处理当前活动图层,保留源图,并在源图右侧创建同尺寸 PNG 派生结果;音频、视频、图片序列和 `character-animation` 不显示该按钮。 +- 完美像素、手动去背景、裁扩和所有图集切片是确定性派生操作,分别保存 `image.perfect-pixel`、`image.remove-background`、`image.crop-expand`、`spritesheet.split` 的 V2 `generationInputs`。四者固定 `fields: []`;存在正式来源资源 / 素材行时只保存服务端权威的 `references[id="source"]`,引用仅用于来源溯源,不是算法参数或可编辑槽位,没有正式行时保存空数组。改造 capability 使用独立 allowlist,四者及历史 `pixel-art-snap-*` 结果永不允许改造;自动切片的 source 是实际被切的透明图集,整张生成图集仍保留原生成 action。 +- 裁扩创建资源时,未携带 `sourceResourceId` 仍允许保存无正式来源引用的确定性结果;一旦携带该 ID,api-server 必须确认它属于当前 owner 和当前项目,否则以 `400` 拒绝,禁止同时持久化悬空 / 越权 `sourceResourceId` 和空来源配方。 - 已有图片像素规整固定调用登录态同源 `POST /api/editor/images/pixel-art-snaps`,复用同一纯内存 Rust snapper、CPU 并发许可和输入尺寸上限。该入口免费、只走当前 HTTP 请求内的 inline 处理,不创建 `external_generation_job`,不刷新或自动打开任务侧栏,也不进入泥点扣费 / 退款链路。它另有一层端点级并发闸(最大 4、等待队列上限 2048),设在首次 IO 之前;队列满返回 `503` 并带 `Retry-After`,等待超预算返回 `504`。30 秒总预算从 handler 入口起算,覆盖归属校验的 SpacetimeDB 读取、OSS 下载、两层排队与规整,不是只算 CPU 部分。 - **完美像素的优先级判据(约束实现、评审与复查)**:该操作免费(`generation_cost_mud_points = 0`)、同步、纯几何规整,重做一次的代价接近于零。因此凡是「已生成的资源在意外情况下丢失关联、需要用户重做或自行去素材库取回」这一类问题,一律**不构成阻断项**;只有主链路本身出问题才阻断——发起被拒、处理失败、结果没落库、已落库的结果既不回填画布也不进素材库。把前一类现象报成必须修复的缺陷之前,先确认它落在主链路上。 - **明确禁止的补救方向**:不得为了防止上述丢失而新增任何限制用户操作的特性,具体包括但不限于「禁止用户删除占位」「禁止用户重试」「禁止同一张图被处理两遍」。用户对自己画布上的元素始终保有删除与重做的权利;重复处理的最坏后果只是素材库多一份、用户可自行删除,这个代价远小于剥夺用户操作权。历史上引入过的同类封锁(未收口 operation 不可删除、随源图层清理豁免)已被逐条作废,不得以任何理由重新引入。既有的 `existingOperation` 闸(占位仍在时拦住从源图重新发起)是本条确立之前的遗留,方向与本条相反,后续应放宽而不是加固——尤其不得改成「让本机账本也参与防重」,那正是被本条禁止的「禁止一张图处理两遍」。 @@ -101,7 +106,7 @@ - 封面图层读取与栅格化集中在 `ImageCanvasProjectCoverSnapshotRenderer`:先排除不与封面输出区相交的图层,再以最多 6 路并发加载实际可绘制图层,最终仍按 z-index 顺序绘制。封面换签必须复用画布预览的 `taskId ?? resourceId` cache version,序列帧和视频 poster 继续使用同源后缀,以命中已有 pending / 内存 / session signed-URL 缓存并尽量复用同一 OSS URL 的浏览器缓存。封面上传只需 `objectKey / assetObjectId / legacyPublicPath`,使用 object-only 上传路径,不得为从未使用的立即预览 `src` 强制 `bypassCache` 换签。 - 图片、音频、视频和角色动画帧文件本体继续走 OSS / asset object;浏览器读取私有 generated 对象统一经 `/api/assets/read-url` 换签,签名 URL 可在 session 内复用,但不得作为持久化真相。`/api/assets/read-url` 属于页面展示层高频后台请求,前端统一在 `assetReadUrlService` 内做同 key pending 去重、session 缓存和跨组件节流;UI 设计切片、角色动画帧或大量素材恢复时不得绕过该服务并发换签,否则单页可在同一秒内打满发布入口 `genarrative_api_rps` burst。 - 登录态上传和生成结果必须先落 OSS / asset object,再向 `editor_project_resource` / `editor_asset` 写入轻量 `imageSrc: "/"`、`objectKey` 和 `assetObjectId`;未登录演示态可以在内存里使用 Data URL 预览,但项目、素材库、项目资源和 `editor_canvas.layers_json` 不得写入 `data:image/*`、`data:video/*`、`data:audio/*` 或 `blob:`。旧数据读取时如果已有 `objectKey`,`imageSrc` 归一成 `/`;没有 `objectKey` 的旧 Data URL 需要走修复上传并回写轻量引用。裁扩在项目上下文中虽然由前端 canvas 本地渲染 PNG,也必须先上传 OSS / asset object 并创建 `editor_project_resource`,再把带正式 `resourceId/objectKey/assetObjectId` 的裁扩图层加入画布;不能先把 `local-resource-*` + Data URL 图层交给项目保存或后续去背景。上传到生成面板参考图槽位的图片必须先创建 `editor_project_resource` 行;没有当前工程 ID 时才创建账号级 `editor_asset` 行,随后把对应 `resourceId` 或 `assetId` 写入参考图临时状态;生成请求提交前必须把临时状态解析成 `objectKey`、项目资源 ID 或素材 ID,未登记的本地图片和普通图片路径先上传 OSS,不能直接提交 Data URL、Blob URL 或临时图片源。 -- 资源表保存资源和素材级元数据,其中 `editor_project_resource.asset_kind` 是资源默认类型;图层位置、层级、分组选中所需 ID、groupId 和可空 `assetKindOverride` 保存在画布布局。legacy 布局 JSON 是混合数组:普通图层按 `layerId/resourceId/assetKindOverride` 保存,生成器占位和生成器对话框按 `itemType: "generation-dialog"` 保存;结构化模式下 `asset_kind_override` 必须是 `editor_canvas_layer` 的 typed 列,不得塞回 `item_json`。普通图层的新保存不再把资源默认 `assetKind` 或 `generationInputs` 写入布局;刷新时从 `editor_project_resource` 恢复资源默认类型,再与图层覆盖合并为 `effectiveAssetKind`,不得把合并结果反写为新的资源或覆盖。旧布局中的 `assetKind` 只用于迁移:与资源默认类型相同则归一为无 override,不同则迁移为图层 override;资源尚无默认值且允许补齐时先补资源默认值,不制造新的资源行。生成器快照必须包含生成器 ID、模式、提示词、参数、参考图、状态、占位框位置和可选 `generatedLayerId`;角色、图标等纯色抠图生成器的前端用户路径不保存或恢复 `screenColor` / `segModel`,同源重绘也不再从 `generationInputs.fields` 恢复 `抠图背景色` 或 `抠图模型`;宣发素材生成器还必须保存并恢复 `publicationWorkflowId`、`publicationGameInfo` 和 `publicationReferences`,避免刷新后生成卡片字段或参考图丢失。生成器快照中的参考图同样只保存 `resourceId/sourceAssetId` 行引用和展示所需 label,不保存图片 Data URL、signed URL 或 `objectKey`;刷新时用 `editor_project_resource` / `editor_asset` 行恢复临时生成请求所需图片源。生成成功后仍保存该快照,只是渲染时由 `generatedLayerId` 锚定到成品图层而不重复显示灰色占位框。`generationInputs.references` 是用户可见输入快照中的行级索引,只允许保存 `{ title, label, refType, refId }`;生成接口只接收提交前临时状态解析出的 `objectKey` 或资源 ID;Data URL、Blob URL 和 signed URL 不进入请求体,不进入资源 / 素材元数据。图层展示尺寸不再作为独立 `Size` 真相保存,刷新与新建图层均按 `Resolution`(`originalWidth/originalHeight`)原分辨率显示。图层组第一版是画布内布局语义,不单独建表。 +- 资源表保存资源和素材级元数据,其中 `editor_project_resource.asset_kind` 是资源默认类型;图层位置、层级、分组选中所需 ID、groupId 和可空 `assetKindOverride` 保存在画布布局。legacy 布局 JSON 是混合数组:普通图层按 `layerId/resourceId/assetKindOverride` 保存,生成器占位和生成器对话框按 `itemType: "generation-dialog"` 保存;结构化模式下 `asset_kind_override` 必须是 `editor_canvas_layer` 的 typed 列,不得塞回 `item_json`。普通图层的新保存不再把资源默认 `assetKind` 或 `generationInputs` 写入布局;刷新时从 `editor_project_resource` 恢复资源默认类型,再与图层覆盖合并为 `effectiveAssetKind`,不得把合并结果反写为新的资源或覆盖。旧布局中的 `assetKind` 只用于迁移:与资源默认类型相同则归一为无 override,不同则迁移为图层 override;资源尚无默认值且允许补齐时先补资源默认值,不制造新的资源行。生成器快照必须包含生成器 ID、模式、提示词、参数、参考图、状态、占位框位置和可选 `generatedLayerId`;角色、图标等纯色抠图生成器的前端用户路径不保存或恢复 `screenColor` / `segModel`,同源重绘也不再从 `generationInputs.fields` 恢复 `抠图背景色` 或 `抠图模型`;宣发素材生成器还必须保存并恢复 `publicationWorkflowId`、`publicationGameInfo` 和 `publicationReferences`,避免刷新后生成卡片字段或参考图丢失。生成成功后仍保存对话框快照,只是渲染时由 `generatedLayerId` 锚定到成品图层而不重复显示灰色占位框。V2 `generationInputs` 沿用现有 JSON 列,不新增表或字段:顶层为 `{ version: 2, action, fields, references }`,`fields` 只保存 `{ id, title, value }`,其中 `value` 为 `string | number | boolean`;`references` 只保存 `{ id, title, label?, refType, refId }`,`refType="project-resource"` 和 `refType="asset"` 仅作为当前画布图层的稳定匹配键,媒体类型从匹配图层获得,不据此额外查询资源行或素材库。提交前对面板值只归一一次,同一份归一值同时进入生成请求与 `fields/references`;Data URL、Blob URL、signed URL 和 `objectKey` 均不进入元数据。面板直接上传的引用即使为生成请求创建了工程资源,也不作为可长期恢复的素材库或画布引用。图层展示尺寸不再作为独立 `Size` 真相保存,刷新与新建图层均按 `Resolution`(`originalWidth/originalHeight`)原分辨率显示。图层组第一版是画布内布局语义,不单独建表。 - 图片类、生成视频和音频结果除作为 `editor_project_resource` 和画布图层保存外,还要写入账号级 `editor_asset` 素材库;该写入由生成 BFF 在请求携带 `assetFolderId` 时完成。角色、图标图集、UI 提取和角色动作等多产物任务把实际产生的 provider 原始输出及后处理结果分别入库:所有条目沿用 `character`、`icon-spritesheet`、`character-animation` 等真实类型,provider 原始输出承载任务模型成本,后处理派生产物阶段成本为 0。后台素材查询以最终产物为父行、每个中间产物为可展开的独立子行,分页只计算父任务;手动重拆图集保留独立 `taskId` 用于存储隔离和日志排障,通过私有 provenance 从服务端生成账号素材的 source resource、asset object 或 Object Key 取得可信来源任务,并把它写入 `groupTaskId`,不信任客户端可提交的 resource `taskId/assetKind`;跨项目复用后仍可通过稳定媒体引用找回来源。没有可信来源的新拆分显式归到自身任务,不走历史资源链回溯。每个手动切片同时写入 `groupTaskExpectedAssetCount`,全部切片落库后写独立 cohort 完成事实;后台 read model 只让同一根任务的一个已完成拆分批次并入原图集父项,用户后来删除单片不会让批次脱组,部分失败批次和后续重复拆分批次按各自真实任务分页,避免残缺批次抢占根任务、单组无限增长或素材丢失。历史行在项目资源仍存在时兼容回溯,删除项目资源前只固化直接受影响行的真实来源字段,有界展示 ID 不反写数据库。`GENARRATIVE_EXTERNAL_GENERATION_MODE=queue` 下,画布图片、改图、图标素材、UI 素材提取、角色动作、视频、音效和背景音乐生成都先返回 `queueState`,前端轮询 `/api/runtime/external-generation/jobs/{jobId}` 到完成后重新读取项目快照;`inline` 或无项目上下文时才使用响应中的 resource / asset 快照做本地落画布兜底,不再把同一生成结果二次调用素材创建接口。生成请求失败、inline 完成或 queue 任务终态完成 / 失败后,右上角泥点 chip 必须通过 `/profile/dashboard` 回读余额,不做本地乐观扣减。生成视频会单独抽取首帧封面写入 `thumbnailSrc`,素材栏和拖回画布时沿用该封面作为 poster。 - 生成面板不展示资源名称输入,默认使用原有自动编号;提示词输入保持统一可见边框。图片生成、快速编辑、重绘、修改图片、视频、音频、角色、图标、UI / 自定义规范、角色动画和宣发素材描述统一直接使用公共 `AutoGrowTextArea`,不经 `PlatformTextField` 的 textarea 分支;`AutoGrowTextArea` 负责尺寸、溢出及统一的 disabled 光标 / 透明度反馈,画布内生成面板统一由 `image-canvas-editor__generation-prompt` 承载边框、背景、padding、圆角和 focus chrome,独立修改图片弹窗继续使用 `image-canvas-editor__generate-prompt`,用途专属类只追加最小高度等差异,不得新建第三套 textarea chrome。两套 chrome 均通过 `--auto-grow-scrollbar-radius` 与内部滚动条共用圆角;WebKit 轨道按该圆角上下内缩,滑块在透明横向 gutter 内保持浏览器原生长度比例,拉至顶部或底部不得越过输入框边界。Enter 继续输入换行而不提交表单。内部命名契约仍使用可选 `assetLabel`,最大 80 字符并在提交时 trim;历史状态或内部调用携带非空名称时,同一个名称必须贯穿 `assetLabel`、`canvasCompletion.title`、项目资源、账号素材和本地兜底图层,刷新后不得退回模板名。图标图集与角色动作请求同样兼容该字段,中间原图使用主名称加固定后缀;图标拆分素材按全连通域视觉顺序自动命名为 `素材 N`。 - 画布 Agent 会话按“SpacetimeDB 元数据 + OSS 消息正文”存储:`editor_agent_conversation` 只保存 `conversationId/projectId/ownerUserId/title/messagesObjectKey/deleted/createdAt/updatedAt` 等会话元数据;消息正文整体保存为私有 OSS JSON 文档 `editor-agent/{conversationId}.json`。消息文档单对象上限为 2 MiB,同一会话的消息追加和工具结果回填由 api-server 按 `conversationId` 串行化,避免“读 OSS → 改消息 → 写 OSS”并发覆盖。前端只通过 api-server BFF 读取和发送会话,不直接读写 SpacetimeDB,也不直接读写 OSS。 @@ -172,7 +177,7 @@ - 默认选择模式;底部工具栏能切换工具;中键拖拽和 Space 临时抓手都能平移画布。 - 拖拽图片或生成占位框接近其它图片 / 生成占位框边缘、中心或等距分布位置时显示吸附线,并保存吸附后的最终布局。 - 生成图片点击后显示画布内 `Image Generator` 占位框和跟随占位框的生成输入框,生成失败保留占位和输入状态,生成成功后在占位位置创建真实图层,并让输入框继续跟随该生成图。 -- 选择 `1K / 2K` 或切换比例后,占位框在待生成和生成中阶段都必须立即显示对应目标像素尺寸;从普通图片、角色、图标图集或 UI 设计图再次改造时同样适用,完成落图前后不得从默认 1K 框跳变为 2K 成品。 +- 选择 `1K / 2K` 或切换比例后,占位框在待生成和生成中阶段都必须立即显示对应目标像素尺寸;从普通图片、角色、图标图集或 UI 设计图进入改造时同样适用,完成落图前后不得从默认 1K 框跳变为 2K 成品。 - 普通图片、角色和图标面板显示 `像素艺术` 勾选项并正确提交 / 恢复 `style: "none" | "pixelArt"`;其它生成或编辑面板不显示该选项。旧 payload、未知字符串、不支持 `kind` 和非字符串输入分别按本方案约定的兼容或错误语义处理。 - `pixelArt` 输出 Alpha 只包含 `0 / 255`;普通图片和角色先完成 Lanczos 交付尺寸归一,再由 snapper 使用 nearest 把逻辑网格恢复到同一输入尺寸,规整后不得再次执行尺寸插值。成功和后处理失败两条路径都不得比 `none` 增加 OSS PUT、项目资源、账号素材或画布 item,逻辑低分辨率图不得出现在 OSS 或响应资源快照中。 - 生成中的占位图聚焦后支持键盘 `Delete` / `Backspace` 删除,不新增可见删除按钮;删除后对应异步回写必须按生成器 ID 判空并丢弃,不能把已删除素材重新落回画布。音乐 / 音频生成占位和已生成音频图层同样必须支持键盘删除。 @@ -195,7 +200,7 @@ - 图片选中后的浮动工具栏按钮顺序固定为:快速编辑、分割线、裁扩按钮、去除背景按钮、完美像素按钮、UI设计图专属提取素材、角色图专属生成动画、分割线、重绘、下载按钮。完美像素只对当前静态栅格图层一键执行,按钮在请求期间按 layer id 进入 disabled / busy,首个 await 前用同步 ref 抢占,连续点击不得重复提交;完成后保留源图并在右侧显示派生 PNG,明确失败的占位保留错误且释放 busy。该路由是 unsafe POST 且不得配置自动重放;纯校验、排队或预算等明确未进入结果持久化的响应可直接失败,transport、网关、abort、客户端超时或 `details.resultPersistenceStarted = true` 属于未知结果,必须按下列 durable operation 与 GET-only 契约收口。该图层的素材类型保存在途时(`persistingAssetKindLayerIds`)完美像素按钮同样必须 disabled / busy,并在 handler 里用同步 ref 二次拦截——请求同时携带 `assetKind` 与 `sourceResourceId`,本地类型已改而资源尚未落库时两者不一致,后端 `resolve_editor_pixel_art_snap_asset_kind` 直接返回 `400`,只留下需要手动清理的失败占位。这与相邻的拆分图集按钮共用同一套门禁,但保存态的无障碍名称必须区分(完美像素用 `完美像素等待素材类型保存`),否则 `icon-spritesheet` 图层上两个按钮会同时叫「素材类型保存中」。 - 裁扩通过画布边界拖拉完成,不再展示四边数值输入;默认自由比例,选择固定比例后拖拉边界保持对应比例,完成后在原素材旁边新增裁扩结果图层,扩展区域透明填充。去除背景调用同源 BFF `POST /api/editor/images/background-removals`;父流程解析并校验私有 OSS object key 后只调用一次唯一内部 `bgfilter-worker` 的 complex 链路,子 worker 负责签发 600 秒 URL、`N / Q` 限流和最多两次顺序 provider attempt,complex 失败不接入 fallback,成功二进制返回后仍由父流程完成最终持久化。有项目上下文时先在画布创建关闭面板的去背景生成占位,完成后由后端通过 `canvasCompletion` 把新 project resource 写入该占位并返回快照,无占位上下文时才用新的 project resource 引用替换当前图层。画布任务侧栏按“排队/生成中”和“已完成”分页,生成中排在排队前,生成中耗时从任务开始时间戳实时计算,排队中不计时;进行中任务只显示阶段文本和已用时,不显示百分比;完成态生成任务副标题显示用户提示词并单行截断;点击任务只聚焦对应画布内容,不激活生成面板或改变任务顺序,聚焦时必须预留图片上方工具栏、底部工具栏和可见生成对话框空间。UI设计图的提取素材必须先进入红框素材框选状态,默认启用矩形框选,右侧框选工具与快速编辑统一且可再次点击取消启用态,当前启用工具按钮必须保持高亮。素材提取面板必须在素材下方,使用与生成新素材一致的面板宽度和底部模型 / 按钮样式,提示语显示 `使用框选工具框选你希望从画面中提取的素材`,并展示按原图坐标准确裁剪的框选区域截图预览、固定模型 `gpt-image-2`、左下角计划规格 `1:1·1K/2K` 和 `提取 · N泥点` 按钮,不显示额外取消按钮;点击素材和面板以外的画布区域即退出 UI 素材提取。至少框选一个区域后才可提交,前端把红色轮廓绘入原图后固定走 `gpt-image-2` 和自动决策纯色背景素材提取提示词。透明处理及拆分正常完成时,透明 spritesheet 和拆分素材都按后端快照保留为画布图层;透明处理失败时仅原图作为主结果,既不要求透明图也不要求切片;透明图成功但拆分失败时保留整张透明图并展示拆分告警。三种完成结果都以后端项目快照为准。 - 2026-08-04 修订:完美像素前端已经让素材刷新退出 verdict,持久化 operation 请求快照与 `pending-confirmation`,并接入刷新后的 GET-only 恢复;是否成功只能由下面的项目 GET 正向证据判定。 -- 完美像素以 durable operation 为提交边界:请求账本 `perfectPixelOperation = { version: 1, kind: "perfect-pixel", operationId, taskId, request, submittedAt, reconcileUntil }` 存在**本机** `perfectPixelOperationStore`(owner + project 双键的 localStorage),其中 `operationId` 等于规范化 dialog id、`taskId` 固定为 `pixel-art-snap-{operationId}`,`request` 是稳定源引用解析完成后的完整 `EditorPixelArtSnapInput`,`submittedAt / reconcileUntil` 构成 **从 POST 发出时刻起算**、不得被 POST 回包续期的 75 秒整链绝对窗口——pre-POST flush 没有整体上限,锚在它之前会让窗口在请求发出前就烧光。项目布局里只保留 `perfectPixelOperationId` 标记,用于把这类占位与队列型占位区分开。**标记与账本的寿命必须对齐**:账本在收口那一刻清除,因此收口态占位(带非空 `generatedLayerId` 且状态不是 `generating` / `pending-confirmation`)既不再写出标记,也不得因为「有标记、没账本」被判成无效——服务端完成 completion 时只做字段级改写、从不摘标记,任何忽略这一点的判据都会把每一次成功判成失败。账本读不到(换设备、清缓存、隐私模式、配额写满)时,**未收口**占位收口成可删除的失败态,不得据此阻断用户删除或重做。完美像素 dialog id 使用跨标签随机 identity,不能复用每个标签页都会从 1 开始的局部计数器。inline 源图以该 identity 作为稳定 upload ID,只执行 object-only 上传,不等待 signed URL;快照不得包含 Data URL、Blob URL 或 signed URL。POST 前仍需 `await` 一次 best-effort 布局保存(服务端要求占位此前已持久化,见上文 409 条款),但保存冲突、鉴权失败或重试耗尽**不再让 POST 为零**——账本已在本机、请求可被追溯,客户端照常发出,由服务端裁决。人工重试只能原样重放该快照与同一 operation,不得重新 placement、上传、读取当前图层字段或暗中换 identity;快照缺失、损坏或与 dialog / project / task / completion 不匹配时失败关闭。首次提交或人工重试在途期间若 owner、project 或组件生命周期已经变化,旧响应的素材写入、项目应用、提示与对账副作用必须全部忽略,不能把前一账号的结果写入当前账号状态。 +- 完美像素以 durable operation 为提交边界:请求账本 `perfectPixelOperation = { version: 1, kind: "perfect-pixel", operationId, taskId, request, submittedAt, reconcileUntil }` 存在**本机** `perfectPixelOperationStore`(owner + project 双键的 localStorage),其中 `operationId` 等于规范化 dialog id、`taskId` 固定为 `pixel-art-snap-{operationId}`,`request` 是稳定源引用解析完成后的完整 `EditorPixelArtSnapInput`,其新请求配方固定为 `version: 2 / action: image.perfect-pixel / fields: []`。账本外层版本不升级;嵌套 generationInputs 以共享严格白名单恢复,V2 完整接受稳定 `id`、有限数字、布尔值和可省略 `label`,旧 `{fields,references}` 请求继续按原形恢复以维持历史 fingerprint,未知键或畸形 V2 失败关闭。`submittedAt / reconcileUntil` 构成 **从 POST 发出时刻起算**、不得被 POST 回包续期的 75 秒整链绝对窗口——pre-POST flush 没有整体上限,锚在它之前会让窗口在请求发出前就烧光。项目布局里只保留 `perfectPixelOperationId` 标记,用于把这类占位与队列型占位区分开。**标记与账本的寿命必须对齐**:账本在收口那一刻清除,因此收口态占位(带非空 `generatedLayerId` 且状态不是 `generating` / `pending-confirmation`)既不再写出标记,也不得因为「有标记、没账本」被判成无效——服务端完成 completion 时只做字段级改写、从不摘标记,任何忽略这一点的判据都会把每一次成功判成失败。账本读不到(换设备、清缓存、隐私模式、配额写满)时,**未收口**占位收口成可删除的失败态,不得据此阻断用户删除或重做。完美像素 dialog id 使用跨标签随机 identity,不能复用每个标签页都会从 1 开始的局部计数器。inline 源图以该 identity 作为稳定 upload ID,只执行 object-only 上传,不等待 signed URL;快照不得包含 Data URL、Blob URL 或 signed URL。POST 前仍需 `await` 一次 best-effort 布局保存(服务端要求占位此前已持久化,见上文 409 条款),但保存冲突、鉴权失败或重试耗尽**不再让 POST 为零**——账本已在本机、请求可被追溯,客户端照常发出,由服务端裁决。人工重试只能原样重放该快照与同一 operation,不得重新 placement、上传、读取当前图层字段或暗中换 identity;快照缺失、损坏或与 dialog / project / task / completion 不匹配时失败关闭。首次提交或人工重试在途期间若 owner、project 或组件生命周期已经变化,旧响应的素材写入、项目应用、提示与对账副作用必须全部忽略,不能把前一账号的结果写入当前账号状态。 - 完美像素 unknown-result 的 verdict 只来自项目 GET,POST 响应体不得直接判成功:找到唯一稳定 task resource 且 dialog 已收口、结果层精确指向该 resource 时为 `Applied`;resource 存在且 dialog 不存在时为 `DialogMissing`,结果只在素材库;dialog 仍 generating(包括匹配 resource 已先可见)或 dialog 不存在且无匹配 resource 时继续有界轮询;resource 与 dialog / layer 出现原子事务不可能产生的错配时保持待确认并提示冲突,禁止自动 POST。首个 GET 立即执行,此后退避不超过 5 秒;即使绝对窗口已过期也必须读取一次。GET 的绝对 deadline 从进入 `requestJson` 起覆盖鉴权恢复、所有 attempt 和响应体读取;不能把只覆盖响应头的 `timeoutMs` 当成整次读取上界。单次 deadline 到期按一次读取失败处理,随后由轮询返回 `pending`,首次提交必须进入 `finally` 释放 dialog ownership 与图层锁,hydrate 恢复必须清理 recovery controller。`refreshAssetLibrary` 只在终态后 best-effort 触发,不进入轮询 deadline、`Promise.all` 或成功判断,同步 throw、异步 reject 和永久挂起均不得阻塞。轮询到期或 GET 失败后 dialog 转 `pending-confirmation`,保留 operation 与请求快照并释放页面 busy,不得伪装成普通失败或声称素材已保存。 - 完美像素恢复只对账、不重新执行:项目 hydrate 后识别带有效 operation 账本的 `generating` / `pending-confirmation` dialog,只按稳定 task/resource 做 GET-only 轮询,绝不 POST、重新上传、重新准备来源或为了恢复而先写布局;owner/project 切换、卸载或更高 revision 到来时旧轮询结果不得生效。恢复还必须覆盖**孤儿账本**——本机有账本、布局里却没有对应占位,这正是「POST 已发、布局尽力保存没落盘、标签页关闭」的结局。孤儿走一次确定性的读(不轮询):`reconcileUntil` 只描述「结果可能还在飞」,而孤儿来自已经消失的会话,按它短路会让这条兜底分支在唯一的目标场景(稍后重开,必然晚于 75 秒)下永不生效。收口口径:`dialog-missing` 提示结果只进素材库;`applied` 静默刷新素材库(读到的就是当前权威状态,结果本就在眼前);未落库完全静默。三者都清账本,读失败不清——那是「不知道」而非「知道没有」。新写入的 v1 operation 固定使用 75 秒跨度;为兼容第一批和滚动升级中的旧标签页,hydrate 仍接受跨度及未来时钟偏差不超过 240 秒的旧 v1 journal。若旧 `submittedAt` 位于可接受的未来区间,先把它规范化到当前时间,再把 `reconcileUntil` 压到 `min(持久截止, 规范化 submittedAt + 75 秒, 当前时间 + 75 秒)`;写回形状必须继续满足 `reconcileUntil >= submittedAt`,确保下次 hydrate 仍保留同一 identity。带 operation 的占位不受 `requiresLiveSession` TTL 清理(系统不替用户删),但**用户主动删除始终允许**——两者是不同的事。TTL 只兼容完全没有 operation 标记字段的历史 inline 孤儿,字段存在但内容损坏时必须保留并失败关闭,清理 legacy 孤儿时必须同时更新 `project.layers` 与 `project.canvas.layers`。恢复到期仍持久保持 `pending-confirmation`,只有用户明确点击重试才进入 exact replay。滚动升级期间从布局读到的 legacy 内联账本必须在 hydrate 后一次性迁入本机账本——布局里的内联快照会在下一次保存时被剥成标记,不迁移就再没有任何路径能补写,部署那一刻仍在途的操作会在第二次加载失去 exact retry identity。**已知限制**:本会话的「已观察」记账按 operation 记录,用于避免同一次 operation 被并发轮询两遍;若用户删除仍在处理中的占位、待原请求收口后再 `Ctrl+Z` 撤销删除,复活的占位在**当前会话内**不会被重新对账,会一直显示处理中。刷新页面即自愈(hydrate 会按标记与账本重新判定),且用户随时可以再删一次。该记账同时承担「并发保护」与「本会话已驱动过」两种语义,要根治需先拆开这两件事;在此之前不接受以「删除路径显式清观察记录」等跨 hook 埋线的方式局部绕过。 - legacy inline 占位的本会话归属必须由同一份封装 ownership 管理:同步 Set 在首个 await 前完成 `claim`,保证到期判定即时可见;`claim / release` 仅在 membership 真变化时推进 React 可观察的 version,`release` 即使发生时 dialogs 与 callbacks identity 都不变,也必须立即唤醒到期 effect 重新判定。禁止重新暴露可变 Set ref 或直接修改 `.current`,React 不会因为 ref 内容变化而重跑 effect。 diff --git a/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md b/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md index 201117451..b2716c956 100644 --- a/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md +++ b/docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md @@ -34,7 +34,7 @@ - 对话与事件:窗口固定使用 `project-supervisor + autonomous-game-build`,继续复用 active Session、External Runner、持久 conversation、流式回复、same-run steer、工具确认与用户追问。以 `/` 开头的输入必须继续走现有内置命令解析,例如 `/preview` 只能生成 `preview.start` 确认卡,不得作为自主构建任务投递给 Supervisor。game-chat 的自主链路中,Supervisor 持久化意图后只有 `code-prototype` 是主 Agent;它可能临时委派一个受限美术 child,后者只写 `assets/**`,回执返回同一主 Run 后由主 Agent 接入与验收。界面聚合当前 Supervisor 父 run、单主 Agent 及其直接美术 child 的最新原始事件,按时间倒序稳定去重并标注 Agent;默认显示 4 条,可展开至最新 20 条。原始 `summary / detail` 仍只作 Runtime 状态投影,不直接写入 conversation。需要进入聊天的事件必须由 Rust 同步生成唯一 `eventId` 与安全 `publicText`;前端只按这两个字段形成独立 assistant 消息,无 `eventId`、空 `publicText`、legacy 事件和内部 tool / Provider / Runner 协议一律忽略。 - 公开消息硬门:模型仍负责 Supervisor / 专业 Agent 回复的业务语义,Runtime 不根据 tool 或 Provider 事件自行补写业务结论;但用户直接投递的 Project Supervisor 根后台任务必须先落为不可执行的 `preparing / public-status-pending`,再以 `runtime-public-status-*` 稳定 message ID 把“任务已接收,正在启动处理”写入项目 conversation,成功后才转为 `pending / queued`;恢复预检只读,只能在验证到同 run accepted 消息后把该任务临时分类为可恢复,真实 resume 持有 Agent 锁后才可持久提升为 `pending / queued`;写入失败则落为 `failed / public-status-write-failed`,不得继续执行。这些 Runtime 公开状态只供 UI 展示,prompt 构建器必须按稳定前缀排除。根 Supervisor 通过正式失败 / 预算耗尽收束或 game-chat 绝对硬期限进入 reconciliation 时,必须在 task、event、state 等其它终态投影之前先幂等写入一条脱敏、用户可理解的失败消息;专业 Agent 命中该全局硬期限时,也必须通过权威 Run Profile 和根 task 将同一根终态写入项目 conversation,同时保留 child 私有 Session 状态;状态文件本身写坏也不能导致零公开结果。当前 Runtime 自称根 agent/run 时,其 session 和两个 parent 字段必须与权威根 task 一致;任一身份冲突必须失败关闭,不得以另一 session 派生第二条项目终态。前端把该前缀识别为 Runtime-owned,同秒时排在触发它的 Supervisor 用户消息之后,不二次持久化;仅根 Supervisor 的 `turn.started / turn.failed / turn.budget_exhausted` 只保留在 Runtime 详情和进度投影中,不能再生成第二条聊天消息,专业 Agent 的公开启动事件仍可见。该硬门不改变 final-reply 的唯一性;非 Supervisor 专业 Agent 的失败消息继续留在对应 Agent Session,不把私有诊断写进项目 conversation。 - 启动恢复和续跑边界:本条取代上一条中“只有 accepted 才可恢复”的窄口径。若进程在 Supervisor 用户消息已持久、accepted 未持久之间崩溃,只读 preflight 可以把该 `preparing` 识别为可恢复,但不改写 task/conversation;真实 resume 持有 Agent 锁后必须先幂等补写 accepted,再提升为 `pending / queued`。用户消息或 accepted conversation 已落盘而辅助审计失败时,以 conversation 为公开真相继续入队,不留下“已接收但永不执行”的任务;根终态首次公开写入的瞬时失败必须在终态投影后用相同 message ID 重试。receipt / isolated-join 等带 parent 的 Supervisor continuation 不再另写 Session 终态,只保留单一后端公开事件;`runtime-task-*` 与 `runtime-public-status-*` 共享同 run 的不透明关联摘要,秒级时间戳下多个连续任务必须按实际 run 对应的 `user -> accepted -> terminal` 顺序交错展示。 -- Supervisor 进度播报:聊天消息流内保留且只保留一条当前 run 的 Runtime-owned 播报卡,由客户端从 manifest 任务图、Supervisor 结构化计划、`loopIteration`、当前动作、直接委派专业 Agent 及其持久事件确定性整理;显示当前轮次、任务 / 计划进度、活跃 Agent、最近试玩与静态检查、返工决定、代码修改和截图检查证据。同一 run 原位更新,切换 run 时替换,不调用额外模型、不追加持久 conversation,也不改变最终 assistant 回复的唯一性;任意详情必须有界且不展示绝对路径、Provider 元数据或内部指纹。 +- Supervisor 进度播报:聊天消息流内保留且只保留一条当前 run 的 Runtime-owned 播报卡,由客户端从 manifest 任务图、Supervisor 结构化计划、`loopIteration`、当前动作、直接委派专业 Agent 及其持久事件确定性整理;显示当前轮次、任务 / 计划进度、活跃 Agent、最近试玩与静态检查、返工决定、代码修改和截图检查证据。同一 run 原位更新,切换 run 时替换,不调用额外模型、不追加持久 conversation,也不改变最终 assistant 回复的唯一性;任意详情必须有界且不展示绝对路径、Provider 元数据或内部指纹。运行详情弹窗在项目或 run 身份切换的同次提交中同步关闭,不能由延迟 effect 关闭用户在新 run 状态可见后刚打开的弹窗。 - ready-task 启动活性:`background_task.queued`、`autonomous_ready_task.scheduled`、Runner heartbeat 或执行锁已移交都不等于 child 已启动。实际持有执行权的 Runner 必须在释放项目写锁后同步写入 child 的 running task、`turn.started` 与 started journal,再把已启动 state 和 per-Agent 执行锁交给已确认开始轮询的独立 execution worker;同步启动或 worker 接管失败时,要在仍持有执行锁期间依次把 child 和 manifest Graph 节点明确落为 failed,再释放锁并让 parent 收到调度错误。`autonomous_ready_task.scheduled` 只作诊断审计,其写入失败不能阻断 durable child 启动;external client 只 wake Runner,不在客户端抢占执行。Supervisor 进度卡通过 durable `startedAt`(旧 Run 从完整 task journal 恢复,最新 task-record fallback 保持 0)显示真实持续时间,并以父 Run 与当前关联专业 Agent 的最大事件时间计算运行态活跃度:运行超过 5 分钟无新事件时显示“运行中 · 疑似停滞”和静默时长;等待用户、等待确认、Provider retry、视觉资产、进程会话、pausing 与 paused 不误报。父 Run terminal 后,持续时间冻结在父 Run 自身最后活动,不随 child 晚到收口事件增长。消息时间统一校验为 JavaScript 可表示的 Date;越界值显示“时间未知”且不写无效 `datetime`。实时回复只显示 response stream 自己的 `updatedAt`,缺失时同样显示“时间未知”,不能借用其它 Runtime 活动时间或随前端时钟漂移。该提示只提供可观测性,不改变 Runtime/manifest 正式状态。 - ready-task manifest 漂移:父 Supervisor 必须分别判断“能否调度新节点”和“是否存在必须等待的工作”。派生视觉需要父规划修复时不再调度新 child,但当前最新且活跃的根 Run 下,只要存在确定性 runId、scheduler source、正确父绑定且 durable journal 为 queued/running 的 ready child,父 Run 就保持 `waiting-for-manifest-tasks`,不能因旧 hydration 快照把 manifest running 覆盖成 pending 而提前 fixed-graph-stalled。game-chat child 可在相同严格身份下容忍 pending 漂移;正式产物、Canvas、revision、`game.static_smoke` 与 `preview.validate` 门禁不放宽。GUI/CLI、旧父 Run、终态、确认/用户输入/reconciliation、伪造绑定或非确定性 runId 全部失败关闭;更新根 Run 后旧 child 不得继续维持新 DAG 或投影完成。 - Supervisor 持久决策与单主条件美术:game-chat 的关键词、用户是否报告“美术未接入”、占位状态和当前资产探测只形成 `advisoryOnly=true` 的补充上下文,不得直接重置 Graph、预完成美术节点、选择复用/生成分支或继承历史试玩类型。当前根 Run 没有持久化 Supervisor 决策时,scheduler 不启动任何 child;Supervisor Provider 只通过 auto-safe 的 `agent.route_manifest` 提交 `game-chat-workflow-decision.v2`:`intentSummary` 是 Supervisor 自行理解并持久化的用户意图,`strategy=audit-existing-first` 只是固定安全执行策略,两者不得混用。此动作不能审计、生成、委派或替代后续判断,也不能把整体视觉重做解释成整套美术的强制重生成;成功后 Runtime 只启动唯一 `code-prototype` 主 Agent。升级恢复时严格校验 v1 sidecar 的旧 fingerprint,并从完成合同绑定的有效任务恢复 `intentSummary`;旧 `code-director` coverage/route 只作为迁移输入,不作为当前完成证据,必须由同一根 Run 的 `code-prototype` 重新 `asset.list` 后原位替换为单主合同。确定性 `code-prototype` Run 仅兼容已知 canonical task 文本版本,其余 task/binding/root 身份继续失败关闭;升级前已运行的 fixed-graph 美术 child 不再具备任何 mutation 或生图权限。主 Agent 必须以当前正式资产、Canvas 登记、私有图集合同、四张语义切片和 art manifest 判断真实缺口;完整覆盖时直接接入,不得生成或扣费。只有可证实缺失 `art-spec` 或核心 spritesheet 时,主 Agent 才可对相应 `art-director` 或 `art-asset-plan` 建立一条 durable 委派;每次最多一个活跃美术 child,child 仅可写 `assets/**`,不得修改 `game/**` 或接入/验收游戏。若两个槽位都缺失,必须先完成 `art-director`,由同一主 Run 认领其 `EvidenceReady` delivery 后,才能委派依赖规范图的 `art-asset-plan`;失败或未就绪 delivery 不得消耗不可重试的图集委派槽位。主 Agent 认领必要回执后继续同一 Run 完成素材接入、原玩法语义校验、`game.static_smoke` 与桌面/移动 `preview.validate`。绝对硬截止对嵌套美术 child 继续核验 `root -> code-prototype -> agent-delegate` 完整身份并保留未知外部生成的 reconciliation 证据。Runtime 只负责校验根/父子身份、当前 revision、路径、Canvas 登记、缺口/路由 fingerprint、写入范围及完成证据;纯“继续”仍走既有正式 continuation 合同,普通美术措辞不得借用更老项目的具体试玩场景。不得以增加 loop 预算、伪造 revision、机械改写 manifest 或重放历史图片 action 代替 Supervisor 决策和程序侧审计。 diff --git a/docs/technical/【技术方案】图片画布游戏场景生成链路-2026-08-04.md b/docs/technical/【技术方案】图片画布游戏场景生成链路-2026-08-04.md index 539daf15c..56dae069e 100644 --- a/docs/technical/【技术方案】图片画布游戏场景生成链路-2026-08-04.md +++ b/docs/technical/【技术方案】图片画布游戏场景生成链路-2026-08-04.md @@ -1,6 +1,6 @@ # 图片画布游戏场景生成链路 -更新时间:`2026-08-04` +更新时间:`2026-08-08` ## 1. 目标 @@ -151,7 +151,20 @@ prompt = 后端完整 Prompt - `prompt`:后端最终提交给图片 Provider 的完整场景 Prompt。 - `actual_prompt`:Provider 返回的 actual/revised Prompt。 - `asset_kind`:`scene`。 -- `generation_inputs_json`:画面内容、视觉风格、自定义画风和用户参考图引用。 +- `generation_inputs_json`:可恢复场景生成器的 V2 配方;`assetKind = scene` 只表示素材类别,不直接授予“改造”能力。 + +场景新产物统一保存以下稳定执行契约: + +```text +version = 2 +action = scene.generate +fields[].id = prompt | stylePreset | customStyle | model | aspectRatio | imageSize +references[].id = reference +``` + +`title` / `label` 仅用于中文展示,不参与路由或恢复。前端提交快照和再次提交使用同一组规范化后的模型、比例与清晰度。请求兼容字段 `generationInputs.fields[]` 保留中文展示 `title`,同时把规范值字符串化,例如 `{ title: "视觉风格", value: "anime" }`;它不携带 V2 的 `id / action / version`。后端忽略客户端提供的执行字段和引用 provenance,按请求顶层 `sceneContent / stylePreset / customStyle / model / aspectRatio / imageSize` 重建 V2 `fields`,并按本次真实 `referenceImageSrcs` 只保留安全的 `references[id="reference"]` 槽位,再由现有 owner-scoped 引用重建链补齐 `refType/refId`。 + +`scene.generate` 必须同时进入已知 action 与可改造 action allowlist,并由 action 级 decoder 和场景 composer 恢复路径读取。`assetKind = scene` 不能替代上述 capability 判断。`72f268e0` 之后、V2 上线之前产生的场景数据只在 `assetKind === "scene"` 且 legacy `fields` 含展示标题“画面内容”时走既有 legacy 恢复和告警;“画面内容”不得加入全局 legacy 可复用标题列表,其他素材即使带同名字段也不能获得改造入口。 ## 8. 验收 @@ -164,3 +177,5 @@ prompt = 后端完整 Prompt - 展示价、入队价、实际扣费和资源成本一致。 - 任务复用现有等待、失败、轮询、资源入库和画布添加逻辑。 - 场景产物以 `assetKind = scene` 持久化,画布图层右上角显示“场景”标签,不降级为“未知”。 +- 新场景产物保存 `scene.generate` V2 配方,点击“改造”后恢复画面内容、风格、自定义风格、模型、比例、清晰度和当前画布中仍存在的参考图,再次提交仍保存规范 V2。 +- V2 场景、窄化匹配的历史场景显示“改造”;非场景素材即使 legacy 字段标题为“画面内容”也不得显示“改造”。 diff --git a/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md b/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md index 4245c5bb3..7a16e1d33 100644 --- a/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md +++ b/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md @@ -448,7 +448,7 @@ Responses 的终态载荷既是工具调用的恢复源,也是正文的恢复 - 运维入口固定为 `node scripts/spacetime-normalize-editor-character-actions.mjs --database --server-url `;默认全量 dry-run,`--apply` 时每批先 dry-run,再用返回的 batch SHA-256 写入,四个 scope 完成后从头执行零匹配 / 零 blocker 复核。迁移完成后 api-server、后台、前端和外部 helper 只读取正式字段,不再包含 legacy fallback。新建动作资源 / 素材若在 `generationInputs` 提交旧运行字段,或正式帧包含 `frameIndex`,api-server 与 SpacetimeDB storage 均失败关闭;其它素材的任意生成输入不受动作专属门禁影响。 - 普通画布 layer 的持久化和前端响应不包含顶层 `mediaType`;渲染类型只由资源 / 素材 `assetKind` 在前端派生。`generationInputs.references[*].mediaType` 是生成参考输入契约,不属于画布 layer legacy 字段,迁移和响应清洗不得递归删除。 -## 新增编辑器 `assetKind` 接入清单(2026-08-03) +## 新增编辑器 `assetKind` 接入清单(2026-08-03,2026-08-08 增补) 新增前先确定稳定字符串、主媒体含义、专属元数据、来源血缘、下载产物、复用规则和公开边界。只修改新类别实际经过的链路,不机械改动全部结构。 @@ -464,6 +464,7 @@ Responses 的终态载荷既是工具调用的恢复源,也是正文的恢复 | 后台契约 | `server-rs/crates/shared-contracts/src/admin.rs`、`api-server/src/admin.rs` | 后台素材查询或精选审核需要展示该类别时返回完整正式字段,不能只返回封面或首帧 | | TypeScript 客户端类型 | `src/services/image-editor/editorProjectClient.ts`、`apps/admin-web/src/api/adminApiTypes.ts` | 接收后端 camelCase 字段,不定义第二套业务真相 | | 画布 layer / layout 映射 | `src/components/image-editor/` | 新类别能进入画布时贯通资源加载、素材点击 / 拖放、保存恢复、复制、撤销、删除和导出;layout 只保留恢复副本 | +| “改造” capability 与 V2 配方 | `src/components/image-editor/ImageCanvasGenerationInputsModel.ts`、`ImageCanvasGenerationModel.ts`、`ImageCanvasGenerationDialogModel.ts` 及生成提交链 | 新类别接入时必须显式判断是否允许恢复原生成器。可改造的生成类别必须定义稳定 V2 `action`、`fields[].id`、`references[].id`、action 级 decoder、面板恢复路径、改造 allowlist 和往返测试;确定性或不可重放类别即使保存 action 也不得进入改造 allowlist。`assetKind` 本身不能授予 capability;legacy 兼容只能按类别和旧配方特征窄化,并覆盖正反例,禁止把显示标题扩成全局路由键 | | 素材库与 renderer | `src/components/image-editor/` | 由 `assetKind` 派生图片、视频、音频或序列 renderer,并实现正确缩略图、预览和下载行为 | | 后台媒体 renderer | `apps/admin-web/src/components/AdminEditorAssetMedia.tsx` | 后台需要预览时复用现有组件;列表只加载最小媒体,弹窗再按需加载完整媒体 | | 精选 read model 与 renderer | `api-server`、`src/components/creation-home/` | 新类别允许公开时贯通正式字段、卡片、弹窗和损坏数据行为 | @@ -473,6 +474,7 @@ Responses 的终态载荷既是工具调用的恢复源,也是正文的恢复 其它注意事项: - `assetKind` 是数据库、Rust DTO 和对外 JSON 的唯一媒体类别真相;不要新增或返回并列的 `mediaType`。前端 renderer 可以保留内部派生类型,但不得回写后端。 +- `assetKind` 只描述素材类别,不代表生成配方可执行或允许“改造”;新增类别必须单独完成上表的 capability 决策和验收。 - 只是新增分类或 renderer 且现有媒体字段足够时,不改 schema。只有必须跨刷新、复用、审核或公开保留的数据才新增类别专属字段。 - legacy 数据必须先通过有界、可审计、带 dry-run/hash/apply 门禁的数据库迁移收口;迁移后的 api-server、mapper、主站、后台和画布只读取正式字段,不保留运行时 fallback。 @@ -674,6 +676,7 @@ Responses 的终态载荷既是工具调用的恢复源,也是正文的恢复 - 生成链路的新 resource ID 必须按 owner + operation kind + operation ID + stable slot 派生,只能在统一结果 procedure 中创建或完整比较;普通“同媒体复用”不得把它替换成另一随机 ID。 - 完美像素资源:处理成功时只允许一个最终 PNG 对象对应一个新 project resource;源图已有正式 project resource 时,`source_resource_id` 指向该资源;结果尺寸与最终 PNG 一致,不写逻辑低分辨率图、诊断图或前后对比图。成功时同时创建一个同源 `editor_asset`,请求省略素材文件夹时落入默认素材文件夹;completion 因权威 dialog 的删除已先持久化而跳过画布写回时,这两类已确认资源无需回滚。 - `generation_inputs_json` 包络契约:`fields` / `references` 是图片信息读取的用户可见生成输入快照;顶层允许保存后端内部结果扩展。现有 `screenColorHex` 保存实际背景色,角色、图标图集和 UI 图集抠图派生资产使用 `mattingProvider` / `mattingModel` 保存实际成功的处理后端与模型。BgFilter 保存本次 `seg_model`,阿里云通用抠图保存 `Aliyun Matting / segment-common-image`,本地键色保存 `Genarrative Local / screen-color-keying`。同源画布 BFF 的角色、图标和 UI 请求由前端自动提交 `screenColor=auto` 与默认 `segModel=birefnet`,其中 `segModel` 是不可由用户选择的请求控制字段,不进入 `generationInputs`;`background_mode` 和 `cross_check` 只属于 api-server 到 worker 的内部 RPC。External OpenAPI 不开放 `segModel`。上述内部结果字段不写入 `fields`,普通用户(包括素材 owner)与匿名公开读取均不得取得;普通用户响应还必须省略素材顶层 `provider` 和内部处理 `model`,但保留正常用户可见 `model` 与其他合法的顶层功能字段。后台管理和服务端审计可读取原始值。过滤只作用于普通用户 / 公开响应边界,不修改素材或精选快照,因此历史数据无需迁移。 +- `generation_inputs_json` V2 可执行改造契约沿用现有 JSON 列,无 SpacetimeDB schema 迁移或存量回填:顶层 `version=2` 和稳定 `action` 确定生成器,`fields[].id` 确定参数,`references[].id/refType/refId` 确定引用参数与稳定指针;`fields[].value` 保持 `string | number | boolean` 类型,`title` / `label` 只作展示。前端改造只在当前画布图层中匹配引用并取得运行时媒体类型,不新增 owner-only 工程资源 / 素材库 resolver;面板直接上传引用和已移出画布的引用均不恢复。可重新选择的引用由前端留空槽位、提示并交给提交门禁校验;必须依赖原 `source` 图层才能构造面板的 action 仍按 capability 保留改造按钮,source 缺失时在点击恢复路径显示明确错误并拒绝,运行期来源变化时再次校验。有效 V2 的引用缺失不得触发 legacy adapter。Owner resource / asset payload 在普通用户元数据清理后保留这些执行字段;匿名公开素材 payload 暂不返回 `generationInputs`,避免公开接口沿用 owner 可执行配方 DTO。独立裁扩、手动去背景和手动图集拆分是确定性派生操作,新结果 `generation_inputs_json = null`;原生成任务内的自动透明化 / 拆分后处理可保留同任务的原生成输入。 - 普通用户生成结果契约:图片、图标图集、视频、音频和角色动画的完成响应与新建画布图层均不返回或写入生成 provider;项目资源、素材、精选和 Agent 紧凑结果使用同一读取边界。真实 provider 只保留在持久化、tracking / tracing 和后台管理原始审计中。该规则针对生成供应商元数据,不改变直传票据等必须由客户端执行的存储协议字段。 - 普通用户错误契约:手动去背景和角色动作透明化的原始服务端错误可能包含 BgFilter、分割模型或 provider 细节;Owner HTTP 响应与外部任务状态必须按 job kind 返回稳定业务文案,原始错误只保留在任务记录、tracing 与后台审计。 - 索引:`by_editor_project_resource_project_id`、`by_editor_project_resource_owner_user_id`。 diff --git a/docs/【编辑器】生成类面板Lovart统一改造方案-2026-06-17.md b/docs/【编辑器】生成类面板Lovart统一改造方案-2026-06-17.md index 45bab1d1f..dd3017764 100644 --- a/docs/【编辑器】生成类面板Lovart统一改造方案-2026-06-17.md +++ b/docs/【编辑器】生成类面板Lovart统一改造方案-2026-06-17.md @@ -72,6 +72,7 @@ - 生成规范类图片固定使用 `16:9·2K · gpt-image-2`。这三个参数在面板底部沿用可编辑参数按钮的胶囊样式展示,但控件保持禁用不可点击,不提供比例、尺寸或模型修改入口。 - 宣发素材的 `游戏首图`、`详情五图`、`运营海报` 固定使用 `gpt-image-2`。面板底部只显示禁用态 `gpt-image-2` 模型胶囊和生成按钮,不出现 `nanobanana2` 选项;后端收到 `publication-material` 旧请求时也必须强制归一为 `gpt-image-2`。 - 图片快速编辑保留一个提示词输入框,并展示与常规图片生成一致的比例 / 尺寸和模型选择;提示词 placeholder 为 `你希望素材如何修改?`,提交按钮显示 `修改`,不展示额外参考图控件。打开面板时优先继承原图关联生成器记录的模型、比例和尺寸;没有关联生成器时使用图层模型,并按原图真实分辨率推导比例和尺寸;模型缺失或已不受支持时回落到当前默认图片模型。切换模型后只展示该模型支持的参数,不兼容的当前值回落到该模型默认值,按钮泥点按选定模型和尺寸同步刷新。提交时必须同时传递 `model / aspectRatio / imageSize`;后端按模型选择 provider 协议:`nanobanana2` 使用 `generateContent + inline_data`,`gpt-image-2` 使用 `/v1/images/edits` multipart,不能把 nanobanana 模型 ID 发往 GPT edits 端点。 +- 图片改造入口也要保持同样约束:恢复历史参数时优先使用 `generationInputs` 中保存的模型、比例和尺寸,失败回退到当前关联参数后再映射为实际 `size`,并一并回传到编辑请求;`canvasCompletion` 的落位尺寸也应与实际输出目标分辨率一致,避免使用源图尺寸伪造改造结果的参数。 - 不再在底部常驻展开全部可选项。 ## 泥点显示 @@ -114,8 +115,9 @@ - 视频 / 角色 / 角色动作 / 音效 / 背景音乐待生成占位的角标同样按 viewport 反向缩放,不随画布缩放变小。 - 已生成角色图、角色动作图或其它生成结果图被点击时只选中图层并收起已有生成输入框;重绘、快速编辑和生成动画面板必须由对应工具栏按钮或右键菜单显式打开。 - 角色图层打开“生成动作”后再点击“改造”,必须重新打开角色形象生成器;动作生成对话框只把角色图层作为输入来源,不得被识别为该角色图层自身的来源生成器。 -- 角色动作结果图层点击“改造”时,必须通过 `sourceResourceId` 找回原角色图层并重新打开角色动作生成器;关联原角色已不存在时应显示明确提示,不得无响应或降级成图片生成器。 -- 恢复已生成图层的来源生成器时,角色、动作、规范、图标、UI、宣发、视频、音效和背景音乐必须按 `assetKind / mediaType` 恢复对应面板;去背景或快速编辑产生的派生 `quick-edit` 对话框不得遮住图层原有来源生成器。 +- 角色动作结果图层点击“改造”时,V2 必须通过 `references[id="source"]` 找回原角色图层,legacy 数据才允许以 `sourceResourceId` 回退;关联原角色已不存在时应显示明确提示,不得无响应或降级成图片生成器。 +- `改造` 覆盖图片、规范、角色、图标、UI、宣发、游戏场景、视频、音效、背景音乐、角色动作和生成型图片编辑。有效 V2 只按 `action + fields[].id + references[].id` 恢复,引用只匹配当前画布图层;面板直接上传引用和已移出画布的引用不恢复。V2 不新增后续版本,读取时统一经 action 级 runtime decoder 原地收紧:已存在但未知、非法、已下线或与当前模型能力不兼容的参数统一回落到该 action 当前默认值,历史 Veo 也回落到当前默认视频模型;图片比例 / 尺寸按回落后的模型联动校验,角色动作帧数 / 时长按完整档位成对校验。发生参数回落时显示明确告警,再次提交和新快照只使用规范值并继续保存为 `version: 2`。服务端以实际媒体时长覆盖 V2 配方时必须保留 `fields[id="durationSeconds"]`,并写入归一后的有限数值,不能改写为无 `id` 的 legacy 展示字符串。可重新选择的引用缺失时打开面板、留空槽位并提示,提交门禁继续校验必填槽位;必须依赖原 `source` 图层才能构造面板的 action 也始终按 capability 保留改造入口,source 缺失时点击后显示不可替换原因并拒绝改造,运行期间来源变化时仍必须复检并拒绝。有效 V2 不得因引用缺失降级到 legacy。生成型图片编辑 V2 中已持久化的附加 `reference` 应恢复到可见参考槽,并让再次提交的模型、比例、尺寸、像素尺寸、参考图和新快照保持一致;普通快速编辑仍不得提交未展示的隐藏参考图。视频快速编辑必须把实际送入请求的源视频同步保存为 `references[id="videoReference"]`,不能只依赖 `sourceResourceId`;视频 V2 同步保存并恢复 `webSearchEnabled`。历史对话框和 legacy 数据保留 `assetKind/mediaType`、标题别名、资源尺寸 / 模型 / 时长 / `sourceResourceId` 回退,并对默认值恢复显示告警。V2 结构水合必须完整保留 `version/action`、字段与引用 `id`、有限数字、布尔值和无标签引用;一旦出现 `version` 或 `action` 却不满足 V2 合同,必须失败关闭,禁止降级成 legacy。 +- 配方元数据不等于改造 capability。完美像素、手动去背景、裁扩、手动图集拆分以及图标 / UI 自动切片分别保存 `image.perfect-pixel`、`image.remove-background`、`image.crop-expand`、`spritesheet.split`,统一使用 `fields: []`;有正式来源行时只保留不可编辑的 `references[id="source"]` 权威来源,没有正式行时保留空引用。这四个确定性 action 永不显示或执行“改造”,历史 `pixel-art-snap-*` 结果也按 task identity 拒绝改造。整张生成图集仍保留原生成 action;生成任务内部自动抠图仍属于同源后处理,不提升为独立用户 action。 - 任何会移除画布图层的入口,包括删除、右键剪切和素材库删除关联素材,都必须同步清理该图层关联的生成面板和派生状态,不得在保存或刷新后恢复成孤立占位。 ## 画布保存 @@ -192,7 +194,7 @@ - 图片快速编辑底部左侧展示比例 / 尺寸组合选择,右侧展示模型选择和 `修改` 按钮;原图或红框序号标注图作为 `sourceImageSrc` 直接编辑,不展示额外参考图条。图标与图集素材不展示快速编辑入口,图标规范仍可快速编辑。 - 快速编辑打开后画布自动缩放平移到原图完整展示,并让面板位于原图下方且不遮挡原图;原图右侧出现竖向矩形 / 椭圆 / 画笔自由框选按钮。进入快速编辑不默认启用框选,点击工具启用并保持高亮,再点同一工具取消;完成框选后画布红色细框显示连续序号,输入框同步追加 `对N号红色圈选框里的内容做以下修改:`。 - 快速编辑提交前保留提示词里对原图的 `原图`、`当前图片`、`当前图` 或 `图1` 引用,不再改写成 `图N`。 -- 快速编辑提交给后端时只把原图或已绘制红框和序号的标注图作为 `sourceImageSrc`,不提交隐藏的 `referenceImageSrcs`。 +- 普通快速编辑提交给后端时只把原图或已绘制红框和序号的标注图作为 `sourceImageSrc`,不提交隐藏的 `referenceImageSrcs`;从生成型图片编辑 V2 快照恢复且在面板中可见的附加参考图除外。 - 生成中的占位图聚焦后可用 `Delete` / `Backspace` 删除;删除后异步结果不再落回画布,也不显示额外删除 UI。 - 快速编辑不创建生成中占位图;提交后当前面板显示修改中,异步结果只允许回填到源图。 - 生成视频 / 角色形象 / 角色动作 / 音效 / 背景音乐新建后,画布占位空白样式和右上角标签均与对应生成类型一致,不再统一使用图片占位 icon。 diff --git a/docs/【编辑器】画板音乐生成入口设计-2026-06-18.md b/docs/【编辑器】画板音乐生成入口设计-2026-06-18.md index b90258ce3..bdfffecb6 100644 --- a/docs/【编辑器】画板音乐生成入口设计-2026-06-18.md +++ b/docs/【编辑器】画板音乐生成入口设计-2026-06-18.md @@ -356,8 +356,8 @@ idle - generated 私有音频资源播放前必须通过 `/api/assets/read-url` 换签;画布卡片不得直接把 `/generated-*` 或 generated OSS 私有地址交给 `