734 lines
20 KiB
Markdown
734 lines
20 KiB
Markdown
## Context
|
||
|
||
当前生图反馈主要依赖 `WorkZone`:
|
||
|
||
- 提交时在画布中插入固定尺寸的卡片
|
||
- 卡片内部展示标题、状态、步骤列表与删除/隐藏按钮
|
||
- 自动插入完成后再删除卡片
|
||
|
||
这种设计适合表达工作流存在,但不适合表达“图片对象即将落入画布”的过程。问题主要有:
|
||
|
||
- 过程对象抢占画布注意力,打断创作空间
|
||
- 固定卡片高度在单图场景下留白过多
|
||
- 生图反馈的核心不是步骤明细,而是“会落在哪、会长成什么比例、现在到了哪个阶段”
|
||
- 当前状态同步分散在 `AIInputBar`、`useWorkflowSubmission`、`useTaskWorkflowSync`、`useAutoInsertToCanvas` 中,后续继续在 `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`
|
||
|
||
拥有:
|
||
|
||
- 从 `taskQueueService`、`workflowCompletionService`、插入事件读取状态
|
||
- 根据 `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. `useImageGenerationAnchorController` 把 `Task.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` / `CardGenerator` 的 `foreignObject + 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 不直接认 `taskQueueService`、`workflowSubmissionService` 或 `workflowCompletionService` 的原始对象
|
||
- 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` 给非生图路径,降低迁移风险
|