@easyops-cn/a2ui-react 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. package/.claude/commands/speckit.analyze.md +184 -0
  2. package/.claude/commands/speckit.checklist.md +294 -0
  3. package/.claude/commands/speckit.clarify.md +181 -0
  4. package/.claude/commands/speckit.constitution.md +82 -0
  5. package/.claude/commands/speckit.implement.md +135 -0
  6. package/.claude/commands/speckit.plan.md +89 -0
  7. package/.claude/commands/speckit.specify.md +256 -0
  8. package/.claude/commands/speckit.tasks.md +137 -0
  9. package/.claude/commands/speckit.taskstoissues.md +30 -0
  10. package/.github/workflows/deploy.yml +69 -0
  11. package/.husky/pre-commit +1 -0
  12. package/.prettierignore +4 -0
  13. package/.prettierrc +7 -0
  14. package/.specify/memory/constitution.md +73 -0
  15. package/.specify/scripts/bash/check-prerequisites.sh +166 -0
  16. package/.specify/scripts/bash/common.sh +156 -0
  17. package/.specify/scripts/bash/create-new-feature.sh +297 -0
  18. package/.specify/scripts/bash/setup-plan.sh +61 -0
  19. package/.specify/scripts/bash/update-agent-context.sh +799 -0
  20. package/.specify/templates/agent-file-template.md +28 -0
  21. package/.specify/templates/checklist-template.md +40 -0
  22. package/.specify/templates/plan-template.md +105 -0
  23. package/.specify/templates/spec-template.md +115 -0
  24. package/.specify/templates/tasks-template.md +250 -0
  25. package/CLAUDE.md +105 -0
  26. package/CONTRIBUTING.md +97 -0
  27. package/README.md +126 -0
  28. package/components.json +21 -0
  29. package/eslint.config.js +25 -0
  30. package/netlify.toml +50 -0
  31. package/package.json +94 -0
  32. package/playground/README.md +75 -0
  33. package/playground/index.html +22 -0
  34. package/playground/package.json +32 -0
  35. package/playground/public/favicon.svg +8 -0
  36. package/playground/src/App.css +256 -0
  37. package/playground/src/App.tsx +115 -0
  38. package/playground/src/assets/react.svg +1 -0
  39. package/playground/src/components/ErrorDisplay.tsx +13 -0
  40. package/playground/src/components/ExampleSelector.tsx +64 -0
  41. package/playground/src/components/Header.tsx +47 -0
  42. package/playground/src/components/JsonEditor.tsx +32 -0
  43. package/playground/src/components/Preview.tsx +78 -0
  44. package/playground/src/components/ThemeToggle.tsx +19 -0
  45. package/playground/src/data/examples.ts +1571 -0
  46. package/playground/src/hooks/useTheme.ts +55 -0
  47. package/playground/src/index.css +220 -0
  48. package/playground/src/main.tsx +10 -0
  49. package/playground/tsconfig.app.json +34 -0
  50. package/playground/tsconfig.json +13 -0
  51. package/playground/tsconfig.node.json +26 -0
  52. package/playground/vite.config.ts +31 -0
  53. package/specs/001-a2ui-renderer/checklists/requirements.md +41 -0
  54. package/specs/001-a2ui-renderer/data-model.md +140 -0
  55. package/specs/001-a2ui-renderer/plan.md +123 -0
  56. package/specs/001-a2ui-renderer/quickstart.md +141 -0
  57. package/specs/001-a2ui-renderer/research.md +140 -0
  58. package/specs/001-a2ui-renderer/spec.md +165 -0
  59. package/specs/001-a2ui-renderer/tasks.md +310 -0
  60. package/specs/002-playground/checklists/requirements.md +37 -0
  61. package/specs/002-playground/contracts/components.md +120 -0
  62. package/specs/002-playground/data-model.md +149 -0
  63. package/specs/002-playground/plan.md +73 -0
  64. package/specs/002-playground/quickstart.md +158 -0
  65. package/specs/002-playground/research.md +117 -0
  66. package/specs/002-playground/spec.md +109 -0
  67. package/specs/002-playground/tasks.md +224 -0
  68. package/src/0.8/A2UIRender.test.tsx +793 -0
  69. package/src/0.8/A2UIRender.tsx +142 -0
  70. package/src/0.8/components/ComponentRenderer.test.tsx +373 -0
  71. package/src/0.8/components/ComponentRenderer.tsx +163 -0
  72. package/src/0.8/components/UnknownComponent.tsx +49 -0
  73. package/src/0.8/components/display/AudioPlayerComponent.tsx +37 -0
  74. package/src/0.8/components/display/DividerComponent.tsx +23 -0
  75. package/src/0.8/components/display/IconComponent.tsx +137 -0
  76. package/src/0.8/components/display/ImageComponent.tsx +57 -0
  77. package/src/0.8/components/display/TextComponent.tsx +56 -0
  78. package/src/0.8/components/display/VideoComponent.tsx +31 -0
  79. package/src/0.8/components/display/display.test.tsx +660 -0
  80. package/src/0.8/components/display/index.ts +10 -0
  81. package/src/0.8/components/index.ts +14 -0
  82. package/src/0.8/components/interactive/ButtonComponent.tsx +44 -0
  83. package/src/0.8/components/interactive/CheckBoxComponent.tsx +45 -0
  84. package/src/0.8/components/interactive/DateTimeInputComponent.tsx +176 -0
  85. package/src/0.8/components/interactive/MultipleChoiceComponent.tsx +157 -0
  86. package/src/0.8/components/interactive/SliderComponent.tsx +53 -0
  87. package/src/0.8/components/interactive/TextFieldComponent.tsx +65 -0
  88. package/src/0.8/components/interactive/index.ts +10 -0
  89. package/src/0.8/components/interactive/interactive.test.tsx +618 -0
  90. package/src/0.8/components/layout/CardComponent.tsx +30 -0
  91. package/src/0.8/components/layout/ColumnComponent.tsx +93 -0
  92. package/src/0.8/components/layout/ListComponent.tsx +81 -0
  93. package/src/0.8/components/layout/ModalComponent.tsx +41 -0
  94. package/src/0.8/components/layout/RowComponent.tsx +94 -0
  95. package/src/0.8/components/layout/TabsComponent.tsx +59 -0
  96. package/src/0.8/components/layout/index.ts +10 -0
  97. package/src/0.8/components/layout/layout.test.tsx +558 -0
  98. package/src/0.8/contexts/A2UIProvider.test.tsx +226 -0
  99. package/src/0.8/contexts/A2UIProvider.tsx +54 -0
  100. package/src/0.8/contexts/ActionContext.test.tsx +242 -0
  101. package/src/0.8/contexts/ActionContext.tsx +105 -0
  102. package/src/0.8/contexts/ComponentsMapContext.tsx +125 -0
  103. package/src/0.8/contexts/DataModelContext.test.tsx +335 -0
  104. package/src/0.8/contexts/DataModelContext.tsx +184 -0
  105. package/src/0.8/contexts/SurfaceContext.test.tsx +339 -0
  106. package/src/0.8/contexts/SurfaceContext.tsx +197 -0
  107. package/src/0.8/hooks/useA2UIMessageHandler.test.tsx +399 -0
  108. package/src/0.8/hooks/useA2UIMessageHandler.ts +123 -0
  109. package/src/0.8/hooks/useComponent.test.tsx +148 -0
  110. package/src/0.8/hooks/useComponent.ts +39 -0
  111. package/src/0.8/hooks/useDataBinding.test.tsx +334 -0
  112. package/src/0.8/hooks/useDataBinding.ts +99 -0
  113. package/src/0.8/hooks/useDispatchAction.test.tsx +83 -0
  114. package/src/0.8/hooks/useDispatchAction.ts +35 -0
  115. package/src/0.8/hooks/useSurface.test.tsx +114 -0
  116. package/src/0.8/hooks/useSurface.ts +34 -0
  117. package/src/0.8/index.ts +38 -0
  118. package/src/0.8/schemas/client_to_server.json +50 -0
  119. package/src/0.8/schemas/server_to_client.json +148 -0
  120. package/src/0.8/schemas/standard_catalog_definition.json +661 -0
  121. package/src/0.8/types/index.ts +448 -0
  122. package/src/0.8/utils/dataBinding.test.ts +443 -0
  123. package/src/0.8/utils/dataBinding.ts +212 -0
  124. package/src/0.8/utils/pathUtils.test.ts +353 -0
  125. package/src/0.8/utils/pathUtils.ts +200 -0
  126. package/src/components/ui/button.tsx +62 -0
  127. package/src/components/ui/calendar.tsx +220 -0
  128. package/src/components/ui/card.tsx +92 -0
  129. package/src/components/ui/checkbox.tsx +30 -0
  130. package/src/components/ui/dialog.tsx +141 -0
  131. package/src/components/ui/input.tsx +21 -0
  132. package/src/components/ui/label.tsx +22 -0
  133. package/src/components/ui/native-select.tsx +53 -0
  134. package/src/components/ui/popover.tsx +46 -0
  135. package/src/components/ui/select.tsx +188 -0
  136. package/src/components/ui/separator.tsx +26 -0
  137. package/src/components/ui/slider.tsx +61 -0
  138. package/src/components/ui/tabs.tsx +64 -0
  139. package/src/components/ui/textarea.tsx +18 -0
  140. package/src/index.ts +1 -0
  141. package/src/lib/utils.ts +6 -0
  142. package/tsconfig.json +28 -0
  143. package/vite.config.ts +29 -0
  144. package/vitest.config.ts +22 -0
  145. package/vitest.setup.ts +8 -0
  146. package/website/README.md +4 -0
  147. package/website/assets/favicon.svg +8 -0
  148. package/website/content/.gitkeep +0 -0
  149. package/website/content/index.md +122 -0
  150. package/website/global.d.ts +9 -0
  151. package/website/package.json +17 -0
  152. package/website/plain.config.js +28 -0
  153. package/website/serve.json +6 -0
  154. package/website/src/client/color-mode-switch.css +47 -0
  155. package/website/src/client/index.js +61 -0
  156. package/website/src/client/moon.svg +1 -0
  157. package/website/src/client/sun.svg +1 -0
  158. package/website/src/components/Footer.jsx +9 -0
  159. package/website/src/components/Header.jsx +44 -0
  160. package/website/src/components/Page.jsx +28 -0
  161. package/website/src/global.css +423 -0
@@ -0,0 +1,123 @@
1
+ # Implementation Plan: A2UIRenderer Component Library
2
+
3
+ **Branch**: `001-a2ui-renderer` | **Date**: 2026-01-10 | **Spec**: [spec.md](./spec.md)
4
+ **Input**: Feature specification from `/specs/001-a2ui-renderer/spec.md`
5
+
6
+ ## Summary
7
+
8
+ Implement the A2UIRender component and public API exports for the A2UI React renderer library. The library already has substantial infrastructure implemented (contexts, hooks, component registry, default components). The remaining work focuses on:
9
+
10
+ 1. Creating the main `A2UIRender` component that matches the README.md API
11
+ 2. Setting up the versioned export path (`@easyops-cn/a2ui-react/0.8`)
12
+ 3. Adding development-mode placeholder for unknown components
13
+ 4. Ensuring all public types and hooks are properly exported
14
+
15
+ ## Technical Context
16
+
17
+ **Language/Version**: TypeScript 5.9, React 19
18
+ **Primary Dependencies**: React 19, Radix UI (for UI primitives), Tailwind CSS (via class-variance-authority)
19
+ **Storage**: N/A (client-side rendering library)
20
+ **Testing**: Vitest with @testing-library/react
21
+ **Target Platform**: Browser (ES2020+)
22
+ **Project Type**: Single library project
23
+ **Performance Goals**: <100ms action callback latency, 10+ levels nested rendering
24
+ **Constraints**: Must work with React 18+ (peer dependency allows ^19.0.0)
25
+ **Scale/Scope**: ~20 component types, single entry point
26
+
27
+ ## Constitution Check
28
+
29
+ _GATE: Must pass before Phase 0 research. Re-check after Phase 1 design._
30
+
31
+ The constitution template is not yet customized for this project. Proceeding with standard library development practices:
32
+
33
+ - [x] Library-first: Self-contained, independently testable
34
+ - [x] Test coverage: Vitest tests exist for contexts, hooks, and components
35
+ - [x] Type safety: Full TypeScript with strict mode
36
+ - [x] Documentation: JSDoc comments on all public APIs
37
+
38
+ ## Existing Implementation Analysis
39
+
40
+ ### Already Implemented
41
+
42
+ | Component/Module | Status | Location |
43
+ | ----------------------- | ----------- | ------------------------------------------------------------------ |
44
+ | **Contexts** | ✅ Complete | `src/0.8/contexts/` |
45
+ | - A2UIProvider | ✅ | Combines Surface, DataModel, Action providers |
46
+ | - SurfaceContext | ✅ | Surface state management |
47
+ | - DataModelContext | ✅ | Data model state management |
48
+ | - ActionContext | ✅ | Action dispatching |
49
+ | **Hooks** | ✅ Complete | `src/0.8/hooks/` |
50
+ | - useDataBinding | ✅ | Read-only data binding |
51
+ | - useFormBinding | ✅ | Two-way form binding |
52
+ | - useDispatchAction | ✅ | Action dispatching |
53
+ | - useComponent | ✅ | Component lookup |
54
+ | - useSurface | ✅ | Surface lookup |
55
+ | - useA2UIMessageHandler | ✅ | Message processing |
56
+ | **Components** | ✅ Complete | `src/0.8/components/` |
57
+ | - ComponentRenderer | ✅ | Component routing with registry |
58
+ | - Display (6 types) | ✅ | Text, Image, Icon, Video, AudioPlayer, Divider |
59
+ | - Layout (6 types) | ✅ | Row, Column, List, Card, Tabs, Modal |
60
+ | - Interactive (6 types) | ✅ | Button, CheckBox, TextField, DateTimeInput, MultipleChoice, Slider |
61
+ | **Types** | ✅ Complete | `src/0.8/types/index.ts` |
62
+ | **Utilities** | ✅ Complete | `src/0.8/utils/` |
63
+
64
+ ### Not Yet Implemented
65
+
66
+ | Component/Module | Status | Required For |
67
+ | ------------------------- | ---------- | ------------------------------------------- |
68
+ | **A2UIRender** | ❌ Missing | Main entry component per README.md |
69
+ | **index.ts exports** | ❌ Missing | Versioned path `@easyops-cn/a2ui-react/0.8` |
70
+ | **Dev-mode placeholder** | ❌ Missing | Unknown component handling per spec |
71
+ | **ComponentsMap support** | ❌ Missing | Custom component override per README.md |
72
+
73
+ ## Project Structure
74
+
75
+ ### Documentation (this feature)
76
+
77
+ ```text
78
+ specs/001-a2ui-renderer/
79
+ ├── plan.md # This file
80
+ ├── research.md # Phase 0 output
81
+ ├── data-model.md # Phase 1 output
82
+ ├── quickstart.md # Phase 1 output
83
+ ├── contracts/ # Phase 1 output (N/A - no API contracts)
84
+ └── tasks.md # Phase 2 output
85
+ ```
86
+
87
+ ### Source Code (repository root)
88
+
89
+ ```text
90
+ src/
91
+ ├── index.ts # Root export (existing)
92
+ └── 0.8/
93
+ ├── index.ts # NEW: Versioned public API exports
94
+ ├── A2UIRender.tsx # NEW: Main render component
95
+ ├── A2UIRender.test.tsx # NEW: Tests for A2UIRender
96
+ ├── contexts/ # Existing: Context providers
97
+ │ ├── A2UIProvider.tsx
98
+ │ ├── ActionContext.tsx
99
+ │ ├── DataModelContext.tsx
100
+ │ └── SurfaceContext.tsx
101
+ ├── hooks/ # Existing: React hooks
102
+ │ ├── useDataBinding.ts
103
+ │ ├── useDispatchAction.ts
104
+ │ ├── useComponent.ts
105
+ │ ├── useSurface.ts
106
+ │ └── useA2UIMessageHandler.ts
107
+ ├── components/ # Existing: Component implementations
108
+ │ ├── ComponentRenderer.tsx
109
+ │ ├── display/
110
+ │ ├── layout/
111
+ │ └── interactive/
112
+ ├── types/ # Existing: Type definitions
113
+ │ └── index.ts
114
+ └── utils/ # Existing: Utility functions
115
+ ├── dataBinding.ts
116
+ └── pathUtils.ts
117
+ ```
118
+
119
+ **Structure Decision**: Single library project structure. The existing codebase follows a well-organized module structure under `src/0.8/`. New files will be added at the same level.
120
+
121
+ ## Complexity Tracking
122
+
123
+ No constitution violations. The implementation follows the existing patterns and adds minimal new code.
@@ -0,0 +1,141 @@
1
+ # Quickstart: A2UIRenderer Component Library
2
+
3
+ **Date**: 2026-01-10
4
+ **Feature**: 001-a2ui-renderer
5
+
6
+ ## Installation
7
+
8
+ ```bash
9
+ npm install @easyops-cn/a2ui-react
10
+ # or
11
+ pnpm add @easyops-cn/a2ui-react
12
+ ```
13
+
14
+ ## Basic Usage
15
+
16
+ ```tsx
17
+ import { A2UIRender, A2UIMessage, A2UIAction } from '@easyops-cn/a2ui-react/0.8'
18
+
19
+ function App() {
20
+ const messages: A2UIMessage[] = [
21
+ // Messages from your A2UI server
22
+ ]
23
+
24
+ const handleAction = (action: A2UIAction) => {
25
+ console.log('Action received:', action)
26
+ // Handle the action (e.g., send to server)
27
+ }
28
+
29
+ return <A2UIRender messages={messages} onAction={handleAction} />
30
+ }
31
+ ```
32
+
33
+ ## Custom Components
34
+
35
+ Override default components or add new ones:
36
+
37
+ ```tsx
38
+ import {
39
+ A2UIRender,
40
+ useDispatchAction,
41
+ ComponentRenderer,
42
+ } from '@easyops-cn/a2ui-react/0.8'
43
+
44
+ // Custom Button component
45
+ function CustomButton({ surfaceId, componentId, child, action }) {
46
+ const dispatchAction = useDispatchAction()
47
+
48
+ const handleClick = () => {
49
+ if (action) {
50
+ dispatchAction(surfaceId, componentId, action)
51
+ }
52
+ }
53
+
54
+ return (
55
+ <button className="my-custom-button" onClick={handleClick}>
56
+ <ComponentRenderer surfaceId={surfaceId} componentId={child} />
57
+ </button>
58
+ )
59
+ }
60
+
61
+ // Register custom components
62
+ const ComponentsMap = new Map([['Button', CustomButton]])
63
+
64
+ function App() {
65
+ return (
66
+ <A2UIRender
67
+ components={ComponentsMap}
68
+ messages={messages}
69
+ onAction={handleAction}
70
+ />
71
+ )
72
+ }
73
+ ```
74
+
75
+ ## Data Binding in Custom Components
76
+
77
+ ```tsx
78
+ import { useDataBinding, useFormBinding } from '@easyops-cn/a2ui-react/0.8'
79
+
80
+ // Read-only binding
81
+ function DisplayComponent({ surfaceId, text }) {
82
+ const textValue = useDataBinding<string>(surfaceId, text, '')
83
+ return <span>{textValue}</span>
84
+ }
85
+
86
+ // Two-way binding for forms
87
+ function InputComponent({ surfaceId, value }) {
88
+ const [inputValue, setInputValue] = useFormBinding<string>(
89
+ surfaceId,
90
+ value,
91
+ ''
92
+ )
93
+
94
+ return (
95
+ <input value={inputValue} onChange={(e) => setInputValue(e.target.value)} />
96
+ )
97
+ }
98
+ ```
99
+
100
+ ## API Reference
101
+
102
+ ### A2UIRender Props
103
+
104
+ | Prop | Type | Required | Description |
105
+ | ------------ | ------------------------------ | -------- | ------------------------------------- |
106
+ | `messages` | `A2UIMessage[]` | Yes | Array of A2UI messages to render |
107
+ | `onAction` | `(action: A2UIAction) => void` | No | Callback when user triggers an action |
108
+ | `components` | `Map<string, ComponentType>` | No | Custom component overrides |
109
+
110
+ ### Hooks
111
+
112
+ | Hook | Signature | Description |
113
+ | ------------------- | --------------------------------------------------------- | --------------------------------------- |
114
+ | `useDispatchAction` | `() => (surfaceId, componentId, action) => void` | Dispatch actions from custom components |
115
+ | `useDataBinding` | `<T>(surfaceId, source, default?) => T` | Read data from the data model |
116
+ | `useFormBinding` | `<T>(surfaceId, source, default?) => [T, (v: T) => void]` | Two-way data binding |
117
+
118
+ ### Types
119
+
120
+ | Type | Description |
121
+ | ------------- | ------------------------------------------ |
122
+ | `A2UIMessage` | Server-to-client message |
123
+ | `A2UIAction` | Action payload sent to onAction callback |
124
+ | `Action` | Action definition in component props |
125
+ | `ValueSource` | Literal value or data model path reference |
126
+
127
+ ## Message Flow
128
+
129
+ ```
130
+ Server Client (A2UIRender)
131
+ │ │
132
+ │──── beginRendering ───────────────►│ Initialize surface
133
+ │ │
134
+ │──── surfaceUpdate ────────────────►│ Add/update components
135
+ │ │
136
+ │──── dataModelUpdate ──────────────►│ Update data model
137
+ │ │
138
+ │◄─── ActionPayload ────────────────│ User interaction
139
+ │ │
140
+ │──── deleteSurface ────────────────►│ Remove surface
141
+ ```
@@ -0,0 +1,140 @@
1
+ # Research: A2UIRenderer Component Library
2
+
3
+ **Date**: 2026-01-10
4
+ **Feature**: 001-a2ui-renderer
5
+
6
+ ## Overview
7
+
8
+ This document captures research findings for implementing the A2UIRender component and public API exports.
9
+
10
+ ## Decision 1: A2UIRender Component Architecture
11
+
12
+ **Decision**: Compose A2UIRender as a thin wrapper around existing infrastructure
13
+
14
+ **Rationale**:
15
+
16
+ - The existing `A2UIProvider` already combines all necessary context providers
17
+ - The existing `useA2UIMessageHandler` hook handles message processing
18
+ - The existing `ComponentRenderer` handles component routing
19
+ - A2UIRender only needs to:
20
+ 1. Accept `messages`, `onAction`, and optional `components` props
21
+ 2. Wrap children in A2UIProvider
22
+ 3. Process messages and render surfaces
23
+
24
+ **Alternatives considered**:
25
+
26
+ - Rewrite from scratch: Rejected - would duplicate existing tested code
27
+ - Extend A2UIProvider: Rejected - A2UIProvider is a context provider, not a renderer
28
+
29
+ ## Decision 2: ComponentsMap Implementation
30
+
31
+ **Decision**: Use React Context to pass custom components to ComponentRenderer
32
+
33
+ **Rationale**:
34
+
35
+ - The README.md shows `components` prop as `Map<string, React.ComponentType<any>>`
36
+ - ComponentRenderer already has a `componentRegistry` object
37
+ - Need to merge custom components with defaults at render time
38
+ - Context allows deep component tree access without prop drilling
39
+
40
+ **Alternatives considered**:
41
+
42
+ - Global registry mutation: Rejected - not React-friendly, causes side effects
43
+ - Prop drilling: Rejected - would require changes to all container components
44
+ - Module-level configuration: Rejected - not compatible with multiple A2UIRender instances
45
+
46
+ ## Decision 3: Unknown Component Handling
47
+
48
+ **Decision**: Use `process.env.NODE_ENV` to switch between dev placeholder and production skip
49
+
50
+ **Rationale**:
51
+
52
+ - Spec requires: "Render placeholder in development, skip in production"
53
+ - Standard React pattern for dev-only features
54
+ - Vite/bundlers automatically replace `process.env.NODE_ENV`
55
+ - No runtime overhead in production builds
56
+
57
+ **Alternatives considered**:
58
+
59
+ - Runtime configuration prop: Rejected - adds API complexity
60
+ - Always show placeholder: Rejected - spec requires production skip
61
+ - Always skip: Rejected - spec requires dev visibility
62
+
63
+ ## Decision 4: Export Structure for Versioned Path
64
+
65
+ **Decision**: Create `src/0.8/index.ts` as the entry point for `@easyops-cn/a2ui-react/0.8`
66
+
67
+ **Rationale**:
68
+
69
+ - package.json already defines: `"./0.8": { "default": "./dist/src/0.8/index.js" }`
70
+ - Single file makes it clear what's public API
71
+ - Re-exports from internal modules maintain encapsulation
72
+ - Matches README.md import pattern
73
+
74
+ **Alternatives considered**:
75
+
76
+ - index.ts in 0.8 folder: Rejected - index.ts is already configured in package.json
77
+ - Barrel exports from each module: Rejected - harder to control public surface
78
+
79
+ ## Decision 5: Surface Rendering Strategy
80
+
81
+ **Decision**: Render all surfaces from the surfaces Map, each with its root component
82
+
83
+ **Rationale**:
84
+
85
+ - Messages can create multiple surfaces
86
+ - Each surface has its own `root` component ID
87
+ - SurfaceContext already maintains `Map<string, Surface>`
88
+ - A2UIRender should render all active surfaces
89
+
90
+ **Alternatives considered**:
91
+
92
+ - Single surface only: Rejected - A2UI protocol supports multiple surfaces
93
+ - Surface selection prop: Could be added later if needed
94
+
95
+ ## Technical Findings
96
+
97
+ ### Existing Hook Signatures (from codebase)
98
+
99
+ ```typescript
100
+ // useDataBinding - matches spec FR-012
101
+ function useDataBinding<T>(
102
+ surfaceId: string,
103
+ source: ValueSource | undefined,
104
+ defaultValue?: T
105
+ ): T
106
+
107
+ // useFormBinding - matches spec FR-013
108
+ function useFormBinding<T>(
109
+ surfaceId: string,
110
+ source: ValueSource | undefined,
111
+ defaultValue?: T
112
+ ): [T, (value: T) => void]
113
+
114
+ // useDispatchAction - matches spec FR-011
115
+ function useDispatchAction(): (
116
+ surfaceId: string,
117
+ componentId: string,
118
+ action: Action
119
+ ) => void
120
+ ```
121
+
122
+ ### Type Exports Required (from spec)
123
+
124
+ - `A2UIMessage` - ✅ exists in types/index.ts
125
+ - `A2UIAction` - ❌ needs alias (currently `ActionPayload`)
126
+ - `A2UIRender` - ❌ needs implementation
127
+ - `ComponentRenderer` - ✅ exists
128
+ - `useDispatchAction` - ✅ exists
129
+ - `useDataBinding` - ✅ exists
130
+ - `useFormBinding` - ✅ exists
131
+
132
+ ### API Alignment with README.md
133
+
134
+ The README.md shows:
135
+
136
+ ```tsx
137
+ import { A2UIRender, A2UIMessage, A2UIAction } from '@easyops-cn/a2ui-react/0.8'
138
+ ```
139
+
140
+ Current types use `ActionPayload` instead of `A2UIAction`. The index.ts should export `ActionPayload as A2UIAction` for API consistency.
@@ -0,0 +1,165 @@
1
+ # Feature Specification: A2UIRenderer Component Library
2
+
3
+ **Feature Branch**: `001-a2ui-renderer`
4
+ **Created**: 2026-01-10
5
+ **Status**: Draft
6
+ **Input**: User description: "按 README.md 的下游用户使用示例实现 A2UIRenderer"
7
+
8
+ ## Clarifications
9
+
10
+ ### Session 2026-01-10
11
+
12
+ - Q: How should unknown component types be handled? → A: Render placeholder in development, skip in production
13
+ - Q: Where does the A2UIMessage schema come from? → A: Define schema internally (this library owns the type definitions)
14
+
15
+ ## User Scenarios & Testing _(mandatory)_
16
+
17
+ ### User Story 1 - Basic Message Rendering (Priority: P1)
18
+
19
+ As a developer, I want to render A2UI messages using the A2UIRender component so that I can display dynamic UI content from A2UI protocol messages.
20
+
21
+ **Why this priority**: This is the core functionality - without basic rendering, no other features can work. It enables the fundamental use case of displaying A2UI messages.
22
+
23
+ **Independent Test**: Can be fully tested by passing an array of A2UIMessage objects to A2UIRender and verifying the UI renders correctly. Delivers immediate value by enabling basic A2UI integration.
24
+
25
+ **Acceptance Scenarios**:
26
+
27
+ 1. **Given** an empty messages array, **When** A2UIRender is rendered, **Then** no UI components are displayed
28
+ 2. **Given** a messages array with valid A2UIMessage objects, **When** A2UIRender is rendered, **Then** all message components are displayed in order
29
+ 3. **Given** a messages array with nested components, **When** A2UIRender is rendered, **Then** nested components are rendered correctly within their parent containers
30
+
31
+ ---
32
+
33
+ ### User Story 2 - Action Handling (Priority: P1)
34
+
35
+ As a developer, I want to receive action callbacks when users interact with components so that I can respond to user interactions and update application state.
36
+
37
+ **Why this priority**: Actions are essential for interactive UIs. Without action handling, the rendered UI would be static and non-functional.
38
+
39
+ **Independent Test**: Can be fully tested by clicking interactive components and verifying the onAction callback receives the correct A2UIAction payload.
40
+
41
+ **Acceptance Scenarios**:
42
+
43
+ 1. **Given** a component with an action defined, **When** the user triggers the action (e.g., clicks a button), **Then** the onAction callback is invoked with the correct A2UIAction object
44
+ 2. **Given** the onAction callback, **When** an action is dispatched, **Then** the callback receives surfaceId, componentId, and action payload
45
+ 3. **Given** multiple interactive components, **When** different components are clicked, **Then** each dispatches its own unique action correctly
46
+
47
+ ---
48
+
49
+ ### User Story 3 - Custom Component Override (Priority: P2)
50
+
51
+ As a developer, I want to override default components with custom implementations so that I can customize the look and behavior of rendered UI elements.
52
+
53
+ **Why this priority**: Customization is important for branding and specific UX requirements, but the library should work with defaults first.
54
+
55
+ **Independent Test**: Can be fully tested by providing a ComponentsMap with a custom Button component and verifying the custom component renders instead of the default.
56
+
57
+ **Acceptance Scenarios**:
58
+
59
+ 1. **Given** a ComponentsMap with a custom Button component, **When** A2UIRender renders a Button, **Then** the custom Button component is used instead of the default
60
+ 2. **Given** a ComponentsMap with multiple custom components, **When** A2UIRender renders those component types, **Then** each custom component is used appropriately
61
+ 3. **Given** a ComponentsMap that does not override a component type, **When** A2UIRender renders that component type, **Then** the default component is used
62
+
63
+ ---
64
+
65
+ ### User Story 4 - Custom Component Creation (Priority: P2)
66
+
67
+ As a developer, I want to add new custom component types that don't exist in the default set so that I can extend the UI capabilities beyond the built-in components.
68
+
69
+ **Why this priority**: Extensibility allows the library to support domain-specific components, but basic functionality must work first.
70
+
71
+ **Independent Test**: Can be fully tested by adding a new component type (e.g., Switch) to ComponentsMap and verifying it renders when that component type appears in messages.
72
+
73
+ **Acceptance Scenarios**:
74
+
75
+ 1. **Given** a ComponentsMap with a new component type "Switch", **When** A2UIRender encounters a Switch component in messages, **Then** the custom Switch component is rendered
76
+ 2. **Given** a custom component using useDispatchAction hook, **When** the component dispatches an action, **Then** the action flows through the onAction callback correctly
77
+
78
+ ---
79
+
80
+ ### User Story 5 - Data Binding in Custom Components (Priority: P3)
81
+
82
+ As a developer, I want to use data binding hooks in custom components so that I can read dynamic values from the A2UI data context.
83
+
84
+ **Why this priority**: Data binding enables dynamic content, but basic rendering and actions are more fundamental.
85
+
86
+ **Independent Test**: Can be fully tested by creating a custom component that uses useDataBinding and verifying it displays the bound value.
87
+
88
+ **Acceptance Scenarios**:
89
+
90
+ 1. **Given** a custom component using useDataBinding hook, **When** the component renders, **Then** it displays the bound data value
91
+ 2. **Given** a data binding with a default value, **When** the bound path has no data, **Then** the default value is used
92
+
93
+ ---
94
+
95
+ ### User Story 6 - Form Binding in Custom Components (Priority: P3)
96
+
97
+ As a developer, I want to use form binding hooks in custom components so that I can create two-way data binding for form inputs.
98
+
99
+ **Why this priority**: Form binding enables interactive forms, building on top of basic data binding.
100
+
101
+ **Independent Test**: Can be fully tested by creating a custom Switch component that uses useFormBinding and verifying value changes are reflected.
102
+
103
+ **Acceptance Scenarios**:
104
+
105
+ 1. **Given** a custom component using useFormBinding hook, **When** the component renders, **Then** it displays the current bound value
106
+ 2. **Given** a custom component using useFormBinding hook, **When** the user changes the value, **Then** the bound value is updated
107
+ 3. **Given** a form binding with a default value, **When** the bound path has no data, **Then** the default value is used
108
+
109
+ ---
110
+
111
+ ### Edge Cases
112
+
113
+ - What happens when messages array is null or undefined? System should handle gracefully without crashing
114
+ - What happens when a component type in messages has no matching renderer? System renders a visible placeholder in development mode to aid debugging, and silently skips the component in production mode
115
+ - What happens when an action is dispatched but no onAction callback is provided? System should handle gracefully
116
+ - What happens when useDataBinding references a non-existent path? System should return the default value
117
+ - What happens when ComponentsMap contains invalid component references? System should fall back to defaults or skip
118
+
119
+ ## Requirements _(mandatory)_
120
+
121
+ ### Functional Requirements
122
+
123
+ - **FR-001**: System MUST export A2UIRender component that accepts messages and onAction props
124
+ - **FR-002**: System MUST export A2UIMessage type for defining message structures
125
+ - **FR-003**: System MUST export A2UIAction type for defining action payloads
126
+ - **FR-004**: A2UIRender MUST render all components defined in the messages array
127
+ - **FR-005**: A2UIRender MUST invoke onAction callback when user interactions trigger actions
128
+ - **FR-006**: A2UIRender MUST accept an optional components prop (ComponentsMap) for custom component overrides
129
+ - **FR-007**: System MUST export useDispatchAction hook for custom components to dispatch actions
130
+ - **FR-008**: System MUST export ComponentRenderer component for rendering child components within custom components
131
+ - **FR-009**: System MUST export useDataBinding hook for reading data from the A2UI context
132
+ - **FR-010**: System MUST export useFormBinding hook for two-way data binding in form components
133
+ - **FR-011**: useDispatchAction hook MUST accept surfaceId, componentId, and action parameters
134
+ - **FR-012**: useDataBinding hook MUST accept surfaceId, binding path, and default value parameters
135
+ - **FR-013**: useFormBinding hook MUST return a tuple of [currentValue, setValue] for two-way binding
136
+ - **FR-014**: ComponentRenderer MUST accept surfaceId and componentId props to render specific components
137
+ - **FR-015**: Custom components MUST receive surfaceId and componentId as props for context identification
138
+
139
+ ### Key Entities
140
+
141
+ - **A2UIMessage**: Represents a message containing UI component definitions to be rendered. Contains component tree structure and associated data.
142
+ - **A2UIAction**: Represents an action payload triggered by user interaction. Contains action type and associated data for the callback.
143
+ - **ComponentsMap**: A Map structure mapping component type names (strings) to React component implementations. Used for overriding defaults and adding custom components.
144
+ - **Surface**: A rendering context identified by surfaceId that contains components and their associated data bindings.
145
+ - **Component**: An individual UI element within a surface, identified by componentId, with properties specific to its type.
146
+
147
+ ## Success Criteria _(mandatory)_
148
+
149
+ ### Measurable Outcomes
150
+
151
+ - **SC-001**: Developers can render A2UI messages with a single component import and minimal configuration (under 10 lines of code for basic usage)
152
+ - **SC-002**: All user interactions on rendered components trigger the onAction callback within 100ms of user input
153
+ - **SC-003**: Custom component overrides work without modifying library source code
154
+ - **SC-004**: Custom components can access all necessary hooks (useDispatchAction, useDataBinding, useFormBinding) from the library exports
155
+ - **SC-005**: The library exports are accessible via the versioned path '@easyops-cn/a2ui-react/0.8'
156
+ - **SC-006**: Nested component rendering works to at least 10 levels deep without performance degradation
157
+ - **SC-007**: Form bindings reflect value changes immediately (within one render cycle)
158
+
159
+ ## Assumptions
160
+
161
+ - This library defines and owns the A2UI protocol message format types (A2UIMessage, A2UIAction, Surface, Component schemas)
162
+ - React 18+ is the target runtime environment
163
+ - TypeScript types are required for all public exports
164
+ - Default components will be provided for common UI elements (Button, Text, etc.)
165
+ - The versioned export path pattern (@easyops-cn/a2ui-react/0.8) follows npm package subpath exports convention