Infinite Canvas 前台(用户端)¶
源码:
src/infinitecanvas/产品域名:canvas.harness.local(ACK)/canvas.localhost(K3s) 本地开发:make canvas-dev→ http://127.0.0.1:5174
面向终端用户的图现 AI 前台:首页营销、无限二创画布、各 AI 工作台生图、我的资产库与账号中心。
技术栈¶
| 项 | 选型 |
|---|---|
| 框架 | React + TypeScript + Vite |
| 路由 | react-router-dom(src/App.tsx) |
| 无限画布 | @xyflow/react(CanvasPage / CanvasFlow) |
| 图片交互编辑 | konva + react-konva(裁剪 / 标注 / 画笔弹层) |
| HTTP | shared/api/backendHttpClient.ts(fetch + JWT) |
| 包管理 | pnpm(目录内 package.json) |
| 质量门禁 | make canvas-check:typecheck / build / test |
目录结构(要点)¶
src/infinitecanvas/
├── src/
│ ├── App.tsx # 路由表
│ ├── pages/ # 页面入口
│ ├── components/
│ │ ├── site/ # 顶栏导航
│ │ ├── canvas/ # 无限二创画布控件
│ │ ├── ai-workspace/ # AI 工作区共用壳
│ │ ├── layout/ # LeftNav / AddNodePanel 等
│ │ └── <feature>/ # 各业务侧栏(虚拟影棚、模特生成等)
│ ├── context/ # Auth / CanvasProvider
│ ├── shared/api/ # backend / 任务 / 计费 / 存储
│ ├── constants/backend-api.ts
│ └── styles/
├── vite.config.ts # 端口 5174;/api 同源代理到 :8001
└── .env.example
产品导航与路由¶
路由定义:src/App.tsx。导航文案:src/components/site/site-nav-config.ts。
除 / 首页外,功能页均经 ProtectedRoute + 懒加载。
| 分组 | 路径 | 页面 | 说明 |
|---|---|---|---|
| 公开 | / |
HomePage |
营销首页 |
| 顶栏 | /canvas |
CanvasPage |
无限二创(无限画布) |
| 我的工作台 | /hot-shoot |
HotShootPage |
虚拟影棚 |
/main-image |
MainImagePage |
电商主图 | |
/detail-image |
DetailImagePage |
电商详情图 | |
/model-gen |
ModelGenPage |
真人模特生成 | |
| 爆款工厂 | /hot-template |
HotTemplatePage |
爆款模板 |
/scene-replace |
SceneReplacePage |
场景替换 | |
/hot-replicate |
HotReplicatePage |
爆款复刻 | |
| 其他 | /single-model / /double-model |
换模特单人 / 双人 | |
| 用户 | /assets-library |
AssetsLibraryPage |
我的资产库 |
/account |
AccountPage |
账号中心(配额 / 充值 / 订单) |
flowchart LR
Home["/"] --> Canvas["/canvas 无限二创"]
Home --> Workbench["工作台 / 爆款工厂"]
Workbench --> AiShell["AiWorkspacePage 壳"]
Canvas --> Flow["CanvasFlow + LeftNav"]
Workbench --> Tasks["POST /api/user/tasks/generate"]
Flow --> CanvasAPI["/api/canvas 等"]
两类 UI 模式¶
1. 无限二创(画布)¶
- 入口:
pages/CanvasPage.tsx→CanvasProvider+CanvasFlow - 首页「开始复刻」:
HomePage无限二创区块先打开CanvasEntryModal(文件夹 → 项目选择/新建),再跳转/canvas?projectId=…;画布 hydration 后由OpenProjectFromQuery调用openProject(一项目=一画布,不另建画布实体) - 本地冒烟路径:未登录点「开始复刻」→ 登录弹窗 → 登录成功后出现选择器 → 新建文件夹/项目或点已有项目 → 进入画布且 URL 短暂带
projectId(随后replace清除)→ 画布加载对应云端文档;失败时 alert,仍可继续用本地草稿 - 左侧挂件:
components/layout/LeftNav.tsx(「+」打开AddNodePanel) - 节点 / 边 / AI 输入框:
components/canvas/*、context/canvas/* - 与站点顶栏并存:
SiteHeader(activeLabel="无限二创") - 图片节点工具:见下方「图片节点工具」与 画布图片工具设计
2. AI 工作区(功能页)¶
- 共用壳:
components/ai-workspace/AiWorkspacePage.tsx(左侧配置栏 + 右侧预览 / 结果) - 各业务侧栏:
VirtualStudioSidePanel、ModelGenSidePanel、SceneReplaceSidePanel等 - 生图统一走
shared/api/userTaskApi.ts的runUserGenerateTask(): - 响应含
image_urls→ 同步展示 - 响应含
job_id→ 轮询GET /api/user/batch-jobs/{job_id}
调用后端 API¶
基地址¶
src/constants/backend-api.ts → getBackendApiBase():
| 场景 | 行为 |
|---|---|
设置了 VITE_BACKEND_API_URL(或兼容名 VITE_IMAGE_API_URL) |
使用该根地址(直连,需 CORS) |
| 未配置 / 留空 | 同源:请求 {origin}/api/... |
默认走同源反代,与 agent / admin / demo 一致:
- 本地 Vite:
vite.config.ts将/api(及/wepayapi)代理到https://tuxianai.com(经 Ingress → backend);本机 backend 可设VITE_DEV_PROXY_TARGET=http://127.0.0.1:8001 - ACK / K3s:Ingress 将
/api→ backend,/api/storage→ storage,/wepayapi→ backend(微信 Native 回调)
Docker 构建默认不注入远端 URL(Dockerfile 中 VITE_BACKEND_API_URL 默认为空)。切勿构建时注入 https://backend.tuxianai.com,否则浏览器会跨域直连子域。
HTTP 客户端¶
shared/api/backendHttpClient.ts:
- 请求:
{base}/api{path}(base为空时即为/api{path}),附带Authorization: Bearer <token> - 解析包络:
{ code, message, data },code !== 200抛错
常用模块:
| 模块 | 用途 |
|---|---|
authApi.ts |
登录 / 短信 |
userTaskApi.ts |
任务类型、生图、batch-jobs、预览 Prompt |
productCatalogApi.ts |
品类 / 模板目录等 |
billingApi.ts |
计费 / 套餐 |
storageApi.ts |
上传(经 backend /api/user/storage) |
与 Ingress 的关系(ACK / K3s)¶
画布站点(canvas.tuxianai.com / tuxianai.com / www.tuxianai.com / canvas.localhost):
/→ infinitecanvas 静态资源/api/storage→ storage/api→ backend(含/api/canvas、/api/auth等)
本地开发前端(:5174)默认请求 /api/...,经 Vite 代理到本机 backend。
认证¶
AuthProvider+ localStorage Token(见shared/types/api.ts的AUTH_TOKEN_KEY)- 未登录访问保护路由时走
ProtectedRoute/ 登录引导(LoginPromptProvider)
图片节点工具¶
选中图片节点后,顶部 ImageNodeToolbar 提供两类能力(不是 xyflow 内置功能):
| 工具 | 实现 | 说明 |
|---|---|---|
| 裁剪 / 标注 / 画笔 | Konva 弹层 | 完成后上传并新建结果节点(源图不变) |
| 抠图 / 去水印 / 扩图 | 画布生图 API | mode=single + 固定提示词;结果进新建节点 |
| 下载 / 放大预览 | 浏览器直链 | 不经编辑管线 |
| 加入 Agent | 引用写入 AI 输入框 | 打开 Agent 面板 |
调度入口:context/canvas/hooks/useImageToolActions.ts。
跨域读像素:远程图经 POST /api/canvas/assets/proxy-image 代拉(utils/image-tool-processor.ts 的 resolveEditableImageUrl)。
flowchart LR
toolbar[ImageNodeToolbar]
toolbar -->|crop annotate brush| konva[ImageKonvaEditorModal]
konva --> newNode[新建结果图片节点]
toolbar -->|cutout watermark outpaint| gen["POST /api/canvas/generate"]
gen --> newNode
source[源图片节点] -.->|不变| source
详细设计见 canvas-image-tools.md。
质量与约束¶
- 有效行数:与仓库统一口径(
check_file_length.py/ ESLint,见 质量门禁技术债) - 勿在组件内直接
fetch到任意域名;新增接口优先落在shared/api/ - 无 agent/admin 的 features import 限制,但仍建议按业务目录拆分,避免超大单文件