Files
TrueGrowth/openspec/changes/archive/2026-04-29-update-image-generation-anchor-feedback/design.md

20 KiB
Raw Blame History

Context

当前生图反馈主要依赖 WorkZone

  • 提交时在画布中插入固定尺寸的卡片
  • 卡片内部展示标题、状态、步骤列表与删除/隐藏按钮
  • 自动插入完成后再删除卡片

这种设计适合表达工作流存在,但不适合表达“图片对象即将落入画布”的过程。问题主要有:

  • 过程对象抢占画布注意力,打断创作空间
  • 固定卡片高度在单图场景下留白过多
  • 生图反馈的核心不是步骤明细,而是“会落在哪、会长成什么比例、现在到了哪个阶段”
  • 当前状态同步分散在 AIInputBaruseWorkflowSubmissionuseTaskWorkflowSyncuseAutoInsertToCanvas 中,后续继续在 WorkZone 上叠交互会进一步放大复杂度

Goals

  • 让生图过程在画布中表现为“对象正在诞生”,而非“任务卡片正在执行”
  • 优先利用提交时即可获得的几何信息,建立可信的一致性反馈
  • 在结果未返回前,通过锚点表达位置、比例与状态,而不伪装已知内容
  • 保留失败重试、详情追踪和恢复能力
  • 首版只覆盖 image 类型任务,不改造其他任务类型

Non-Goals

  • 不在首版统一视频、音频、文本、流程图等其他任务反馈
  • 不在首版默认引入强品牌化的传送门或粒子特效
  • 不依赖结果侧稳定返回 width/height 元数据来决定首版锚点几何

Decision

1. 生图改为 Generation Anchor

生图提交后,系统在预期插入位置创建轻量锚点,而不是默认创建大面积 WorkZone 卡片。

锚点的职责只有四类:

  • 表达“已受理”
  • 表达“预期落点”
  • 表达“预期几何外壳”
  • 表达“当前阶段 + 主动作”

步骤详情、历史记录、错误细节默认不在画布中展开。

2. 三档几何策略

Frame-first

若提交时已存在:

  • targetFrameId
  • targetFrameDimensions

则锚点直接继承 Frame 外壳,在该容器内部完成生成、显影与结果接管。

Size-first

若不存在 Frame但已存在明确的 size,则使用 size 作为锚点外壳的比例来源。该策略与当前插入逻辑一致,因为首版插入已经依赖 parseSizeToPixels(size) 进行几何推导。

Ghost-anchor

若既无 Frame 又无稳定比例信息,则仅创建轻量出生点锚点,不伪装为完整图片框。待结果返回后,再从出生点平滑 morph 为真实图片。

3. 用户态状态机

生图锚点统一使用以下用户态:

  • submitted
  • queued
  • generating
  • developing
  • inserting
  • completed
  • failed

状态解释:

  • submitted: 用户点击后,任务已创建,输入条需要立即确认
  • queued: 请求已受理但尚未进入明显执行
  • generating: 模型正在生成结果
  • developing: 结果已返回,正在准备插入或执行轻显影
  • inserting: 正在把真实图片插入画布并与锚点连续过渡
  • completed: 图片已稳定落位,锚点将短暂停留后淡出
  • failed: 保留失败节点,允许重试或查看详情

用户态文案规范

首版默认文案应保持“对象正在诞生”的语义,而不是暴露底层工作流术语:

  • submitted: 已提交,等待执行
  • queued: 请求已受理,等待执行
  • generating: 图片正在生成,请稍候
  • developing: 结果已返回,正在准备显影
  • inserting: 正在放入画布
  • completed: 图片已稳定落位
  • failed: 生成失败,请重试

允许在特定交互瞬间使用更细的提示文案,例如:

  • 主线程回退时:请求已受理,正在转入本地执行
  • 用户点击重试时:正在重新触发,请稍候

但这些文案必须仍然映射回上述统一用户态,不允许新增平行状态名。

4. 详情层收敛

锚点默认只展示:

  • 当前阶段文案
  • 轻进度
  • 一个主动作

详细步骤、错误明细、历史版本应进入任务详情层,而不是在画布中展开完整 WorkZone

职责边界

Generation Anchor 负责:

  • 受理确认
  • 预期落点
  • 预期几何外壳
  • 当前阶段
  • 轻进度
  • 当前上下文中的恢复动作

任务详情层负责:

  • 完整步骤列表
  • 错误详情与调试信息
  • 历史记录与重试历史
  • 批量结果管理
  • 非画布主路径的任务追踪

画布中的锚点不应默认展开完整步骤列表,也不应承载需要持续滚动阅读的信息。

5. 渐进迁移

首版仅对 image 类型启用 Generation Anchor

  • image:默认启用 Anchor
  • 其他类型:继续使用现有 WorkZone 或现有任务反馈

这样能避免一次性改造所有任务语义,也能降低对现有任务同步链路的影响。

State Mapping

首版建议由一个统一的 view model 负责把现有信号映射成锚点状态:

  • 提交态信号:
    • expectedInsertPosition
    • targetFrameId
    • targetFrameDimensions
    • size
  • 任务执行信号:
    • Task.status
    • Task.progress
    • Task.executionPhase
  • 后处理信号:
    • workflowCompletionService
    • 自动插入开始/完成事件

映射规则示例:

  • 任务创建后但未进入稳定进度更新:submitted
  • TaskExecutionPhase.SUBMITTING: queued
  • Task.progress 持续推进:generating
  • 任务已成功但后处理未完成:developing
  • 图片正在落入画布:inserting
  • 后处理完成并已插入:completed
  • 任意执行或插入失败:failed

UI Model

建议新增面向 UI 的锚点模型:

  • anchorType: frame | ratio | ghost | stack
  • phase
  • position
  • dimensions
  • progress
  • title
  • subtitle
  • primaryAction
  • secondaryAction
  • transitionMode

该模型只表达画布中锚点需要展示什么,不直接持有完整工作流对象。

Integration Notes

  • 输入条负责第一层反馈:点击后立即显示“已提交”
  • 画布锚点负责第二层反馈:表达位置、比例、阶段
  • 任务详情层负责第三层反馈:步骤明细、失败原因、历史记录
  • 现有 WorkZone 首版可作为非生图路径保留,不与生图锚点混用

Architecture Alignment

Runtime Topology

首版推荐的运行时拓扑如下:

  1. AIInputBar

    • 负责生图提交入口
    • 产出提交期几何上下文
    • 调用 ImageGenerationAnchorTransforms.insertAnchor
    • 触发输入条“已提交”反馈
  2. with-image-generation-anchor

    • 负责 generation-anchor 元素的画布承载与渲染
    • 管理 anchor 元素的插入、更新、删除与命中
  3. useImageGenerationAnchorController

    • 负责把原始信号映射为 UI model
    • 不直接写画布,只输出“应该显示什么”
  4. useImageGenerationAnchorSync

    • 负责监听任务与后处理事件
    • 负责按 taskId / workflowId 找到 anchor
    • 负责把 controller 产出的状态回写到画布元素
  5. useAutoInsertToCanvas

    • 负责真实图片落图
    • 负责驱动 developing -> inserting -> completed
    • 负责在插入完成后让 anchor 收口
  6. WorkZone

    • 生图首版不再作为默认反馈
    • 仅保留给非生图任务与旧路径兼容

Ownership Matrix

AIInputBar

拥有:

  • generationType === 'image' 分流决策
  • expectedInsertPosition
  • targetFrameId
  • targetFrameDimensions
  • requestedSize
  • currentImageAnchorIdRef
  • 输入条首段反馈

不拥有:

  • anchor 运行中阶段推进
  • 刷新恢复
  • 自动插入后的收口
  • 全局任务同步

Drawnix

拥有:

  • 注册 with-image-generation-anchor
  • 挂载 useImageGenerationAnchorSync
  • 在 board ready / task storage ready 后进行恢复协调

不拥有:

  • 提交期几何推导
  • 真实插入行为本身

useImageGenerationAnchorSync

拥有:

  • taskQueueServiceworkflowCompletionService、插入事件读取状态
  • 根据 taskId / workflowId 查找 anchor
  • 调用 transforms 更新 anchor 元素

不拥有:

  • 具体 JSX 渲染
  • 业务提交入口

useAutoInsertToCanvas

拥有:

  • 结果 URL 到画布对象的真实插入
  • 插入前后的 anchor 状态推进
  • 插入失败到失败态的回写

不拥有:

  • anchor 初次创建
  • 输入条即时反馈

Data Contracts

Submission Context

建议定义一个单独的提交期上下文对象,避免多个模块重复各自取值:

  • workflowId
  • taskIds
  • expectedInsertPosition
  • targetFrameId
  • targetFrameDimensions
  • requestedSize
  • prompt
  • count
  • createdAt

来源:

  • AIInputBar

用途:

  • 创建 anchor
  • 恢复定位
  • 插入时兜底寻找几何上下文

Anchor View Model

建议定义一个稳定的 UI model避免 JSX 直接依赖任务/工作流对象:

  • anchorType
  • phase
  • progressMode: 'indeterminate' | 'percent' | 'steps'
  • progressValue
  • title
  • subtitle
  • showFrameShell
  • showRatioShell
  • showGhostPulse
  • canRetry
  • canOpenDetails
  • transitionMode: 'fade' | 'morph' | 'frame-fill' | 'stack-expand'

Runtime Sequence

提交阶段

  1. AIInputBar 解析生图请求
  2. 推导 expectedInsertPosition / Frame / size
  3. 构造 submission context
  4. 创建 generation-anchor
  5. 提交任务到工作流 / 任务队列

执行阶段

  1. taskQueueService 发出 taskCreated / taskUpdated
  2. useImageGenerationAnchorSync 读取任务状态
  3. useImageGenerationAnchorControllerTask.status / progress / executionPhase 映射为 submitted / queued / generating
  4. transforms 更新 anchor 元素

后处理阶段

  1. 任务成功返回结果 URL
  2. workflowCompletionService 发出后处理开始
  3. anchor 切到 developing
  4. useAutoInsertToCanvas 开始插入
  5. anchor 切到 inserting
  6. 图片落图成功后anchor 切到 completed 并收口

失败阶段

  1. 任务失败或插入失败
  2. useImageGenerationAnchorSync / useAutoInsertToCanvas 写回失败态
  3. anchor 保留失败节点
  4. 用户可原位重试或打开详情

Implementation Breakdown

1. 元素层拆分

首版不建议在现有 workzone 元素上继续追加“生图锚点”模式,而是新增独立的画布元素类型:

  • generation-anchor

原因:

  • workzone 语义是“工作流过程面板”
  • generation-anchor 语义是“结果出生点”
  • 两者在几何、交互密度、恢复逻辑和删除时机上都不同
  • 生图首版需要与非生图路径并行存在,独立元素类型更利于渐进迁移

插件实现建议优先参考现有的轻元素模式,而不是继续沿用 with-workzone 的重型编排模式:

  • 参考 withCard / CardGeneratorforeignObject + React root + generator 范式
  • 参考 withAudioNode / AudioNodeGenerator 的“轻元素 + 实时状态渲染”模式
  • 若后续需要手动缩放,再单独增加 resize companion plugin参考 with-card-resize / with-audio-node-resize

不建议参考 withTool 的消息桥式重型模式,因为 anchor 不是一个 iframe 工具,也不应承担任务编排职责。

2. 建议新增文件

类型层

  • packages/drawnix/src/types/image-generation-anchor.types.ts

职责:

  • 定义 PlaitImageGenerationAnchor
  • 定义 anchorType
  • 定义用户态 phase
  • 定义提交期几何上下文与主动作字段

建议字段:

  • workflowId
  • taskIds
  • anchorType: 'frame' | 'ratio' | 'ghost' | 'stack'
  • phase
  • progress
  • requestedSize
  • expectedInsertPosition
  • targetFrameId
  • targetFrameDimensions
  • resultUrl
  • error
  • zoom

渲染层

  • packages/drawnix/src/components/image-generation-anchor/ImageGenerationAnchorContent.tsx
  • packages/drawnix/src/components/image-generation-anchor/image-generation-anchor.scss
  • packages/drawnix/src/components/image-generation-anchor/index.ts

职责:

  • 渲染 Frame Anchor
  • 渲染 Ratio Anchor
  • 渲染 Ghost-anchor
  • 渲染失败态与主动作

约束:

  • 只做展示,不直接 claim / resume / 查询任务状态
  • 不持有工作流恢复逻辑

插件层

  • packages/drawnix/src/plugins/with-image-generation-anchor.ts

职责:

  • 注册 generation-anchor 元素
  • 创建 foreignObject
  • 管理激活态边框与点击命中
  • 提供 transforms
    • insertAnchor
    • updateAnchorState
    • updateGeometry
    • removeAnchor
    • getById
    • getAnchorByTaskId
    • getAnchorByWorkflowId
    • getAllAnchors

边界约束:

  • 插件层只提供元素能力
  • 不直接订阅 taskQueueService
  • 不直接处理 workflow 恢复
  • 不直接决定 anchor 何时完成或消失

状态映射层

  • packages/drawnix/src/hooks/useImageGenerationAnchorController.ts
  • packages/drawnix/src/utils/image-generation-anchor-view-model.ts

职责:

  • 将提交期几何信号 + 任务状态 + 后处理状态映射为 anchor UI model
  • 统一决定:
    • anchorType
    • phase
    • progress
    • title/subtitle
    • primaryAction
    • transitionMode

关键原则:

  • UI 不直接认 taskQueueServiceworkflowSubmissionServiceworkflowCompletionService 的原始对象
  • UI 只认 ImageGenerationAnchorController 产出的 anchor view model
  • controller 是 image anchor 的单一状态源

建议保持该层为纯映射或轻控制器,不直接操作 DOM。

同步层

  • packages/drawnix/src/hooks/useImageGenerationAnchorSync.ts

职责:

  • 在 board 级别监听 taskQueueService
  • 在 board 级别监听后处理 / 自动插入事件
  • 在刷新恢复后重新同步 anchor 状态
  • 将更新写回 ImageGenerationAnchorTransforms

边界约束:

  • sync 只负责订阅和归一化原始事件
  • sync 不直接做业务判断
  • sync 允许重复事件、乱序事件和恢复事件
  • controller 负责幂等推进和去重

该 hook 应由 Drawnix 持有,而不是由 AIInputBar 或单个 anchor 组件持有。

3. 现有文件改动范围

AIInputBar.tsx

保留职责:

  • 判断当前是否是 image 类型提交
  • 计算 expectedInsertPosition
  • 解析 targetFrameId / targetFrameDimensions
  • 计算 requestedSize
  • 在提交时创建首个 anchor
  • 提供输入条“已提交”即时反馈

移出职责:

  • 不再负责生图默认大 WorkZone 创建
  • 不再通过 currentWorkZoneIdRef 驱动生图的完整生命周期
  • 不再直接订阅 taskQueueService.observeTaskUpdates()
  • 不再直接订阅 workflowCompletionService.observeCompletionEvents()
  • 不再直接基于 image 路径写 WorkZoneTransforms.update/remove

建议新增:

  • currentImageAnchorIdRef

并保留现有:

  • currentWorkZoneIdRef

作为非生图路径兼容。

useAutoInsertToCanvas.ts

新增职责:

  • 根据 taskId 查找 image anchor
  • 在“结果已返回但未插入”时切换到 developing
  • 在“开始插入”时切换到 inserting
  • 在“插入完成”时执行 anchor → image 的收口
  • 在失败时更新为失败节点

需要替换的当前耦合:

  • 现有 findWorkZoneForTask
  • 现有基于 WorkZone 的 expectedInsertPosition / targetFrameDimensions 读取
  • 现有生图完成后直接删除 WorkZone 的逻辑
  • 现有依赖 ai-generation-complete 解锁输入条的生图路径

drawnix.tsx

新增职责:

  • 注册 with-image-generation-anchor
  • 挂载 useImageGenerationAnchorSync

保留职责:

  • 现有 WorkZone 恢复与同步先只服务非生图路径

建议调整:

  • Drawnix 成为 image anchor 的全局恢复入口
  • 不再让 AIInputBar 持有刷新恢复或跨任务同步逻辑
  • 现有 restoreWorkZones、workflow sync、task queue sync 中的 image 逻辑应迁移到 anchor sync而不是继续直接写 WorkZone

useWorkflowSubmission.ts

建议调整:

  • 将 image 类型从“默认 WorkZone 同步路径”中分流出来
  • 保持文本、Agent、非生图任务继续复用现有逻辑

说明:

生图首版的主要状态源更应该是:

  • 提交期几何上下文
  • taskQueueService
  • 后处理 / 插入事件

而不是完整 workflow 步骤列表。

需要重点清理的耦合:

  • 当前把 workflow 事件直接同步到 WorkZone 的分支
  • 当前把 image 路径当成默认 workflow 面板处理的分支

with-workzone.ts / WorkZoneContent.tsx

首版策略:

  • 保留
  • 不扩展生图主路径
  • 仅服务非生图任务与兜底流程

后续可选策略:

  • 当 image anchor 稳定后,再把生图相关分支从 WorkZone 中逐步收缩或删除

4. 状态层职责划分

建议把状态划成三层:

提交期几何状态

来源:

  • AIInputBar
  • 选区 / Frame
  • size

职责:

  • 决定 anchor 初始位置与外壳类型
  • AIInputBar 唯一应长期保有的 image 特有语义

任务执行状态

来源:

  • taskQueueService
  • Task.progress
  • Task.executionPhase

职责:

  • 决定 submitted / queued / generating
  • 是任务生命周期的唯一执行真相,不直接决定最终收口

后处理 / 插入状态

来源:

  • workflowCompletionService
  • useAutoInsertToCanvas

职责:

  • 决定 developing / inserting / completed / failed
  • 用于避免“任务已完成但图片尚未真正落图”时提前收口

5. 单一状态源约束

对于 image anchor必须明确以下 ownership 约束:

  • AIInputBar 只拥有提交瞬间快照,不拥有持续运行态
  • WorkflowContext / workflowSubmissionService 只拥有 workflow 计划真相,不拥有 anchor UI 真相
  • taskQueueService 只拥有任务执行真相
  • workflowCompletionService 只拥有后处理真相
  • ImageGenerationAnchorController 拥有唯一的 anchor 运行态

任何直接绕过 controller 写入 anchor UI 状态的路径,都视为架构违规。

6. 迁移顺序

推荐按以下顺序推进,避免双系统互相打架:

  1. 新增 anchor 类型、plugin 与 view model
  2. AIInputBar 中仅对 image 路径创建 anchor
  3. useAutoInsertToCanvas 中接入 anchor 查找与完成态收口
  4. Drawnix 中增加 anchor 同步 / 恢复 hook
  5. 将 image 路径从默认 WorkZone 更新链中分流
  6. 手工验证稳定后,再考虑清理 WorkZone 中的 image 分支

7. 为什么不复用 WorkZone

不推荐直接把 WorkZoneContent 改成“生图锚点样式”的主要原因:

  • 仍会保留错误的元素语义
  • 仍会把大量工作流字段塞进画布元素
  • 仍会让生图依赖当前分散的 WorkZone 同步链
  • 会增加“单组件承担展示 + 恢复 + 兜底编排”的复杂度

独立元素类型虽然首版工作量更高,但长期边界更清晰。

Audit And Review Plan

代码层重点审计

  • 是否仍有 image 路径写入 WorkZoneTransforms
  • 是否存在 anchor 与 WorkZone 对同一 image 任务双写
  • 是否有组件直接读取 taskQueueService 并绕过 controller
  • 是否把恢复逻辑重新塞回展示组件
  • 是否在未知比例场景下错误创建完整比例框
  • 是否仍沿用 ai-generation-complete 作为 image 路径的唯一解锁信号

迁移层重点审计

  • AIInputBar 创建 image anchor 后,是否仍然默认创建 WorkZone
  • useAutoInsertToCanvas 是否还依赖 findWorkZoneForTask
  • drawnix.tsx 的 WorkZone 恢复逻辑是否误处理 image anchor
  • useWorkflowSubmission 是否仍将 image 事件同步到 WorkZone
  • useTaskWorkflowSync 是否仍把 image 任务 fallback 到 WorkZone
  • 是否仍存在多个 setTimeout(1500) 竞争删除同一 image 反馈对象

手工验证重点

  • 选中 Frame 的单图生图
  • 指定 size 的单图生图
  • 不指定 size 的单图生图
  • 同 prompt 多图生成
  • 失败后原位重试
  • 刷新页面后的锚点恢复
  • 自动插入完成后的收口行为

Code Review 必查文件

  • packages/drawnix/src/components/ai-input-bar/AIInputBar.tsx
  • packages/drawnix/src/hooks/useAutoInsertToCanvas.ts
  • packages/drawnix/src/drawnix.tsx
  • packages/drawnix/src/hooks/useWorkflowSubmission.ts
  • packages/drawnix/src/hooks/useTaskWorkflowSync.ts
  • packages/drawnix/src/plugins/with-workzone.ts
  • packages/drawnix/src/plugins/with-image-generation-anchor.ts
  • packages/drawnix/src/services/workflow-completion-service.ts
  • packages/drawnix/src/services/task-queue-service.ts

Risks

  • 若锚点状态映射继续分散在多个 hook 与组件中,会重新产生“多处同步”的维护问题
  • 若在未知比例时仍强制生成完整图片框,会造成锚点与最终结果不一致
  • 若首版同时覆盖多任务类型,会让锚点语义被稀释,增加复杂度

Mitigations

  • 首版限制在 image 类型
  • 将状态映射收敛到单一 view model / controller
  • 将几何策略明确为 Frame-first / Size-first / Ghost-anchor
  • 保留现有 WorkZone 给非生图路径,降低迁移风险