跳转至

无限二创:图片节点工具设计

文档定位:唯一详设(product/canvas 仅一句概述)。对应实现:src/infinitecanvas/;架构总览:infinitecanvas.md

1. 目标

在无限画布的图片节点上提供两类编辑能力:

  1. 本地交互编辑:裁剪、标注、画笔(用户可控、即时反馈)
  2. AI 图生图编辑:抠图、去水印、扩图、智能分层(参考原图 + 固定提示词)

xyflow 仅负责节点壳(选中、工具栏挂载、缩放);图像编辑为业务自研。

2. 工具一览

工具栏项 action 技术 结果落点
裁剪 crop Konva 弹层 新建图片节点(源右侧 + 连线)
标注 editElements Konva(矩形 / 箭头 / 文字) 同上
画笔 brush Konva 笔触层 同上
抠图 cutout POST /api/canvas/generate 同上
智能分层 smartLayer 同上(并行两次) 新建主体层 + 背景层(源右侧竖向紧贴 + 各连线)
扩图 outpaint 同上 同上
去水印 removeWatermark 同上 同上
下载 / 放大 download / expand 浏览器 不改节点
加入 Agent joinAgent AI 输入框引用 打开 Agent

3. Konva 交互编辑

3.1 交互形态

点击裁剪 / 标注 / 画笔 → 全屏遮罩弹层(非节点内嵌),避免与 xyflow 拖拽冲突。

  • 组件目录:src/components/canvas/image-editor/
  • 会话状态:useImageToolActions 中的 konvaEditor
  • 弹层挂载:CanvasProvider 内渲染 ImageKonvaEditorModal

3.2 完成写回

  1. Stage / 离屏 Canvas 导出 dataURL
  2. 在源节点右侧新建图片节点(处理中占位),并连线 源 → 结果
  3. uploadDataUrlAsAsset → OSS(直传失败可走 backend 代传)
  4. registerCanvasAsset + updateImageNodeWithSize 填充结果节点
  5. 关闭弹层;失败时删除占位结果节点并 alert

不修改源图片节点。

3.3 跨域读图

展示用 <img src> 不要求 CORS;Konva/Canvas 读像素需要可导出位图:

  • resolveEditableImageUrl:先 CORS 直载,失败则 POST /api/canvas/assets/proxy-image 得 data URL

4. AI 抠图 / 去水印 / 扩图

4.1 池与上游

画布默认模型统一来自多模态池(text_to_multimodal);三项工具仍走生图 API(输出图片),不是 chat/completions:

  • API:POST /api/canvas/generate(默认池 text_to_multimodal
  • mode: single(单张参考图)
  • imageUrls: [源图 URL]
  • prompt: 固定中文提示词(utils/ai-image-tool-prompts.ts
  • 上游仍为 Nonlinear 图生图;池内需配置可生图的多模态模型

4.2 前端流程

runImageToolAction
  → 右侧新建结果节点(isProcessing + processingLabel)+ 连线
  → ensureHttpImageUrl(blob/data 先上传)
  → runAiImageTool → requestImageGeneration
  → pickGeneratedImageUrl → updateImageNodeWithSize(结果节点)
  → 失败则删除占位结果节点
  • 不修改源图片节点
  • 不再回退本地 Canvas 演示效果
  • RunningHub 占位接口可保留,但不参与这三项调度

4.3 提示词原则(产品侧)

工具 意图要点
抠图 保留主体边缘,去背景,勿加水印文字
去水印 去 Logo/角标,保持主体与光影,勿改构图
扩图 四周扩展并自然补全,风格一致
智能分层 · 主体 提取主体,去背景
智能分层 · 背景 去除主体并自然补全背景

具体文案以代码中常量为准,便于迭代而不改接口。

4.4 智能分层(双结果)

与抠图等同池生图,但一次操作并行两次 requestImageGeneration

runImageToolAction(smartLayer)
  → 右侧新建「主体层」「背景层」占位节点 + 各连线
  → 竖向紧贴排布(主体在上)
  → runSmartLayerSplit(并行 subject / background 提示词)
  → 各自 persist + updateImageNodeWithSize
  → 尺寸回写后再竖向紧贴
  → 失败则删除两个占位节点
  • 不修改源图片节点
  • smartLayer 计入 AI_IMAGE_TOOL_ACTIONS(单结果列表)

5. 与「我的资产」边界

操作 是否进「我的资产」
资产弹窗「本地上传」 是(只入库,不建节点)
画布上传建节点 / Konva 保存 / AI 结果 否(仅画布 canvasAssets / 节点 imageUrl
右键「存到我的资产」 是(手动)

6. 关键文件

路径 职责
components/canvas/ImageNodeToolbar.tsx 工具栏 UI
context/canvas/hooks/useImageToolActions.ts 调度
components/canvas/image-editor/* Konva 弹层与 Stage
utils/ai-image-tool.ts / ai-image-tool-prompts.ts AI 调用与提示词
utils/image-tool-processor.ts CORS 安全读图
utils/image-generation-api.ts 生图 HTTP
backend/api/canvas/router.py generate / assets/proxy-image

7. 测试要点与验收记录

7.1 自动化(单元)

用例 文件 结果
AI 工具识别与提示词 tests/unit/utils/ai-image-tool.test.ts 通过
runAiImageTool mock 生图 / dataURL 先上传 同上 通过
runSmartLayerSplit 并行双层 同上 通过
resolveEditableImageUrl 本地地址 tests/unit/utils/image-tool-processor.test.ts 通过
fitImageToStage 缩放 tests/unit/utils/fit-image-to-stage.test.ts 通过

7.2 接口冒烟(本地 backend + storage)

接口 预期 结果
GET /api/canvas/config 200 + 模型列表 通过
POST /api/canvas/upload 200 + url/object_key 通过
POST /api/canvas/assets/proxy-image 200 + data_url 通过
POST /api/canvas/generate(single + 参考图) 200 返回生成图 前端已发起;本地直连探测约 90s 超时(上游耗时/配额需单独确认)

7.3 浏览器功能验收(localhost:5174,JWT 注入)

步骤 预期 结果
登录后进入 /canvas 进入画布 通过
图片节点上传 出现图片节点与工具栏 通过(OSS 直传 CORS 失败时回退 /upload
点击「裁剪」 Konva 弹层(标题裁剪 / 取消 / 完成) 通过
弹层加载原图 proxy-image 后出现 Stage canvas 通过
点击「完成」 弹层关闭并写回节点 通过
点击「标注」「画笔」 对应弹层打开,可取消 通过
点击「抠图」 节点显示「抠图中…」并请求 /generate 通过;当前测试账号配额为 0,最终 alert「配额不足」——链路与错误提示正确

说明:

  • OSS Bucket 未配浏览器 CORS 时,控制台会出现直传 Failed to fetch;代传与 proxy-image 已覆盖编辑路径。
  • AI 三项出图还依赖画布生图池上游与用户配额;无配额时应明确提示,而非卡在处理中(当前会弹出配额错误)。

8. 非目标(本阶段)

  • 不新建专用「图片编辑」后端资源接口(可后续薄封装计费/审计)
  • 不替换整个 xyflow 画布为 Konva
  • 不实现专业级蒙版/图层系统(标注为轻量矢量叠加)