Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Jan 22, 2026

Implements a generic, implementation-agnostic UI component schema that defines "how to describe a UI tree" rather than "what properties each component has". This allows UI implementations to evolve independently while maintaining structural compatibility.

Design Philosophy

Protocol Layer Responsibility: Define the tree structure (id, type, props, children, events, style, meta)
Implementation Layer Responsibility: Define what each component type means and how to render it

This separation of concerns ensures the protocol remains stable while UI implementations can innovate freely.

Schema Structure

Generic Component Node with flexible, extensible properties:

export const ComponentSchema = z.object({
  id: z.string().optional(),                 // Unique component instance identifier
  type: z.string(),                          // Extensible component type identifier
  props: z.record(z.unknown()).optional(),   // Implementation-defined properties
  children: z.lazy(() => z.array(ComponentSchema)).optional(),  // Recursive nesting
  events: z.record(z.function()).optional(), // Event handler bindings
  style: z.record(z.string()).optional(),    // Custom CSS styles
  meta: z.record(z.unknown()).optional(),    // Editor/designer metadata
});

Key Features

1. Extensible Component Types

  • type is a flexible string, allowing custom component types without protocol changes
  • No rigid enum constraining component innovation

2. Generic Props Container

  • props accepts any key-value pairs via z.record(z.unknown())
  • Protocol carries data without validating business logic
  • UI implementations define their own prop requirements

3. Recursive Composition

  • children supports unlimited nesting depth via z.lazy()
  • Enables complex component trees for enterprise UIs

4. Standard Event & Style Support

  • events for event handler bindings
  • style for custom CSS styling
  • These remain protocol-standard across all components

5. Component Instance Identification

  • id field provides unique identifiers for component instances
  • Essential for rendering engines, diff algorithms, and drag-drop editors
  • First-class field for direct engine-layer management

6. Editor & Plugin Metadata

  • meta field stores editor/designer-specific information
  • Separates rendering props from non-rendering metadata
  • Supports position data, locked states, binding paths, and plugin-specific data
  • Prevents pollution of component props with editor-only information

Usage Examples

Simple Component:

const button: Component = {
  type: 'button',
  props: {
    label: 'Click me',
    variant: 'primary',
    customProp: 'any value'
  }
}

Component with ID and Metadata (for visual editors):

const editableCard: Component = {
  id: 'user-profile-card',
  type: 'card',
  props: {
    title: 'User Profile',
    subtitle: 'Account Details'
  },
  meta: {
    x: 100,
    y: 200,
    locked: false,
    bindingPath: 'user.profile'
  }
}

Nested Component Tree:

const form: Component = {
  type: 'form',
  props: { action: '/submit' },
  children: [
    {
      type: 'input',
      props: { name: 'username', placeholder: 'Enter username' }
    },
    {
      type: 'button',
      props: { type: 'submit', label: 'Login' }
    }
  ]
}

Custom Components (no protocol changes needed):

const customWidget: Component = {
  type: 'my-custom-widget',
  props: {
    config: { foo: 'bar' },
    data: [1, 2, 3]
  }
}

Files Changed

  • packages/spec/src/ui/component.zod.ts - Generic component schema (~95 lines, reduced from ~970 lines)
  • packages/spec/src/ui/component.test.ts - Structural validation tests
  • packages/spec/src/ui/index.ts - Export configuration
  • Auto-generated: JSON schema and MDX reference docs

Testing

24 structural tests validate:

  • Component type extensibility (any string accepted)
  • Arbitrary prop passing (any key-value pairs)
  • Recursive nesting (3+ levels deep)
  • Event binding patterns
  • Style property handling
  • Real-world component trees (forms, tables, menus, modals)
  • Component instance identification with id field
  • Editor metadata with meta field
  • Complex nested metadata structures

All tests passing ✅

Benefits

Decoupled: Protocol and implementation evolve independently
Extensible: New component types without protocol changes
Maintainable: Simple, focused protocol definition
Future-proof: Adaptable to any UI framework or library
Editor-Ready: Built-in support for visual editors and plugin systems
Engine-Friendly: First-class id field for efficient component tracking

Original prompt

任务 4.2: 组件库协议

文件: packages/spec/src/ui/component.zod.ts
负责人: UI 协议负责人
工作量: 3 天
依赖: 无

实现要点:

// 可复用 UI 组件定义
export const ComponentType = z.enum([
'card',
'tabs',
'accordion',
'modal',
'drawer',
'timeline',
'stepper',
'breadcrumb',
'alert',
'badge',
'tooltip',
'popover',
]);

export const ComponentSchema = z.object({
type: ComponentType,
props: z.record(z.any()).optional(),
children: z.lazy(() => z.array(ComponentSchema)).optional(),
events: z.record(z.function()).optional(),
style: z.record(z.string()).optional(),
});

// 卡片组件
export const CardComponentSchema = ComponentSchema.extend({
type: z.literal('card'),
props: z.object({
title: z.string().optional(),
subtitle: z.string().optional(),
image: z.string().url().optional(),
actions: z.array(z.any()).optional(),
}).optional(),
});

// 标签页组件
export const TabsComponentSchema = ComponentSchema.extend({
type: z.literal('tabs'),
props: z.object({
tabs: z.array(z.object({
label: z.string(),
icon: z.string().optional(),
content: ComponentSchema.optional(),
})),
defaultTab: z.number().int().min(0).optional(),
}),
});
验收标准:

定义所有常用组件
支持组件嵌套 (children)
支持事件绑定
文档包含渲染示例


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

@vercel
Copy link

vercel bot commented Jan 22, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
spec Ready Ready Preview, Comment Jan 22, 2026 4:17pm

Request Review

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
@hotlong
Copy link
Contributor

hotlong commented Jan 22, 2026

@copilot 不要局限于列表,考虑所有企业管理软件开发所必须的常用组件

…ples

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Copilot AI changed the title [WIP] Add reusable UI component definitions Add Component Library Protocol with recursive composition Jan 22, 2026
@hotlong
Copy link
Contributor

hotlong commented Jan 22, 2026

@copilot 继续

Copy link
Contributor Author

Copilot AI commented Jan 22, 2026

@copilot 不要局限于列表,考虑所有企业管理软件开发所必须的常用组件

已扩展组件库从12个增加到79个企业管理软件必备组件类型。

新增类型涵盖:

  • 数据展示 (11): table, list, tree, description, statistic, tag, collapse, carousel, image, avatar, calendar_view
  • 数据录入 (15): form, input, select, checkbox, radio, switch, slider, date_picker, time_picker, upload, autocomplete, cascader, transfer, color_picker, rate
  • 导航 (6): menu, sidebar, pagination, dropdown + 原有的breadcrumb, stepper
  • 反馈 (8): message, notification, progress, skeleton, spin, result, empty + 原有的alert
  • 交互 (4): button, button_group, icon_button, split_button
  • 布局 (5): container, divider, space, grid, flex + 原有的card, tabs, accordion, modal, drawer

已为13个最重要的企业组件实现详细schema(Table, Form, Menu, Button, Input, Select, List, Tree, Progress, Pagination, Upload等),所有92个测试通过。

Commit: 86fd948

@copilot 继续

已完成扩展。组件库现在包含79个类型,涵盖企业管理软件开发的所有核心组件需求。实现了13个重要组件的完整schema定义,包括表格、表单、菜单、树形控件、文件上传等。所有测试通过(92个测试)。

Commit: 86fd948

@xuyushun441-sys
Copy link
Contributor

@copilot 继续

…ed schemas

Co-authored-by: xuyushun441-sys <255036401+xuyushun441-sys@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Jan 22, 2026

@copilot 继续

已完成组件库扩展。现已包含79个企业级组件类型,并为13个最重要的组件实现了完整schema定义:

新增组件类型 (67个):

  • 数据展示: table, list, tree, description, statistic, tag, collapse, carousel, image, avatar, calendar_view
  • 数据录入: form, input, select, checkbox, radio, switch, slider, date_picker, time_picker, upload, autocomplete, cascader, transfer, color_picker, rate
  • 导航: menu, sidebar, pagination, dropdown
  • 反馈: message, notification, progress, skeleton, spin, result, empty
  • 交互: button, button_group, icon_button, split_button
  • 布局: container, divider, space, grid, flex
  • 覆盖层: dialog, confirm
  • 其他: steps, anchor, back_top, watermark, qrcode

详细实现的组件 (13个):
Table, Form, Menu, Button, Input, Select, List, Tree, Progress, Pagination, Upload

所有79个测试通过 ✅

Commit: 53090e5

Copilot AI changed the title Add Component Library Protocol with recursive composition Add Enterprise Component Library Protocol with 79 component types Jan 22, 2026
Copilot AI requested a review from xuyushun441-sys January 22, 2026 15:00
@hotlong
Copy link
Contributor

hotlong commented Jan 22, 2026

@copilot

Refactor request: Simplify Component Protocol to be implementation-agnostic

Please refactor packages/spec/src/ui/component.zod.ts to reduce the coupling between the Protocol layer and specific UI implementation details.

The current problem:
The current implementation defines strict Zod schemas for 79 specific components (e.g., TableComponentSchema validating specific column props, ButtonComponentSchema validating variants). This is too rigid for a general spec and will make maintenance difficult as UI libraries evolve.

Refactoring Requirements:

  1. Generalize the Schema: Instead of 79 specific schemas, create a Generic Component Node structure.

    • type: Should be a string (or a loose enum of core types like 'container', 'text', 'image') to allow extension.
    • props: Change to z.record(z.unknown()) or z.any(). The protocol should carry the data, not validate the business logic of every component.
    • children: Keep the recursive structure.
    • events & style: Keep these as they are standard across components.
  2. Remove Detailed Component Definitions:

    • Delete the detailed schemas for complex components like Watermark, QRCode, Table (with column validation), etc.
    • You can keep a few Core Structural Components (like Layout, Container) strictly defined if necessary, but leaf components should be generic.
  3. Update Tests:

    • Update component.test.ts to verify the structure (nesting, prop passing) rather than specific component validation rules.

Goal: The spec should define "How to describe a UI tree", not "What properties a Button has".


提示词设计思路解析(供您参考)

  1. 指出问题 (Problem):明确告诉 AI,现在的写法“太具体了”(Implementation Detail),会导致“维护困难”(Maintenance difficult)和“耦合过重”(Coupling)。
  2. 明确变更 (Requirements)
    • type 放宽:从严格的 Enum 改为 String,允许业务方自定义新组件。
    • props 放宽:从 z.object({ specific_field... }) 改为 z.record(z.any())。这是最关键的一步,意味着协议层只负责“搬运”数据,不负责“检查”数据。
    • 保留结构:强调 children(递归)和 events 是协议的核心价值,必须保留。
  3. 设定目标 (Goal):用一句话总结架构原则——协议层定义的是“树的结构”,而不是“叶子的形状”。

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Jan 22, 2026

@copilot

Refactor request: Simplify Component Protocol to be implementation-agnostic...

已完成重构,将组件协议简化为实现无关的通用结构。

主要变更:

  1. 类型灵活化: type 从严格的79个枚举值改为可扩展的 string,允许自定义组件类型
  2. 属性通用化: props 从具体schema改为 z.record(z.unknown()),协议层只传输数据不验证业务逻辑
  3. 删除详细定义: 移除所有79个组件的具体schema定义(Table、Button等)
  4. 保留核心结构: 保持 children(递归嵌套)、eventsstyle 等协议核心价值

架构原则: 协议层定义"如何描述UI树"而非"叶子节点的形状"

测试覆盖: 19个结构性测试验证嵌套、属性传递和组合能力

Commit: 4f2f041

Copilot AI changed the title Add Enterprise Component Library Protocol with 79 component types Add Implementation-Agnostic Component Protocol Jan 22, 2026
@github-actions github-actions bot added documentation Improvements or additions to documentation tests protocol:ui size/xl labels Jan 22, 2026
@github-actions
Copy link
Contributor

This PR is very large. Consider breaking it into smaller PRs for easier review.

@hotlong
Copy link
Contributor

hotlong commented Jan 22, 2026

为什么改了几十个文件 @copilot
其次,经过仔细审查 packages/spec/src/ui/component.zod.ts,目前的实现采用了高度灵活的Generic Component Node模式,这在很大程度上满足了未来的扩展需求。

但是,要做到“尽善尽美”,特别是考虑到“插件化”和“复杂交互”的长远需求,目前的协议还可以补充以下两个关键点(Optional):

1. 唯一标识符 id (建议补充)

目前 Schema 中没有定义 idkey

// 当前定义
type: string;
props?: Record<string, unknown>;

在实际的渲染引擎、拖拽编辑器或 Diff 算法中,组件实例的唯一标识是必不可少的。虽然可以放在 props.id 里,但将其提升为协议的一级字段(First-class citizen)通常是更好的实践,方便引擎层直接管理。

建议

export const ComponentSchema = z.object({
  id: z.string().optional(), // 建议增加这一行
  type: z.string(),
  // ...
});

2. 元数据槽位 meta (建议补充)

插件系统往往需要存储一些“不参与渲染”但对“编辑器/设计器”有用的信息。例如:

  • 这个组件在画布上的坐标 (x, y)
  • 组件是否被锁定 (locked)
  • 组件的显示标签 (label/title)
  • 用于数据绑定的上下文路径 (bindingPath)

目前只能塞进 props 里,但这会污染组件的真实属性(Props 是传给 React/Vue 组件的,Meta 是给设计器用的)。

建议

export const ComponentSchema = z.object({
  // ...
  // 专门存放设计器、插件系统的元数据
  meta: z.record(z.unknown()).optional(), 
});

…tors

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Jan 22, 2026

为什么改了几十个文件 @copilot
其次,经过仔细审查 packages/spec/src/ui/component.zod.ts,目前的实现采用了高度灵活的Generic Component Node模式,这在很大程...

关于文件数量问题:
28个文件中的24个是自动生成的文档文件(.mdx),这是构建系统删除具体组件schema后自动创建的参考文档。实际代码修改只有4个文件。

已添加 idmeta 字段:

  1. id 字段 (可选):

    • 组件实例的唯一标识符
    • 用于渲染引擎、Diff算法、拖拽编辑器
    • 作为一级字段方便引擎层直接管理
  2. meta 字段 (可选):

    • 存储编辑器/设计器专用元数据
    • props 分离,避免污染渲染属性
    • 支持存储坐标、锁定状态、数据绑定路径等信息

新增测试: 5个新测试验证 idmeta 的使用场景(总共24个测试通过)

Commit: d5a98d8

Copilot AI changed the title Add Implementation-Agnostic Component Protocol Add Implementation-Agnostic Component Protocol with Plugin Support Jan 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation protocol:ui size/xl tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants