feat: vibe-registry M1 — 万物皆注册框架骨架
- core: registerLayout/Widget/Action(泛型)/Schema + 校验闸门 - definePage: 页面只管业务,结构来自注册表 - presets: admin / app-shell 预设布局(vr add 物化) - cli: vr list/add/update 防覆盖 - examples/qa-orchestrator: 实战编排验证(workspace:* 依赖)
This commit is contained in:
+262
@@ -0,0 +1,262 @@
|
||||
# vibe-registry — 面向 AI 前端的"万物皆注册"页面框架(设计文档 v0.5)
|
||||
|
||||
> 状态:探究中(未定稿) · 作者:dsh-agent 与用户协作
|
||||
> 定位:vibe coding 时代,把"AI 前端生成"从自由发挥收束到**受控注册 + 声明式组合**
|
||||
|
||||
---
|
||||
|
||||
## 1. 问题陈述
|
||||
|
||||
### 1.1 痛点的三张面孔
|
||||
|
||||
| 痛点 | 现象 | 根因 |
|
||||
|---|---|---|
|
||||
| **重复造轮子** | 让 AI 做个表单/列表/弹窗,它每次从零拼一遍 | AI 无"既有资产"记忆,不查你已有组件 |
|
||||
| **布局不统一** | 页面间距、栅格、导航、按钮位置随生成批次漂移 | 布局规范只存在于人脑/文档,AI 不会遵守 |
|
||||
| **无法收敛** | 改一处样式要动 N 个文件,AI 改完这处崩那处 | 约定不成为代码,就没有强制力 |
|
||||
|
||||
### 1.2 为什么组件库没解决
|
||||
|
||||
组件库(shadcn/antd)解决了"有组件可用",但没解决两件事:
|
||||
|
||||
1. **AI 不会主动用**——提示词劝说无效,生成时照样自创;
|
||||
2. **页面级结构(布局/区块/页面骨架)没有注册位**——组件库只管原子,不管"一个后台页长什么样"。
|
||||
|
||||
### 1.3 一句话目标
|
||||
|
||||
> 让**页面 = 注册项的组合**,让 AI(和人)通过**受控的注册/声明语法**产出页面,
|
||||
> 而不是通过自由的 JSX——从生成源头锁死重复与漂移。
|
||||
|
||||
---
|
||||
|
||||
## 2. 核心理念:万物皆注册(Everything is Registered)
|
||||
|
||||
### 2.1 三个公理
|
||||
|
||||
1. **一切可复用之物必须注册**:布局、区块、组件、操作、路由、表单字段、数据源……先注册,才可用。
|
||||
2. **页面是声明的产物,不是手写的产物**:页面文件只声明"由哪些注册项、以什么配置组合",结构由注册表驱动渲染。
|
||||
3. **改动即配置,配置即代码**:任何视觉/结构改动落在注册项配置里,版本可控、可 diff、可被 AI 精确编辑。
|
||||
|
||||
### 2.2 注册项类型(草案)
|
||||
|
||||
```ts
|
||||
type RegistryItem =
|
||||
| { kind: 'layout'; id: string; ...; preview: { demo?: PageDemo; thumbs?: string } } // 页面骨架,带 live 预览数据
|
||||
| { kind: 'block'; id: string; ... } // 区块:导航/侧栏/页脚/内容容器
|
||||
| { kind: 'widget'; id: string; ...; preview?: WidgetDemo } // 业务组件:表单/表格/弹窗(映射到 UI 库)
|
||||
| { kind: 'action'; id: string; ... } // 行为:提交/跳转/确认(可复用事件语义)
|
||||
| { kind: 'schema'; id: string; ... } // 数据/字段注册:表单字段、表格列
|
||||
| { kind: 'theme'; id: string; ... } // 设计 token:间距/圆角/色板(轻量覆盖)
|
||||
```
|
||||
|
||||
关键:
|
||||
|
||||
- **widget 允许指向任意第三方组件并注册**,万物皆注册的"物"是开放的;
|
||||
- **预览数据是注册项的一部分**:layout/widget 注册时自带 demo 数据,选择层渲染真实形态,
|
||||
预览与实装共用同一渲染内核——**预览即真实,无 mock 漂移**;
|
||||
- 二次开发定制页面 = 新增 widget/layout 注册并挂入 P 层,天然获得风格/主题一体化(共用 theme + 渲染内核)。
|
||||
|
||||
---
|
||||
|
||||
## 3. 架构分层
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────┐
|
||||
│ P. 选择/预览层(新,用户长时间停留的地方) │
|
||||
│ 布局画廊:预设布局 live 预览 + 缩略图 │
|
||||
│ IDE 联动(Webview/面板):选中→回写声明 │
|
||||
│ 用户只做:挑布局 → 声明业务 → 生成 │
|
||||
├─────────────────────────────────────────────┤
|
||||
│ A. 声明层(用户/AI 产出) │
|
||||
│ page.tsx 声明:layout id + blocks + │
|
||||
│ 配置 + 业务回调 │
|
||||
├─────────────────────────────────────────────┤
|
||||
│ B. 注册表层(单一事实来源) │
|
||||
│ registry/**/*.ts:注册项定义与配置 │
|
||||
│ (类型安全:TS 推导注册项类型) │
|
||||
├─────────────────────────────────────────────┤
|
||||
│ C. 极薄 schema 渲染内核 │
|
||||
│ 声明 → 按 id 查注册项 → 布局槽位填充 → │
|
||||
│ widget 装配 → 输出组件树 │
|
||||
├─────────────────────────────────────────────┤
|
||||
│ D. 业务接入层(页面"只管业务") │
|
||||
│ 页面文件只写:数据获取 / 状态 / 事件回调, │
|
||||
│ 结构全部来自 C 层输出 │
|
||||
└─────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 3.0 选择/预览层(P 层)定位
|
||||
|
||||
- **拿包即画廊**:脚手架创建项目时把一批预设布局(源码)放进 `registry/**` 并生成画廊首页——`npm run dev` 打开即见(类 Vite 欢迎首页);
|
||||
- **默认预设全部带 live 预览**:每个预设布局(admin/dashboard/landing…)内置一份"demo 数据渲染",用户一眼看到成品形态再决定;
|
||||
- **配合 IDE 联动**(M3 增强):VS Code/Cursor Webview 面板或 HMR 预览路由——选布局、声明业务、实时看效果,**长时间停留在"选择"而不写布局**;
|
||||
- **声明业务即可**:P 层把用户的"业务描述"折成 `definePage` 的 setup/blocks 声明;
|
||||
- **预设与自定义一体**:项目 `registry/**` 是唯一资产空间,`vr add` 物化预设、直接改源码,二次开发定制页天然与预设同一渲染内核、风格/主题/布局统一。
|
||||
|
||||
### 3.1 页面文件长什么样(愿景示例)
|
||||
|
||||
```tsx
|
||||
// 页面只管业务——不写任何布局 JSX
|
||||
export default definePage({
|
||||
layout: 'app-shell', // ← 注册的布局
|
||||
blocks: [
|
||||
{ widget: 'page-header', props: { title: '订单管理' } },
|
||||
{ widget: 'filter-bar', bind: { filters } },
|
||||
{ widget: 'data-table', bind: { columns, rows, onPage } },
|
||||
],
|
||||
// 业务:
|
||||
async setup(ctx) {
|
||||
const rows = await api.listOrders(ctx.query)
|
||||
return { rows }
|
||||
},
|
||||
actions: { onPage: async (p) => { ... } },
|
||||
})
|
||||
```
|
||||
|
||||
AI 生成的"页面代码"被限制成这种形状:**结构来自注册表,AI 只填业务与配置**。
|
||||
|
||||
---
|
||||
|
||||
## 4. 面向 AI 的受控生成(AI Gate 草案)
|
||||
|
||||
这是与所有"低代码引擎"的分水岭——**用户是 AI,不是拖拽者**。
|
||||
|
||||
### 4.1 交互协议:用户只须说"xx 布局"
|
||||
|
||||
```
|
||||
用户: "订单列表页,用 admin 布局,带筛选和分页表格"
|
||||
agent: layout: 'admin' ← 唯一 id 契约
|
||||
blocks: [ filter-bar, data-table ] ← 全部来自注册表
|
||||
setup() { 业务 } ← 只填业务
|
||||
```
|
||||
|
||||
- 用户**只需报布局 id**(`admin`/`dashboard`/`landing`…),布局的栅格、骨架、样式细节全在注册项里,用户与 agent 都不需要知道实现;
|
||||
- 所有可用的 id 是**公开面**,agent 通过注册表目录/README 获取,不许自创 id;
|
||||
- 布局 id 是**稳定契约**:实现可换,id 不变(防止 agent 记错实现造成静默漂移)。
|
||||
|
||||
### 4.2 受控语法与校验闸门
|
||||
|
||||
- **受控语法**:AI 只能产出 `definePage/registerItem` 形状的代码;
|
||||
- **校验闸门**:生成产物过 `validate()`——引用了未注册的 widget/layout → 报错退回,提示"请先注册或改用已有项";
|
||||
- **注册优先**:AI 想造新组件,先走"注册申请",注册完再引用;
|
||||
- **收敛目标**:同一仓库内,所有 AI 生成的页面共享同一注册表 → 布局天然统一。
|
||||
|
||||
### 4.3 扩展:万物皆可注册(注册是开放集)
|
||||
|
||||
- 布局、widget、schema、action 均可由用户/agent 新增注册;
|
||||
- 新增注册 = 写一个 `registerXxx({...})` 文件放 `registry/**`,自动纳入类型推导;
|
||||
- **未使用不进编译和源码**:新注册但没页面引用 → 留在注册源、不进产物(零成本扩展);
|
||||
- 首次被引用时自动物化,进入编译与源码树。
|
||||
|
||||
---
|
||||
|
||||
## 5. 与现有方案的分界(避免"又一个低代码")
|
||||
|
||||
| 方案 | 我们不做 | 我们做 |
|
||||
|---|---|---|
|
||||
| 低代码平台(拖拽/配置后台) | 不做可视化画布、不做运行时编辑器 | 配置即代码,存 git,可 review |
|
||||
| 组件库(shadcn/antd) | 不重新造原子组件 | 组件**注册化包装**,统一暴露 |
|
||||
| 代码生成器(v0/bolt) | 不只"生成页面",不解决复用 | 生成受控产物,强制复用注册项 |
|
||||
| headless 框架 | 不做渲染运行时新轮子 | 渲染内核极薄,组合已有库 |
|
||||
|
||||
一句话:**我们不发明 UI,只发明"约束 AI 的注册协议"。**
|
||||
|
||||
---
|
||||
|
||||
## 6. 已定决策(v0.5,用户拍板)
|
||||
|
||||
1. **声明格式:TS 优先**——`definePage`/`registerItem` 全 TS,类型推导强、AI 好生成;YAML 仅备用于纯数据资产。
|
||||
2. **渲染内核:极薄 schema 渲染器**——注册项 → 映射已有组件(antd/shadcn),内核保持 <500 行,不重造 UI。
|
||||
3. **预设布局模板:内置多个、均带简易 id 与 live 预览**——如 `app-shell` / `admin` / `dashboard` / `landing` / `form-page`……用户(或用户告诉 agent)**只需说"用 xx 布局"**,agent 按 id 取布局,无需理解布局实现;每个布局附带 demo 预览,选择层直接可看。
|
||||
4. **可扩展**:用户可注册自己的布局/widget(文件约定,见 §8),注册是开放集;二次开发定制页可并入现有布局,与常见组件库联动,风格/主题一体化。
|
||||
5. **未使用不进编译、不进源码**——只有被页面**实际引用**的注册项才进入编译产物与源码树;未被引用的注册项不物化、不打包。
|
||||
6. **选择/预览层(P 层)**:用户长时间停留在"选布局 + 声明业务",由 P 层回写声明文件。
|
||||
7. **预设物化进项目 + 拿包即画廊(类 Vite 首页)**——预设与自定义**物理一体**,消灭两套资产:
|
||||
- **预设不是黑盒包**:CLI `vr add <layout>` 或脚手架创建时,把预设布局**以源码拷贝进**项目 `registry/**`(类 shadcn add + Vite 模板心智);
|
||||
- 之后预设与自定义**同目录、同源码、同改法**——想改布局细节直接改源码,无"包内预设不可改";
|
||||
- **升级**:框架发新版,`vr update` 拉 diff,用户选择是否同步(类 shadcn upgrade);
|
||||
- **画廊**:项目 `registry/**` 自带 demo 数据,dev 首页列出全部已注册项 live 预览,选中即生成 `definePage` 声明;
|
||||
- **未使用不进产物**:物化进项目的预设,仍只有被页面**引用**才进入编译产物。
|
||||
|
||||
### 6.1 注册与物化的统一模型(决策 5 × 7)
|
||||
|
||||
```
|
||||
包内预设 registry 源 ──vr add / 脚手架──> 拷贝进项目 registry/**(与自定义同源码)
|
||||
│
|
||||
项目 registry/**(唯一资产空间)
|
||||
│ 被页面引用 → 物化进产物
|
||||
▼
|
||||
编译产物 / 源码树(仅物化项)
|
||||
```
|
||||
|
||||
- **一个资产空间**:项目 `registry/**` 是唯一事实来源,预设与自定义无差别;
|
||||
- **物化 = 被引用**:bundler 树摇保证未引用注册项不打包;
|
||||
- **画廊 = dev 首页**:渲染项目内全部已注册项 live 预览,生产不含画廊。
|
||||
|
||||
|
||||
|
||||
### 6.1 决策 5 的机制:按需物化(On-Demand Materialization)
|
||||
|
||||
```
|
||||
注册表(全量,dev 可见) ──静态引用分析──> 物化清单(Materialized Set)
|
||||
│ 只装载被引用项
|
||||
▼
|
||||
编译产物 / 源码树(仅物化项)
|
||||
```
|
||||
|
||||
- 触发时机:**生成期**(定义页面的构建/生成脚本时)做全量静态扫描,收集所有 `definePage` 中被引用的 layout/widget/schema id;
|
||||
- 未被引用的注册项停留在"注册源"里,**不生成运行时文件、不进 bundle**;
|
||||
- 好处:产物极轻、无死代码、新增预设布局零成本(不用就不存在);
|
||||
- 张力与对策:见 §6.2。
|
||||
|
||||
### 6.2 新决策暴露的两个张力(需留意,不阻塞)
|
||||
|
||||
1. **"未使用即不物化" vs "注册即复用"**:一个 widget 被页面 A 注册、但尚未被任何页面引用——它算"未使用"而被裁剪吗?
|
||||
→ 对策:物化判断以**引用**为准;仅注册未引用 = 留在注册源但不进产物(符合你的设计);
|
||||
"想让它先进产物"需显式 `include`(用于模板扩展场景)。
|
||||
2. **布局 id 语义漂移**:agent 记忆里的 `admin` 和你注册的 `admin` 若实现不同,会静默用错布局。
|
||||
→ 对策:布局 id 是**稳定契约**,实现可换、id 不变;id 表是文档化的公开面,进 README 供 agent 引用。
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 8. 扩展注册机制(文件约定)
|
||||
|
||||
```
|
||||
registry/ ← 唯一资产空间(预设 + 自定义同在此)
|
||||
layouts/ admin.ts registerLayout('admin', { skeleton: [...] })
|
||||
app-shell.ts ← vr add app-shell 从包内拷贝而来
|
||||
widgets/ table.ts registerWidget('data-table', { render: Table, schema })
|
||||
filter.ts ...
|
||||
schemas/ order.ts registerSchema('order-fields', ...)
|
||||
actions/ submit.ts registerAction('submit-order', ...)
|
||||
```
|
||||
|
||||
- 每个文件幂等注册一个 id;重名 id = 编译报错(唯一性强制);
|
||||
- 目录/文件路径仅组织用,**id 才是引用键**(满足"简易 id"诉求);
|
||||
- **预设 = `vr add` 拷贝进此目录的源码**,与自定义无差别,可直接修改;
|
||||
- `vr update` 对比包内新预设 diff,用户选择同步(可保留本地改动);
|
||||
- 未引用的注册文件可留在仓库作为资产,但不出产物(决策 5)。
|
||||
|
||||
## 9. MVP 范围(建议第一刀)
|
||||
|
||||
**阶段 M1:类型 + 渲染内核 + 静态物化(不接 UI 库)**
|
||||
- [ ] `registerItem()` + 目录式注册表,类型推导
|
||||
- [ ] `definePage()` + 布局槽位填充(首内置:`admin`/`app-shell` 布局示例)
|
||||
- [ ] 注册项带 demo 预览数据的类型骨架(为 P 层铺路)
|
||||
- [ ] `validate()` 校验闸门(未注册引用即报错)
|
||||
- [ ] 静态引用分析:未使用注册项不进产物(决策 5 的最小实现)
|
||||
|
||||
**阶段 M2:接入真实 UI 库 + 引入即预览**
|
||||
- [ ] widget 映射 antd/shadcn(首批 8-10 个:按钮/表格/表单/弹窗/筛选条/分页…)
|
||||
- [ ] 引入即注册验证:import 注册模块 → 自动进入画廊
|
||||
- [ ] dev 首页(布局画廊):缩略图 + live 预览 + demo 数据(类 Vite 首页)
|
||||
- [ ] 2-3 个示例后台页,证明"同一注册表,AI 生成即统一"
|
||||
|
||||
**阶段 M3:生成回写 + IDE 联动**
|
||||
- [ ] 画廊选中布局 → 声明业务 → 生成 `definePage` → 回写文件
|
||||
- [ ] VS Code/Cursor Webview 或 HMR 面板接入
|
||||
|
||||
## 10. 命名候选(未定)
|
||||
|
||||
- `vibe-registry` / `regui` / `page-registry` / 中文语境:万物注册?敬请给方向
|
||||
Reference in New Issue
Block a user