# 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 ` 或脚手架创建时,把预设布局**以源码拷贝进**项目 `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` / 中文语境:万物注册?敬请给方向