Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 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
d9bd279
feat(layout): add floating product navigation
huyanxius Jul 30, 2026
5350293
test(layout): cover floating navigation routes
huyanxius Jul 30, 2026
57851c4
feat(home): add first-screen entry surface
huyanxius Jul 30, 2026
73b6497
test(home): cover first-screen entry routes
huyanxius Jul 30, 2026
cf6130a
Merge upstream/main into feat/home-entry-surface
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
102 changes: 102 additions & 0 deletions frontend-architecture-v3.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# Windup 前端架构

本文记录当前前端的模块划分与依赖规则。2026-07-30 按当日评审意见重写:本阶段只提交模块边界与接口,实现进后续 PR。

---

## 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,旧 Revision 保留为只读历史,不会被改写成失败或完成。

快速开始与手动模式共用同一份推进逻辑,区别只是前者连续调用、后者一次一步。隐藏步骤不等于跳过步骤——门禁写在流程模型里,不在界面里。

---

## 5. 本次不包含

- 任何实现代码(真实请求、假数据、组件内部逻辑)
- 测试文件
- 图片上传模块(体量太小,本次不单独体现)
- 穿戴道具相关(产品侧未设计)
- 第三方登录

页面当前是占位外壳,只声明路由与模块边界。

---

## 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` 动作类型
30 changes: 30 additions & 0 deletions frontend/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# 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`。

**本阶段只提交模块边界与接口,不含实现。** 页面是占位外壳,各模块只有类型与 `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