Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
1adacea
build(frontend): scaffold vite react typescript workspace
huyanxius Jul 30, 2026
059d375
build(frontend): add oxlint and oxfmt configuration
huyanxius Jul 30, 2026
980d511
build(frontend): add vitest configuration
huyanxius Jul 30, 2026
fd4424a
feat(shared): add shared layer with pagination contract
huyanxius Jul 30, 2026
7e22b51
feat(entities): add data layer public interfaces
huyanxius Jul 30, 2026
73d7b98
feat(features): add use case layer contracts
huyanxius Jul 30, 2026
6b21545
feat(pages): add page modules
huyanxius Jul 30, 2026
e5de440
feat(app): add application shell and entry point
huyanxius Jul 30, 2026
0a13193
ci(frontend): add quality gate and preview deployment
huyanxius Jul 30, 2026
33b6fa3
docs(frontend): document architecture and backend contract
huyanxius Jul 30, 2026
01b0e0a
fix(workflow): persist the in-flight task id on the step
huyanxius Jul 30, 2026
1f0ddca
build(frontend): typecheck the vitest config
huyanxius Jul 30, 2026
2be18e3
refactor(entities): keep only confirmed content in the asset tree
huyanxius Jul 30, 2026
f9776f9
docs(entities): mark what the product has not defined yet
huyanxius Jul 30, 2026
a86d23b
docs(frontend): remove stale skeleton references
huyanxius Jul 30, 2026
77fca50
refactor(character): remove unsupported character name
huyanxius Jul 30, 2026
b03250c
refactor(workflow): keep workflow run frontend-owned
huyanxius Jul 30, 2026
378fee0
docs(frontend): record confirmed backend boundaries
huyanxius Jul 30, 2026
869827b
fix(tasks): require project context for task lookup
huyanxius Jul 30, 2026
7a6515e
docs(api): record project-scoped task lookup
huyanxius Jul 30, 2026
43e4809
feat(generation): define task recovery contract
huyanxius Jul 30, 2026
e8b68ba
feat(workflow-run): add local state store
huyanxius Jul 30, 2026
244dd55
feat(workflow-controller): run character template generation
huyanxius Jul 30, 2026
2aef23a
test(workflow-run): cover local execution lifecycle
huyanxius Jul 30, 2026
339edba
docs(frontend): document workflow runtime boundaries
huyanxius Jul 30, 2026
b72262f
refactor(workflow-controller): separate state and task runtime
huyanxius Jul 30, 2026
6d3f51c
test(workflow-controller): cover extracted state transitions
huyanxius Jul 30, 2026
7e4fff2
fix(character-setup): accept workflow step input
huyanxius Jul 30, 2026
f251ddd
test(character-setup): lock workflow input contract
huyanxius Jul 30, 2026
c274cf3
Merge upstream/main into feat/workflow-run-core
huyanxius Jul 31, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions .github/workflows/frontend-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: Frontend CI

on:
pull_request:
branches: [main]
push:
branches: [main]
workflow_dispatch:

permissions:
contents: read

concurrency:
group: frontend-ci-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
frontend-checks:
name: Frontend checks
runs-on: ubuntu-latest
timeout-minutes: 15
env:
CI: 'true'

defaults:
run:
working-directory: frontend

steps:
- name: Checkout
uses: actions/checkout@v7

- name: Setup Node.js
uses: actions/setup-node@v7
with:
node-version: 24
cache: npm
cache-dependency-path: frontend/package-lock.json

- name: Install dependencies
run: npm ci

- name: Check formatting
run: npm run format:check

- name: Lint
run: npm run lint

- name: Typecheck
run: npm run typecheck

- name: Test
run: npm run test

- name: Build
run: npm run build
115 changes: 115 additions & 0 deletions frontend-architecture-v3.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# Windup 前端架构

本文记录当前前端的模块划分、依赖规则和已经落地的首个工作流纵切。

---

## 1. 模块划分

业务模块都在 `src/entities/` 下:

| 模块 | 职责 |
|---|---|
| `project` | 项目级全局约束:视角、朝向数、精灵尺寸、画风 |
| `character` | 角色资产。造型、动作、帧是它内部的一棵树 |
| `action-template` | 能跨角色复用的动作配方 |
| `generation` | 一次生成任务这份业务数据 |
| `media` | 已上传媒体的不透明引用 |
| `task` | 后端异步步骤的状态 |
| `workflow-run` | 制作流程的运行记录 |

**模块判据:这个东西能不能被单独取到。**

能单独取,说明它需要自己的一套取数逻辑,才值得一个模块;取不到的,它只是别人身上的一个字段。

按这条判据,`Outfit`、`Action`、`Frame` 没有独立模块——它们不能脱离 `Character` 被取到,所以是 `character` 内部的类型。`ActionTemplate` 有独立模块,因为它能被不同角色复用。

---

## 2. 层次

```text
pages -> features -> entities -> shared
```

| 层 | 内容 |
|---|---|
| `pages` | 八个路由页面 |
| `features` | 用户操作:角色设置、生成、审核、导出;以及流程推进 `workflow-controller` |
| `entities` | 上表业务模块 |
| `shared` | 无业务语义的形状,目前只有分页 |

`app` 只做启动和路由,不构造服务、不向下注入。

### 依赖规则

1. 只能向下依赖,不允许反向。
2. 同层模块之间不互相导入。要共用就往下沉。
3. 跨模块只从模块目录的 `index.ts` 进入;`entities` 统一从 `@/entities` 使用。
4. `entities` 内部模块之间可以互相导入,对外仍是一个门。

---

## 3. 接口命名

需要访问后端资源的模块暴露一组接口,统一叫 `XxxApis`:

```text
ProjectApis CharacterApis ActionTemplateApis GenerationApis
TaskApis
```

**不使用 `Repository` / `Port` / `Adapter` 这些叫法**,也不做接口与实现的分离——实现跟着接口放在同一个模块里。

`WorkflowRun` 是前端运行态,不声明后端接口。后端不读取、不推进、也不持久化它。

---

## 4. 流程推进

`features/workflow-controller` 是快速开始与手动工作流共用的推进边界,不含界面。

Controller 围绕同一份 WorkflowRun 提供创建、读取、订阅、当前步骤更新、推进、
任务恢复、结果写回和中断。这些操作依赖同一份步骤数据,不拆成互不共享状态的独立模块。

步骤顺序固定八步:

```text
角色资料 → 角色图 → 候选选择 → 动作资料 → 首帧 → 完整动画 → 审核 → 导出
```

**步骤怎么走、运行状态如何保存都由前端决定。** 后端不参与 WorkflowRun,
只接收各节点发起的生成请求,并在最终确认时持久化角色与动作资产。固定八步是当前
产品流程,不是为了通用编排而写的可配置工作流。

当前存储版本只支持一个 Revision。从历史步骤重开尚未进入产品定义,Controller
不提前暴露该操作;实现时必须同步升级本地存储版本和迁移规则。

快速开始与手动模式将共用同一份推进逻辑,但连续自动推进属于 Quick Start 页面接入范围,
当前 Controller 只实现一次推进一个步骤。

Controller 的提交锁和任务订阅属于实例状态。页面接入时必须复用同一个 Feature 实例,
不能在组件渲染或路由切换时重复创建。

---

## 5. 当前实现范围

- `WorkflowRun` 的内存状态、版本化 localStorage 镜像和刷新校验
- `角色资料 → 角色图生成 → 候选选择` 的 Controller 纵切
- Store、Controller 和纵向流程测试

页面、Workflow Editor、Quick Start 自动推进、后五步和真实后端适配器仍未实现。

### 恢复边界

- 已取得 `taskId`:刷新后先查询任务当前状态,未结束才重新订阅。
- 请求已经发出但尚未取得 `taskId`:后端没有幂等键或按请求标识查询的能力,
前端将本地 Run 标为失败,不自动重提,避免静默创建重复任务。
- localStorage 写入失败时当前会话继续使用内存快照;页面提示与重新持久化策略在 UI 接入时补充。

---

## 6. 未与后端对齐的部分

明细见 `frontend/API_CONTRACT.md`。
28 changes: 28 additions & 0 deletions frontend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Vercel 本地项目链接元数据,不提交账号和项目 ID
.vercel

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
.env*
9 changes: 9 additions & 0 deletions frontend/.oxfmtrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"$schema": "./node_modules/oxfmt/configuration_schema.json",
"endOfLine": "lf",
"printWidth": 100,
"semi": false,
"singleQuote": true,
"trailingComma": "all",
"ignorePatterns": ["**/*.md"]
}
8 changes: 8 additions & 0 deletions frontend/.oxlintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"$schema": "./node_modules/oxlint/configuration_schema.json",
"plugins": ["react", "typescript", "oxc"],
"rules": {
"react/rules-of-hooks": "error",
"react/only-export-components": ["warn", { "allowConstantExport": true }]
}
}
119 changes: 119 additions & 0 deletions frontend/API_CONTRACT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# 前后端接口对齐清单

前端各模块的 `XxxApis` 与后端 2026-07-30 接口文档逐条比对结果。

后端现有四个相关模块:`project`、`character`、`generation`、`media`。`asset` 与 `wearable` 已按 07-30 评审要求删除。

---

## 一、已经确认的边界

- `WorkflowRun` 是前端固定工作流的运行态。后端不读取、不推进、也不持久化,前端不声明 `WorkflowRunApis`。
- `Character` 不使用独立 `name` 字段;前端已删除。
- 前端保留 `jump` 动作类型,由后端补充对应枚举。
- 查询生成任务统一携带 `projectId + taskId`。
- 前端工作流节点不与后端 `GenerationType` 一一对应,按下表调用:

| 前端工作流节点 | 后端接口 | 后端任务类型 |
|---|---|---|
| `character_template` | `POST /generation/image` | `character_image` |
| `first_frame` | `POST /generation/image`,以上一步角色图作为参考图 | `character_image` |
| `complete_animation` | `POST /generation/action`,以已确认动作首帧作为参考图 | `character_action` |

图片生成和动作生成只返回任务及结果,不自动修改 WorkflowRun 或角色资产。用户最终确认后,前端再通过角色更新接口保存角色图和完整动作数据。

---

## 二、前端预期有、后端目前没有

**这些接口仍需要确定由后端提供,还是改为前端本地能力。**

| 前端接口 | 后端情况 |
|---|---|
| `ActionTemplateApis.listAvailable` | 没有 action template 模块 |
| `ProjectApis.update` | 没有 `PATCH /projects/{project_id}` |

前端已按服务端现状去掉 `TaskApis.cancel`——后端没有取消能力,不声明前端用不到的接口。

---

## 三、形状不一致

这些差异可以在前端接口层转换,不要求领域类型与后端 DTO 使用相同命名。

| 项 | 后端 | 前端 |
|---|---|---|
| 角色列表 | `list_characters` 分页,返回 `(list, total)` | `listByProject` 无分页 |
| 更新角色 | `update_character(character_id, **fields)` 部分更新 | `update(character)` 整棵树替换 |
| 等待任务完成 | 提供 `GET /generation/tasks/{task_id}` 轮询 | `TaskApis.subscribe`;适配器先立即回放当前快照,再继续轮询 |
| 图片生成数量 | 入参有 `num_images`,结果只有一个 `image_url` | 角色图候选结果是 `images[]` |
| 动作类型 | `walk` `idle` `attack` `custom`;待增加 `jump` | `walk` `idle` `attack` `jump` `custom` |
| 角色视角 | `character_perspective` 为 `1~3`,文档中 2、3 都写成“正面” | `side` `top-down` `isometric` |

ID 类型后端为 `int`、前端为 `string`,由前端转换层处理,不需要后端改动。

---

## 四、后端有、前端没接

| 后端 | 说明 |
|---|---|
| `delete_character` | 前端 `CharacterApis` 没有删除 |
| `Character.description` | 后端存在实体上;前端只在创建入参里,创建完查不到 |
| `Character.reference_image_url` | 后端存在实体上;前端 `Character` 类型没有这个字段 |
| `MediaService.upload` | 前端本次未提交上传模块 |

---

## 五、前端资产字段在后端没有落点

后端 `character_data` 的嵌套结构(见 `character/model.py`):

```text
outfits[] → id / name / preview_url / actions[]
actions[] → id / type / name / loop / fps / frame_count / frames[]
frames[] → index / image_url / duration_ms
```

前端以下字段在后端结构里没有落点:

- `Action.kind`(preset / custom 来源)
- `Action.keyFrameIndex`
- `Frame.rootMotion`
- `Outfit.candidateCharacterTemplates`(母版候选列表)
- `Outfit.characterTemplateUrl`(每套造型的已确认角色图)
- `Outfit.baseFrames`

`candidateCharacterTemplates` 属于生成过程数据;若只在当前 WorkflowRun 中使用,可以留在前端。其余字段若要随最终资产恢复,需要后端增加字段,或者前端在 MVP 中删除。

---

## 六、概念不一致

后端 `character/model.py` 字段说明:

> `reference_image_url`: 角色参考图,即旧概念中的 Character Template

前端把这两者当成不同的东西:

- 用户上传的参考图 —— 创建角色时的输入
- AI 生成后用户选定的角色图(母版)—— `Outfit.characterTemplateUrl`

**后端合成了一个字段。** 07-30 评审也提到「模板」这个叫法容易与 action template 混淆,暂改称「角色图」。三方对这里是几个概念的理解需要统一。

---

## 待确认

- [ ] `ActionTemplateApis` 由后端提供还是前端内置
- [ ] 母版候选几张
- [ ] 参考图与角色图是一个字段还是两个
- [ ] `Character.description` 前端要不要跟着存
- [ ] `Action.kind` / `Action.keyFrameIndex` / `Frame.rootMotion` 是否进入最终资产
- [ ] 上传模块何时提交

## 已分工

- [x] 前端删除 `WorkflowRunApis`,WorkflowRun 全程由前端管理
- [x] 前端删除 `Character.name`
- [ ] 后端增加 `jump` 动作类型
32 changes: 32 additions & 0 deletions frontend/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Windup 前端

React + Vite + TypeScript。

## 开发

```bash
npm ci
npm run dev
```

## 检查

```bash
npm run format:check # 格式
npm run lint # 静态检查
npm run typecheck # 类型
npm run test # 单元与纵向集成测试
npm run build # 构建
```

CI 按上面顺序全跑一遍。

## 结构

模块划分、依赖规则与命名约定见仓库根目录 `frontend-architecture-v3.md`。

当前已实现纯前端 `WorkflowRun` 存储,以及
`角色资料 → 角色图生成 → 候选选择` 的首个 Controller 纵切。页面仍是占位外壳,
后五步和真实 `XxxApis` 实现按模块拆成后续 PR。

与后端尚未对齐的接口见 `API_CONTRACT.md`。
12 changes: 12 additions & 0 deletions frontend/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Windup · 2D 角色资产生成</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
Loading