[backlog] 迭代 11:可点击原型——模型自动连跳转关系 + 预览模式 + 流程连线
- Dominant language
- TypeScript
- Stars
- 0
- Forks
- 0
- Avg merge
- 1h 7m
- Merged PRs (30d)
- 969
Description
## 用户诉求
> 生成的原型,可以生成可点击,连贯的吗?(2026-09-07)
>
> 取舍已定:**跳转关系由模型自动连**(人可改;否则每次整页重生成都要重连一遍)。
## 现状(为什么现在不能)
生成的是一棵**静态组件树**。画布上点节点是「选中它去改」(迭代 2 的 `focusNodeId`),不是跳转。21 个原语里没有任何一个带「点了去哪」——`button` 只有 `label`/`variant`/`full`(`design-prototype.ts:253`)。页与页之间零关系,所以它是可看、可改、可导出的高保真静态稿,不是能走通流程的可点击原型。
## 设计取舍(开工前要定,其中第 1 条建议已给)
**① 跳转关系存在哪** —— 建议 **B**:
| | 方案 A:节点 props 上加 `goTo` | 方案 B:屏级 `links: {from, item?, to}[]` |
|---|---|---|
| 单目标原语(button/chip/card/hero) | 直接,好写 | 需按 nodeId 查表(一个 Map,便宜) |
| 多项原语(list/tabs/bottomnav) | `items: string[]` 要配一条平行的 `goTo` 数组,且 `PROTOTYPE_FIELDS` 没有「按行给目标」这种字段类型,属性面板要新增一类控件 | 天然支持:`item` 指第几项 |
| 对既有契约的冲击 | 动 5–8 个 `*Props`,`PROTOTYPE_FIELDS` 与它的机械门控(键集合 == shape 键集合)全要跟着改 | **21 个 `*Props` 一个不动**,契约测试不受影响 |
| 校验 | 散在各 props 里 | 集中一处:目标页存在、`from` 是本页真实节点、不重复 |
**② 目标怎么寻址** —— 建议按**页序号**(`0 ≤ to < screens.length`)。仓库既有约定就是按位置配对(`prototype`/`frames`/`frameNotes` 三份平行数组按下标对应),按标签会在改名时断,按序号与现状一致。
**③ 模型给了悬空目标怎么办** —— 需要人定。既有纪律是「一页被拒 ⇒ 整个 `prototype` 写回被拒(半套原型比没有更糟)」,但悬空的**跳转**不至于让整页作废,倾向**只丢那一条 link、保留页面**。这与上面那条不一致,属于要显式写进 domain.md 的判断,不该我自己定。
## 工作量拆解
| # | 工作 | 规模 | 说明 |
|---|---|---|---|
| 1 | 契约:`PrototypeScreen.links` + 校验 + 契约测试 | M | 目标越界/悬空 `from`/自跳/重复各一条反证 |
| 2 | **存储:两张表各加一列** | **M(意料之外的税,见下)** | `design_projects.frame_links` + `design_project_prototype_versions.frame_links`,各带长度不变量 |
| 3 | 画布预览模式:点击执行跳转而非选中 | M | `PrototypeCanvas` 走 context,加一个 mode;单页视图跳转 = 切页 |
| 4 | 画板连线:页与页之间画箭头 | M | 已有 `data-frame-index` 与布局计算,加一层 SVG overlay(坐标换算是主要成本) |
| 5 | 属性面板:给选中节点设/改跳转目标 | S | `PROTOTYPE_FIELDS` 加一种 `frameRef` 字段类型(下拉列出各页) |
| 6 | 模型侧:prompt + few-shot + 纠偏 | S–M | 教它连线;`coercePrototypeRaw` 顺手把字符串页号转数字 |
| 7 | 导出:md 加「跳转关系」小节,JSON 规格自动带上 | S | links 挂在 screen 上 ⇒ JSON 免费 |
| 8 | e2e + 截图 + `ui.md` + 评分卡加一维 | M | 主链路:生成 → 预览模式点按钮 → 真的切到目标页 |
**总量 ≈ 一个完整迭代**,与已完成的迭代 5 / 迭代 6 同量级。
## ⚠ 读代码才发现的成本:平行数组税
一屏的数据在库里被拆成**三份平行数组**(`frames` 标签 / `prototype` 树 / `frame_notes` 说明),按下标配对。所以每加一项「每屏一份」的数据,代价是:一次迁移 + 项目表一列 + **版本表也要一列**(`20260906170000` 那张表同样存 `frames`/`prototype`)+ 读写两侧各一份长度不变量。
这个形状**刚刚已经咬过一次**:#2900 修的正是「只写 `frames` 不写 `prototype` ⇒ 强制清空」导致用户整份原型丢失。再加第四份平行数组,等于把同一类 bug 的面积再扩大一圈。
**建议在迭代 11 里顺手收敛**:把三(四)份平行数组合并成单列 `screens jsonb`(每项 `{frame, root, notes, links}`),长度不变量随之消失(不再有"对不上"这种状态)。代价是一次数据迁移 + 读写两侧重写,但换掉的是一整类"按位置对应"的脆弱性。这条要不要做、要不要单独成一个迭代,请人类定。
## 开工前的硬门(ADR-023)
这次改的是 **API 契约**(`PrototypeScreen` 变形),按契约先行流程,`contracts/design-prototype/design-signoff.md` 需要**人类重新签核第 ③ 件**,并过一次阶段一致性复核。**agent 不许自己改 status。** 在那之前这条只是 backlog,不开工。
Contributor guide
No contributing guide indexed for this repository
Research direction
Start with ADR-023 and contracts/design-prototype/design-signoff.md, then inspect design-prototype.ts and migration 20260906170000. After the human decisions are signed off, trace PrototypeCanvas and the storage read/write paths. Done means the contract, persistence, preview navigation, links, export, and e2e, screenshot, ui.md, and scoring checks are covered.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- api, database, frontend, full-stack, testing
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100