@easyops-cn/a2ui-react 0.0.0 → 0.0.1

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.
package/CLAUDE.md CHANGED
@@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
4
4
 
5
5
  ## Project Overview
6
6
 
7
- A2UI React Renderer Library (`@easyops-cn/a2ui-react`) - A React implementation for rendering A2UI protocol. This is a library package (not an application) that downstream developers consume.
7
+ A2UI React Renderer (`@easyops-cn/a2ui-react`) - A React implementation for rendering A2UI protocol. This is a library package (not an application) that downstream developers consume.
8
8
 
9
9
  ## Monorepo Structure
10
10
 
@@ -43,11 +43,15 @@ npm run build -w playground # Build playground
43
43
 
44
44
  ## Library Usage (Downstream API)
45
45
 
46
- The primary entry point is `A2UIRender` component:
46
+ The primary entry point is `A2UIRenderer` component:
47
47
 
48
48
  ```tsx
49
- import { A2UIRender, A2UIMessage, A2UIAction } from '@easyops-cn/a2ui-react/0.8'
50
- ;<A2UIRender messages={messages} onAction={handleAction} />
49
+ import {
50
+ A2UIRenderer,
51
+ A2UIMessage,
52
+ A2UIAction,
53
+ } from '@easyops-cn/a2ui-react/0.8'
54
+ ;<A2UIRenderer messages={messages} onAction={handleAction} />
51
55
  ```
52
56
 
53
57
  Custom components can override defaults or add new ones via `components` prop (Map<string, React.ComponentType>).
@@ -58,7 +62,7 @@ Custom components use hooks: `useDispatchAction`, `useDataBinding`, `useFormBind
58
62
 
59
63
  ```javascript
60
64
  import { v0_8 } from '@easyops-cn/a2ui-react' // Main namespace export
61
- import { ... } from '@easyops-cn/a2ui-react/0.8' // Core module (A2UIRender, hooks, types)
65
+ import { ... } from '@easyops-cn/a2ui-react/0.8' // Core module (A2UIRenderer, hooks, types)
62
66
  ```
63
67
 
64
68
  ## Architecture
package/CONTRIBUTING.md CHANGED
@@ -4,7 +4,7 @@ Thank you for your interest in contributing to `@easyops-cn/a2ui-react`!
4
4
 
5
5
  ## Project Overview
6
6
 
7
- A2UI React Renderer Library - A React implementation for rendering A2UI protocol. This is a library package that downstream developers consume.
7
+ A2UI React Renderer - A React implementation for rendering A2UI protocol. This is a library package that downstream developers consume.
8
8
 
9
9
  ## Monorepo Structure
10
10
 
package/README.md CHANGED
@@ -1,6 +1,4 @@
1
- # A2UI React Renderer Library
2
-
3
- https://easyops-cn.github.io/a2ui-react/
1
+ # A2UI React Renderer
4
2
 
5
3
  A React renderer library for [A2UI](https://a2ui.org) protocol.
6
4
 
@@ -8,6 +6,8 @@ Supports all components in A2UI standard catalog out of the box. Built with [sha
8
6
 
9
7
  Currently A2UI protocol v0.8 is fully supported. Work on v0.9 is in progress.
10
8
 
9
+ [Docs](https://easyops-cn.github.io/a2ui-react/) | [Playground](https://easyops-cn.github.io/a2ui-react/playground/)
10
+
11
11
  ## Installation
12
12
 
13
13
  ```sh
@@ -22,11 +22,11 @@ First, use the `@source` directive to tell Tailwind to scan the library code for
22
22
  @source "../node_modules/@easyops-cn/a2ui-react";
23
23
  ```
24
24
 
25
- Next, use the `A2UIRender` component to render A2UI messages:
25
+ Next, use the `A2UIRenderer` component to render A2UI messages:
26
26
 
27
27
  ```tsx
28
28
  import {
29
- A2UIRender,
29
+ A2UIRenderer,
30
30
  type A2UIMessage,
31
31
  type A2UIAction,
32
32
  } from '@easyops-cn/a2ui-react/0.8'
@@ -38,7 +38,7 @@ function App() {
38
38
  console.log('Action received:', action)
39
39
  }
40
40
 
41
- return <A2UIRender messages={messages} onAction={handleAction} />
41
+ return <A2UIRenderer messages={messages} onAction={handleAction} />
42
42
  }
43
43
  ```
44
44
 
@@ -47,7 +47,11 @@ function App() {
47
47
  You can override default components or add new custom components via the `components` prop, which takes a `Map<string, React.ComponentType>`.
48
48
 
49
49
  ```tsx
50
- import { A2UIRender, A2UIMessage, A2UIAction } from '@easyops-cn/a2ui-react/0.8'
50
+ import {
51
+ A2UIRenderer,
52
+ A2UIMessage,
53
+ A2UIAction,
54
+ } from '@easyops-cn/a2ui-react/0.8'
51
55
 
52
56
  const ComponentsMap = new Map<string, React.ComponentType<any>>([
53
57
  // Override default Button component with a custom one
@@ -59,7 +63,7 @@ const ComponentsMap = new Map<string, React.ComponentType<any>>([
59
63
 
60
64
  function App() {
61
65
  return (
62
- <A2UIRender
66
+ <A2UIRenderer
63
67
  components={ComponentsMap}
64
68
  messages={messages}
65
69
  onAction={handleAction}
package/package.json CHANGED
@@ -1,7 +1,12 @@
1
1
  {
2
2
  "name": "@easyops-cn/a2ui-react",
3
- "version": "0.0.0",
4
- "description": "A2UI React Renderer Library",
3
+ "version": "0.0.1",
4
+ "description": "A2UI React Renderer",
5
+ "homepage": "https://easyops-cn.github.io/a2ui-react/",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git@github.com:easyops-cn/a2ui-react.git"
9
+ },
5
10
  "type": "module",
6
11
  "main": "./dist/index.js",
7
12
  "types": "./dist/index.d.ts",
@@ -14,6 +14,7 @@ export function Header({ title, children }: HeaderProps) {
14
14
  <div className="header-right">
15
15
  <nav className="header-nav">
16
16
  <a href="/a2ui-react/">Docs</a>
17
+ <a href="/a2ui-react/api/">API Reference</a>
17
18
  <a href="/a2ui-react/playground/" className="active">
18
19
  Playground
19
20
  </a>
@@ -1,6 +1,6 @@
1
1
  import { Component, type ReactNode } from 'react'
2
2
  import {
3
- A2UIRender,
3
+ A2UIRenderer,
4
4
  type A2UIMessage,
5
5
  type A2UIAction,
6
6
  } from '@easyops-cn/a2ui-react/0.8'
@@ -72,7 +72,7 @@ export function Preview({ messages, error, onAction }: PreviewProps) {
72
72
 
73
73
  return (
74
74
  <ErrorBoundary>
75
- <A2UIRender messages={messages} onAction={onAction} />
75
+ <A2UIRenderer messages={messages} onAction={onAction} />
76
76
  </ErrorBoundary>
77
77
  )
78
78
  }
@@ -34,7 +34,7 @@
34
34
  - All items pass validation
35
35
  - Specification is ready for `/speckit.clarify` or `/speckit.plan`
36
36
  - The spec covers all usage patterns shown in README.md:
37
- - Basic rendering with A2UIRender component
37
+ - Basic rendering with A2UIRenderer component
38
38
  - Action handling via onAction callback
39
39
  - Custom component overrides via ComponentsMap
40
40
  - Custom component creation with hooks (useDispatchAction, useDataBinding, useFormBinding)
@@ -5,9 +5,9 @@
5
5
 
6
6
  ## Summary
7
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:
8
+ Implement the A2UIRenderer 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
9
 
10
- 1. Creating the main `A2UIRender` component that matches the README.md API
10
+ 1. Creating the main `A2UIRenderer` component that matches the README.md API
11
11
  2. Setting up the versioned export path (`@easyops-cn/a2ui-react/0.8`)
12
12
  3. Adding development-mode placeholder for unknown components
13
13
  4. Ensuring all public types and hooks are properly exported
@@ -65,7 +65,7 @@ The constitution template is not yet customized for this project. Proceeding wit
65
65
 
66
66
  | Component/Module | Status | Required For |
67
67
  | ------------------------- | ---------- | ------------------------------------------- |
68
- | **A2UIRender** | ❌ Missing | Main entry component per README.md |
68
+ | **A2UIRenderer** | ❌ Missing | Main entry component per README.md |
69
69
  | **index.ts exports** | ❌ Missing | Versioned path `@easyops-cn/a2ui-react/0.8` |
70
70
  | **Dev-mode placeholder** | ❌ Missing | Unknown component handling per spec |
71
71
  | **ComponentsMap support** | ❌ Missing | Custom component override per README.md |
@@ -91,8 +91,8 @@ src/
91
91
  ├── index.ts # Root export (existing)
92
92
  └── 0.8/
93
93
  ├── index.ts # NEW: Versioned public API exports
94
- ├── A2UIRender.tsx # NEW: Main render component
95
- ├── A2UIRender.test.tsx # NEW: Tests for A2UIRender
94
+ ├── A2UIRenderer.tsx # NEW: Main render component
95
+ ├── A2UIRenderer.test.tsx # NEW: Tests for A2UIRenderer
96
96
  ├── contexts/ # Existing: Context providers
97
97
  │ ├── A2UIProvider.tsx
98
98
  │ ├── ActionContext.tsx
@@ -14,7 +14,11 @@ pnpm add @easyops-cn/a2ui-react
14
14
  ## Basic Usage
15
15
 
16
16
  ```tsx
17
- import { A2UIRender, A2UIMessage, A2UIAction } from '@easyops-cn/a2ui-react/0.8'
17
+ import {
18
+ A2UIRenderer,
19
+ A2UIMessage,
20
+ A2UIAction,
21
+ } from '@easyops-cn/a2ui-react/0.8'
18
22
 
19
23
  function App() {
20
24
  const messages: A2UIMessage[] = [
@@ -26,7 +30,7 @@ function App() {
26
30
  // Handle the action (e.g., send to server)
27
31
  }
28
32
 
29
- return <A2UIRender messages={messages} onAction={handleAction} />
33
+ return <A2UIRenderer messages={messages} onAction={handleAction} />
30
34
  }
31
35
  ```
32
36
 
@@ -36,7 +40,7 @@ Override default components or add new ones:
36
40
 
37
41
  ```tsx
38
42
  import {
39
- A2UIRender,
43
+ A2UIRenderer,
40
44
  useDispatchAction,
41
45
  ComponentRenderer,
42
46
  } from '@easyops-cn/a2ui-react/0.8'
@@ -63,7 +67,7 @@ const ComponentsMap = new Map([['Button', CustomButton]])
63
67
 
64
68
  function App() {
65
69
  return (
66
- <A2UIRender
70
+ <A2UIRenderer
67
71
  components={ComponentsMap}
68
72
  messages={messages}
69
73
  onAction={handleAction}
@@ -99,7 +103,7 @@ function InputComponent({ surfaceId, value }) {
99
103
 
100
104
  ## API Reference
101
105
 
102
- ### A2UIRender Props
106
+ ### A2UIRenderer Props
103
107
 
104
108
  | Prop | Type | Required | Description |
105
109
  | ------------ | ------------------------------ | -------- | ------------------------------------- |
@@ -127,7 +131,7 @@ function InputComponent({ surfaceId, value }) {
127
131
  ## Message Flow
128
132
 
129
133
  ```
130
- Server Client (A2UIRender)
134
+ Server Client (A2UIRenderer)
131
135
  │ │
132
136
  │──── beginRendering ───────────────►│ Initialize surface
133
137
  │ │
@@ -5,18 +5,18 @@
5
5
 
6
6
  ## Overview
7
7
 
8
- This document captures research findings for implementing the A2UIRender component and public API exports.
8
+ This document captures research findings for implementing the A2UIRenderer component and public API exports.
9
9
 
10
- ## Decision 1: A2UIRender Component Architecture
10
+ ## Decision 1: A2UIRenderer Component Architecture
11
11
 
12
- **Decision**: Compose A2UIRender as a thin wrapper around existing infrastructure
12
+ **Decision**: Compose A2UIRenderer as a thin wrapper around existing infrastructure
13
13
 
14
14
  **Rationale**:
15
15
 
16
16
  - The existing `A2UIProvider` already combines all necessary context providers
17
17
  - The existing `useA2UIMessageHandler` hook handles message processing
18
18
  - The existing `ComponentRenderer` handles component routing
19
- - A2UIRender only needs to:
19
+ - A2UIRenderer only needs to:
20
20
  1. Accept `messages`, `onAction`, and optional `components` props
21
21
  2. Wrap children in A2UIProvider
22
22
  3. Process messages and render surfaces
@@ -41,7 +41,7 @@ This document captures research findings for implementing the A2UIRender compone
41
41
 
42
42
  - Global registry mutation: Rejected - not React-friendly, causes side effects
43
43
  - Prop drilling: Rejected - would require changes to all container components
44
- - Module-level configuration: Rejected - not compatible with multiple A2UIRender instances
44
+ - Module-level configuration: Rejected - not compatible with multiple A2UIRenderer instances
45
45
 
46
46
  ## Decision 3: Unknown Component Handling
47
47
 
@@ -85,7 +85,7 @@ This document captures research findings for implementing the A2UIRender compone
85
85
  - Messages can create multiple surfaces
86
86
  - Each surface has its own `root` component ID
87
87
  - SurfaceContext already maintains `Map<string, Surface>`
88
- - A2UIRender should render all active surfaces
88
+ - A2UIRenderer should render all active surfaces
89
89
 
90
90
  **Alternatives considered**:
91
91
 
@@ -123,7 +123,7 @@ function useDispatchAction(): (
123
123
 
124
124
  - `A2UIMessage` - ✅ exists in types/index.ts
125
125
  - `A2UIAction` - ❌ needs alias (currently `ActionPayload`)
126
- - `A2UIRender` - ❌ needs implementation
126
+ - `A2UIRenderer` - ❌ needs implementation
127
127
  - `ComponentRenderer` - ✅ exists
128
128
  - `useDispatchAction` - ✅ exists
129
129
  - `useDataBinding` - ✅ exists
@@ -134,7 +134,11 @@ function useDispatchAction(): (
134
134
  The README.md shows:
135
135
 
136
136
  ```tsx
137
- import { A2UIRender, A2UIMessage, A2UIAction } from '@easyops-cn/a2ui-react/0.8'
137
+ import {
138
+ A2UIRenderer,
139
+ A2UIMessage,
140
+ A2UIAction,
141
+ } from '@easyops-cn/a2ui-react/0.8'
138
142
  ```
139
143
 
140
144
  Current types use `ActionPayload` instead of `A2UIAction`. The index.ts should export `ActionPayload as A2UIAction` for API consistency.
@@ -16,17 +16,17 @@
16
16
 
17
17
  ### User Story 1 - Basic Message Rendering (Priority: P1)
18
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.
19
+ As a developer, I want to render A2UI messages using the A2UIRenderer component so that I can display dynamic UI content from A2UI protocol messages.
20
20
 
21
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
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.
23
+ **Independent Test**: Can be fully tested by passing an array of A2UIMessage objects to A2UIRenderer and verifying the UI renders correctly. Delivers immediate value by enabling basic A2UI integration.
24
24
 
25
25
  **Acceptance Scenarios**:
26
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
27
+ 1. **Given** an empty messages array, **When** A2UIRenderer is rendered, **Then** no UI components are displayed
28
+ 2. **Given** a messages array with valid A2UIMessage objects, **When** A2UIRenderer is rendered, **Then** all message components are displayed in order
29
+ 3. **Given** a messages array with nested components, **When** A2UIRenderer is rendered, **Then** nested components are rendered correctly within their parent containers
30
30
 
31
31
  ---
32
32
 
@@ -56,9 +56,9 @@ As a developer, I want to override default components with custom implementation
56
56
 
57
57
  **Acceptance Scenarios**:
58
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
59
+ 1. **Given** a ComponentsMap with a custom Button component, **When** A2UIRenderer renders a Button, **Then** the custom Button component is used instead of the default
60
+ 2. **Given** a ComponentsMap with multiple custom components, **When** A2UIRenderer renders those component types, **Then** each custom component is used appropriately
61
+ 3. **Given** a ComponentsMap that does not override a component type, **When** A2UIRenderer renders that component type, **Then** the default component is used
62
62
 
63
63
  ---
64
64
 
@@ -72,7 +72,7 @@ As a developer, I want to add new custom component types that don't exist in the
72
72
 
73
73
  **Acceptance Scenarios**:
74
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
75
+ 1. **Given** a ComponentsMap with a new component type "Switch", **When** A2UIRenderer encounters a Switch component in messages, **Then** the custom Switch component is rendered
76
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
77
 
78
78
  ---
@@ -120,12 +120,12 @@ As a developer, I want to use form binding hooks in custom components so that I
120
120
 
121
121
  ### Functional Requirements
122
122
 
123
- - **FR-001**: System MUST export A2UIRender component that accepts messages and onAction props
123
+ - **FR-001**: System MUST export A2UIRenderer component that accepts messages and onAction props
124
124
  - **FR-002**: System MUST export A2UIMessage type for defining message structures
125
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
126
+ - **FR-004**: A2UIRenderer MUST render all components defined in the messages array
127
+ - **FR-005**: A2UIRenderer MUST invoke onAction callback when user interactions trigger actions
128
+ - **FR-006**: A2UIRenderer MUST accept an optional components prop (ComponentsMap) for custom component overrides
129
129
  - **FR-007**: System MUST export useDispatchAction hook for custom components to dispatch actions
130
130
  - **FR-008**: System MUST export ComponentRenderer component for rendering child components within custom components
131
131
  - **FR-009**: System MUST export useDataBinding hook for reading data from the A2UI context
@@ -16,7 +16,7 @@
16
16
  ## Path Conventions
17
17
 
18
18
  - **Single library project**: `src/0.8/` at repository root
19
- - Tests co-located with source files (e.g., `A2UIRender.test.tsx`)
19
+ - Tests co-located with source files (e.g., `A2UIRenderer.test.tsx`)
20
20
 
21
21
  ---
22
22
 
@@ -46,30 +46,30 @@
46
46
  - [x] T011 Add dev-mode placeholder component for unknown types in src/0.8/components/UnknownComponent.tsx
47
47
  - [x] T012 Update ComponentRenderer to render UnknownComponent in dev mode, skip in production
48
48
 
49
- **Checkpoint**: Foundation ready - A2UIRender component can now be implemented
49
+ **Checkpoint**: Foundation ready - A2UIRenderer component can now be implemented
50
50
 
51
51
  ---
52
52
 
53
53
  ## Phase 3: User Story 1 - Basic Message Rendering (Priority: P1) 🎯 MVP
54
54
 
55
- **Goal**: Render A2UI messages using the A2UIRender component
55
+ **Goal**: Render A2UI messages using the A2UIRenderer component
56
56
 
57
- **Independent Test**: Pass an array of A2UIMessage objects to A2UIRender and verify the UI renders correctly
57
+ **Independent Test**: Pass an array of A2UIMessage objects to A2UIRenderer and verify the UI renders correctly
58
58
 
59
59
  ### Tests for User Story 1
60
60
 
61
- - [x] T013 [P] [US1] Create test file src/0.8/A2UIRender.test.tsx with test setup
61
+ - [x] T013 [P] [US1] Create test file src/0.8/A2UIRenderer.test.tsx with test setup
62
62
  - [x] T014 [P] [US1] Add test: renders nothing for empty messages array
63
63
  - [x] T015 [P] [US1] Add test: renders components from valid A2UIMessage objects
64
64
  - [x] T016 [P] [US1] Add test: renders nested components correctly
65
65
 
66
66
  ### Implementation for User Story 1
67
67
 
68
- - [x] T017 [US1] Create A2UIRender component in src/0.8/A2UIRender.tsx
68
+ - [x] T017 [US1] Create A2UIRenderer component in src/0.8/A2UIRenderer.tsx
69
69
  - [x] T018 [US1] Implement messages prop handling with useA2UIMessageHandler
70
70
  - [x] T019 [US1] Implement surface rendering loop (render all surfaces from context)
71
71
  - [x] T020 [US1] Add null/undefined messages handling (graceful empty render)
72
- - [x] T021 [US1] Export A2UIRender from src/0.8/index.ts
72
+ - [x] T021 [US1] Export A2UIRenderer from src/0.8/index.ts
73
73
 
74
74
  **Checkpoint**: User Story 1 complete - basic rendering works independently
75
75
 
@@ -89,8 +89,8 @@
89
89
 
90
90
  ### Implementation for User Story 2
91
91
 
92
- - [x] T025 [US2] Add onAction prop to A2UIRender component in src/0.8/A2UIRender.tsx
93
- - [x] T026 [US2] Pass onAction to A2UIProvider in A2UIRender
92
+ - [x] T025 [US2] Add onAction prop to A2UIRenderer component in src/0.8/A2UIRenderer.tsx
93
+ - [x] T026 [US2] Pass onAction to A2UIProvider in A2UIRenderer
94
94
  - [x] T027 [US2] Add test: no error when action dispatched without onAction callback
95
95
 
96
96
  **Checkpoint**: User Stories 1 AND 2 complete - rendering and actions work
@@ -111,7 +111,7 @@
111
111
 
112
112
  ### Implementation for User Story 3
113
113
 
114
- - [x] T031 [US3] Add components prop (ComponentsMap) to A2UIRender in src/0.8/A2UIRender.tsx
114
+ - [x] T031 [US3] Add components prop (ComponentsMap) to A2UIRenderer in src/0.8/A2UIRenderer.tsx
115
115
  - [x] T032 [US3] Wrap rendering with ComponentsMapProvider passing custom components
116
116
  - [x] T033 [US3] Merge custom components with defaults in ComponentsMapContext
117
117
 
@@ -199,7 +199,7 @@
199
199
  - **Setup (Phase 1)**: No dependencies - can start immediately
200
200
  - **Foundational (Phase 2)**: Depends on Setup completion - BLOCKS user stories 3-4
201
201
  - **User Story 1 (Phase 3)**: Depends on Setup (Phase 1) only
202
- - **User Story 2 (Phase 4)**: Depends on User Story 1 (needs A2UIRender component)
202
+ - **User Story 2 (Phase 4)**: Depends on User Story 1 (needs A2UIRenderer component)
203
203
  - **User Story 3 (Phase 5)**: Depends on Foundational (Phase 2) for ComponentsMapContext
204
204
  - **User Story 4 (Phase 6)**: Depends on User Story 3 (extends ComponentsMap support)
205
205
  - **User Story 5 (Phase 7)**: Depends on Setup (Phase 1) - hooks already exist
@@ -115,6 +115,6 @@ type ThemeToggleHandler = () => void
115
115
  ### Action Dispatch Event
116
116
 
117
117
  ```typescript
118
- // Triggered when A2UIRender dispatches an action
118
+ // Triggered when A2UIRenderer dispatches an action
119
119
  type ActionHandler = (action: A2UIAction) => void
120
120
  ```
@@ -72,7 +72,7 @@ JSON.parse() attempted
72
72
  │ parsedMessages = result │ parsedMessages = null
73
73
  │ parseError = null │ parseError = error.message
74
74
  │ ↓ │ ↓
75
- A2UIRender receives messages │ ErrorDisplay shown
75
+ A2UIRenderer receives messages │ ErrorDisplay shown
76
76
  └─────────────────────────────────────┘
77
77
  ```
78
78
 
@@ -124,7 +124,7 @@ UI re-renders with new theme
124
124
  │ renders
125
125
 
126
126
  ┌─────────────┐
127
- A2UIRender
127
+ A2UIRenderer
128
128
  └─────────────┘
129
129
  ```
130
130
 
@@ -49,7 +49,7 @@ playground/
49
49
  │ │ ├── Header.tsx # Header with title and theme toggle
50
50
  │ │ ├── ThemeToggle.tsx # Sun/moon theme switch component
51
51
  │ │ ├── JsonEditor.tsx # CodeMirror-based JSON editor
52
- │ │ ├── Preview.tsx # A2UIRender wrapper with error boundary
52
+ │ │ ├── Preview.tsx # A2UIRenderer wrapper with error boundary
53
53
  │ │ ├── ExampleSelector.tsx # Dropdown for example selection
54
54
  │ │ └── ErrorDisplay.tsx # Error state display component
55
55
  │ ├── data/
@@ -44,7 +44,7 @@ playground/src/
44
44
  │ ├── Header.tsx # App header with title and theme toggle
45
45
  │ ├── ThemeToggle.tsx # Sun/moon theme switch
46
46
  │ ├── JsonEditor.tsx # CodeMirror JSON editor
47
- │ ├── Preview.tsx # A2UIRender wrapper
47
+ │ ├── Preview.tsx # A2UIRenderer wrapper
48
48
  │ ├── ExampleSelector.tsx # Example dropdown
49
49
  │ └── ErrorDisplay.tsx # Error state component
50
50
  ├── data/
@@ -87,14 +87,14 @@ import { json } from '@codemirror/lang-json'
87
87
  />
88
88
  ```
89
89
 
90
- ### A2UIRender Integration
90
+ ### A2UIRenderer Integration
91
91
 
92
92
  ```typescript
93
- import { A2UIRender, type A2UIMessage } from '@easyops-cn/a2ui-react/0.8'
93
+ import { A2UIRenderer, type A2UIMessage } from '@easyops-cn/a2ui-react/0.8'
94
94
 
95
95
  // Parse JSON and render
96
96
  const messages: A2UIMessage[] = JSON.parse(jsonContent)
97
- <A2UIRender messages={messages} onAction={handleAction} />
97
+ <A2UIRenderer messages={messages} onAction={handleAction} />
98
98
  ```
99
99
 
100
100
  ### Error Boundary Pattern
@@ -69,10 +69,10 @@ Import from workspace dependency `@easyops-cn/a2ui-react/0.8`
69
69
  ### Integration Pattern
70
70
 
71
71
  ```typescript
72
- import { A2UIRender, type A2UIMessage } from '@easyops-cn/a2ui-react/0.8'
72
+ import { A2UIRenderer, type A2UIMessage } from '@easyops-cn/a2ui-react/0.8'
73
73
 
74
74
  // Parse JSON string to A2UIMessage[]
75
- // Pass to A2UIRender component
75
+ // Pass to A2UIRenderer component
76
76
  // Handle parse errors gracefully
77
77
  ```
78
78
 
@@ -111,7 +111,7 @@ Use React Error Boundary for preview panel with graceful fallback
111
111
  ### Implementation Pattern
112
112
 
113
113
  ```typescript
114
- // ErrorBoundary wraps A2UIRender
114
+ // ErrorBoundary wraps A2UIRenderer
115
115
  // Catches render errors, displays ErrorDisplay component
116
116
  // JSON parse errors handled separately before render
117
117
  ```
@@ -66,7 +66,7 @@ playground/
66
66
  ### Implementation for User Story 1
67
67
 
68
68
  - [x] T011 [P] [US1] Create JsonEditor component with CodeMirror integration in playground/src/components/JsonEditor.tsx
69
- - [x] T012 [P] [US1] Create Preview component wrapping A2UIRender with error boundary in playground/src/components/Preview.tsx
69
+ - [x] T012 [P] [US1] Create Preview component wrapping A2UIRenderer with error boundary in playground/src/components/Preview.tsx
70
70
  - [x] T013 [US1] Create split-panel layout in playground/src/App.tsx with editor (left) and preview (right)
71
71
  - [x] T014 [US1] Add layout styles for split-panel (50/50) in playground/src/App.css
72
72
  - [x] T015 [US1] Implement JSON parsing with error handling in App.tsx (parse on change, update preview or show error)
@@ -1,13 +1,13 @@
1
1
  /**
2
- * A2UIRender Tests
2
+ * A2UIRenderer Tests
3
3
  *
4
- * Tests for the main A2UIRender component.
4
+ * Tests for the main A2UIRenderer component.
5
5
  */
6
6
 
7
7
  import { describe, it, expect, vi } from 'vitest'
8
8
  import { render, screen } from '@testing-library/react'
9
9
  import userEvent from '@testing-library/user-event'
10
- import { A2UIRender, type ComponentsMap } from './A2UIRender'
10
+ import { A2UIRenderer, type ComponentsMap } from './A2UIRenderer'
11
11
  import type { A2UIMessage, BaseComponentProps, Action } from './types'
12
12
  import { useDispatchAction } from './hooks/useDispatchAction'
13
13
  import { useDataBinding, useFormBinding } from './hooks/useDataBinding'
@@ -79,20 +79,20 @@ function createTestMessages(options: {
79
79
  return messages
80
80
  }
81
81
 
82
- describe('A2UIRender', () => {
82
+ describe('A2UIRenderer', () => {
83
83
  describe('Phase 3: User Story 1 - Basic Message Rendering', () => {
84
84
  it('T014: renders nothing for empty messages array', () => {
85
- const { container } = render(<A2UIRender messages={[]} />)
85
+ const { container } = render(<A2UIRenderer messages={[]} />)
86
86
  expect(container.firstChild).toBeNull()
87
87
  })
88
88
 
89
89
  it('T014b: renders nothing for null/undefined messages', () => {
90
90
  // @ts-expect-error - testing null handling
91
- const { container: c1 } = render(<A2UIRender messages={null} />)
91
+ const { container: c1 } = render(<A2UIRenderer messages={null} />)
92
92
  expect(c1.firstChild).toBeNull()
93
93
 
94
94
  // @ts-expect-error - testing undefined handling
95
- const { container: c2 } = render(<A2UIRender messages={undefined} />)
95
+ const { container: c2 } = render(<A2UIRenderer messages={undefined} />)
96
96
  expect(c2.firstChild).toBeNull()
97
97
  })
98
98
 
@@ -109,7 +109,7 @@ describe('A2UIRender', () => {
109
109
  ],
110
110
  })
111
111
 
112
- render(<A2UIRender messages={messages} />)
112
+ render(<A2UIRenderer messages={messages} />)
113
113
 
114
114
  expect(screen.getByText('Hello World')).toBeInTheDocument()
115
115
  })
@@ -137,7 +137,7 @@ describe('A2UIRender', () => {
137
137
  ],
138
138
  })
139
139
 
140
- render(<A2UIRender messages={messages} />)
140
+ render(<A2UIRenderer messages={messages} />)
141
141
 
142
142
  expect(screen.getByText('First')).toBeInTheDocument()
143
143
  expect(screen.getByText('Second')).toBeInTheDocument()
@@ -173,7 +173,7 @@ describe('A2UIRender', () => {
173
173
  },
174
174
  ]
175
175
 
176
- render(<A2UIRender messages={messages} />)
176
+ render(<A2UIRenderer messages={messages} />)
177
177
 
178
178
  expect(screen.getByText('Surface 1')).toBeInTheDocument()
179
179
  expect(screen.getByText('Surface 2')).toBeInTheDocument()
@@ -205,7 +205,7 @@ describe('A2UIRender', () => {
205
205
  ],
206
206
  })
207
207
 
208
- render(<A2UIRender messages={messages} onAction={onAction} />)
208
+ render(<A2UIRenderer messages={messages} onAction={onAction} />)
209
209
 
210
210
  const button = screen.getByRole('button')
211
211
  await user.click(button)
@@ -242,7 +242,7 @@ describe('A2UIRender', () => {
242
242
  ],
243
243
  })
244
244
 
245
- render(<A2UIRender messages={messages} onAction={onAction} />)
245
+ render(<A2UIRenderer messages={messages} onAction={onAction} />)
246
246
 
247
247
  const button = screen.getByRole('button')
248
248
  await user.click(button)
@@ -299,7 +299,7 @@ describe('A2UIRender', () => {
299
299
  ],
300
300
  })
301
301
 
302
- render(<A2UIRender messages={messages} onAction={onAction} />)
302
+ render(<A2UIRenderer messages={messages} onAction={onAction} />)
303
303
 
304
304
  const buttons = screen.getAllByRole('button')
305
305
  await user.click(buttons[0])
@@ -347,7 +347,7 @@ describe('A2UIRender', () => {
347
347
  })
348
348
 
349
349
  // No onAction prop
350
- render(<A2UIRender messages={messages} />)
350
+ render(<A2UIRenderer messages={messages} />)
351
351
 
352
352
  const button = screen.getByRole('button')
353
353
 
@@ -395,7 +395,7 @@ describe('A2UIRender', () => {
395
395
  ],
396
396
  })
397
397
 
398
- render(<A2UIRender messages={messages} components={customComponents} />)
398
+ render(<A2UIRenderer messages={messages} components={customComponents} />)
399
399
 
400
400
  expect(screen.getByTestId('custom-button')).toBeInTheDocument()
401
401
  expect(screen.getByText('Hello')).toBeInTheDocument()
@@ -437,7 +437,7 @@ describe('A2UIRender', () => {
437
437
  ],
438
438
  })
439
439
 
440
- render(<A2UIRender messages={messages} components={customComponents} />)
440
+ render(<A2UIRenderer messages={messages} components={customComponents} />)
441
441
 
442
442
  expect(screen.getByTestId('custom-button')).toBeInTheDocument()
443
443
  expect(screen.getByTestId('custom-text')).toBeInTheDocument()
@@ -471,7 +471,7 @@ describe('A2UIRender', () => {
471
471
  ],
472
472
  })
473
473
 
474
- render(<A2UIRender messages={messages} components={customComponents} />)
474
+ render(<A2UIRenderer messages={messages} components={customComponents} />)
475
475
 
476
476
  // Custom button
477
477
  expect(screen.getByTestId('custom-button')).toBeInTheDocument()
@@ -509,7 +509,7 @@ describe('A2UIRender', () => {
509
509
  ],
510
510
  })
511
511
 
512
- render(<A2UIRender messages={messages} components={customComponents} />)
512
+ render(<A2UIRenderer messages={messages} components={customComponents} />)
513
513
 
514
514
  expect(screen.getByTestId('custom-switch')).toBeInTheDocument()
515
515
  expect(screen.getByText('Enable feature')).toBeInTheDocument()
@@ -557,7 +557,7 @@ describe('A2UIRender', () => {
557
557
  })
558
558
 
559
559
  render(
560
- <A2UIRender
560
+ <A2UIRenderer
561
561
  messages={messages}
562
562
  onAction={onAction}
563
563
  components={customComponents}
@@ -615,7 +615,7 @@ describe('A2UIRender', () => {
615
615
  },
616
616
  ]
617
617
 
618
- render(<A2UIRender messages={messages} components={customComponents} />)
618
+ render(<A2UIRenderer messages={messages} components={customComponents} />)
619
619
 
620
620
  expect(screen.getByTestId('custom-display')).toHaveTextContent(
621
621
  'Hello from data model'
@@ -656,7 +656,7 @@ describe('A2UIRender', () => {
656
656
  },
657
657
  ]
658
658
 
659
- render(<A2UIRender messages={messages} components={customComponents} />)
659
+ render(<A2UIRenderer messages={messages} components={customComponents} />)
660
660
 
661
661
  expect(screen.getByTestId('custom-display')).toHaveTextContent(
662
662
  'fallback value'
@@ -700,7 +700,7 @@ describe('A2UIRender', () => {
700
700
  },
701
701
  ]
702
702
 
703
- render(<A2UIRender messages={messages} components={customComponents} />)
703
+ render(<A2UIRenderer messages={messages} components={customComponents} />)
704
704
 
705
705
  expect(screen.getByTestId('custom-input')).toHaveValue('john_doe')
706
706
  })
@@ -745,7 +745,7 @@ describe('A2UIRender', () => {
745
745
  },
746
746
  ]
747
747
 
748
- render(<A2UIRender messages={messages} components={customComponents} />)
748
+ render(<A2UIRenderer messages={messages} components={customComponents} />)
749
749
 
750
750
  const input = screen.getByTestId('custom-input')
751
751
  await user.type(input, 'new_value')
@@ -785,7 +785,7 @@ describe('A2UIRender', () => {
785
785
  },
786
786
  ]
787
787
 
788
- render(<A2UIRender messages={messages} components={customComponents} />)
788
+ render(<A2UIRenderer messages={messages} components={customComponents} />)
789
789
 
790
790
  expect(screen.getByTestId('custom-input')).toHaveValue('default_value')
791
791
  })
@@ -1,19 +1,19 @@
1
1
  /**
2
- * A2UIRender - Main entry component for rendering A2UI messages.
2
+ * A2UIRenderer - Main entry component for rendering A2UI messages.
3
3
  *
4
4
  * This component processes A2UI messages and renders the resulting UI.
5
5
  * It supports custom component overrides via the components prop.
6
6
  *
7
7
  * @example
8
8
  * ```tsx
9
- * import { A2UIRender, A2UIMessage, A2UIAction } from '@easyops-cn/a2ui-react/0.8'
9
+ * import { A2UIRenderer, A2UIMessage, A2UIAction } from '@easyops-cn/a2ui-react/0.8'
10
10
  *
11
11
  * function App() {
12
12
  * const messages: A2UIMessage[] = [...]
13
13
  * const handleAction = (action: A2UIAction) => {
14
14
  * console.log('Action:', action)
15
15
  * }
16
- * return <A2UIRender messages={messages} onAction={handleAction} />
16
+ * return <A2UIRenderer messages={messages} onAction={handleAction} />
17
17
  * }
18
18
  * ```
19
19
  */
@@ -38,7 +38,7 @@ export type ComponentsMap = Map<
38
38
  >
39
39
 
40
40
  /**
41
- * Props for A2UIRender component.
41
+ * Props for A2UIRenderer component.
42
42
  */
43
43
  export interface A2UIRenderProps {
44
44
  /** Array of A2UI messages to render */
@@ -105,21 +105,21 @@ function A2UIRenderInner({ messages }: { messages: A2UIMessage[] }) {
105
105
  * @example
106
106
  * ```tsx
107
107
  * // Basic usage
108
- * <A2UIRender messages={messages} onAction={handleAction} />
108
+ * <A2UIRenderer messages={messages} onAction={handleAction} />
109
109
  *
110
110
  * // With custom components
111
111
  * const customComponents = new Map([
112
112
  * ['Button', CustomButton],
113
113
  * ['Switch', CustomSwitch],
114
114
  * ])
115
- * <A2UIRender
115
+ * <A2UIRenderer
116
116
  * messages={messages}
117
117
  * onAction={handleAction}
118
118
  * components={customComponents}
119
119
  * />
120
120
  * ```
121
121
  */
122
- export function A2UIRender({
122
+ export function A2UIRenderer({
123
123
  messages,
124
124
  onAction,
125
125
  components,
@@ -139,4 +139,4 @@ export function A2UIRender({
139
139
  )
140
140
  }
141
141
 
142
- A2UIRender.displayName = 'A2UI.Render'
142
+ A2UIRenderer.displayName = 'A2UI.Render'
package/src/0.8/index.ts CHANGED
@@ -6,14 +6,14 @@
6
6
  *
7
7
  * @example
8
8
  * ```tsx
9
- * import { A2UIRender, A2UIMessage, A2UIAction } from '@easyops-cn/a2ui-react/0.8'
9
+ * import { A2UIRenderer, A2UIMessage, A2UIAction } from '@easyops-cn/a2ui-react/0.8'
10
10
  *
11
11
  * function App() {
12
12
  * const messages: A2UIMessage[] = [...]
13
13
  * const handleAction = (action: A2UIAction) => {
14
14
  * console.log('Action:', action)
15
15
  * }
16
- * return <A2UIRender messages={messages} onAction={handleAction} />
16
+ * return <A2UIRenderer messages={messages} onAction={handleAction} />
17
17
  * }
18
18
  * ```
19
19
  */
@@ -29,7 +29,7 @@ export type {
29
29
 
30
30
  // ============ Components ============
31
31
 
32
- export { A2UIRender } from './A2UIRender'
32
+ export { A2UIRenderer } from './A2UIRenderer'
33
33
  export { ComponentRenderer } from './components/ComponentRenderer'
34
34
 
35
35
  // ============ Hooks ============
@@ -0,0 +1,108 @@
1
+ # API Reference
2
+
3
+ ## Core
4
+
5
+ ```typescript
6
+ /**
7
+ * Main entry component for rendering A2UI messages.
8
+ * @param messages - Array of A2UI messages to render
9
+ * @param onAction - Callback when an action is dispatched
10
+ * @param components - Custom component overrides
11
+ */
12
+ function A2UIRenderer(props: {
13
+ messages: A2UIMessage[]
14
+ onAction?: (action: A2UIAction) => void
15
+ components?: Map<string, React.ComponentType<any>>
16
+ }): React.ReactElement
17
+
18
+ /**
19
+ * A2UI message from server to client.
20
+ * Only one of the fields should be set per message.
21
+ */
22
+ interface A2UIMessage {
23
+ beginRendering?: BeginRenderingPayload
24
+ surfaceUpdate?: SurfaceUpdatePayload
25
+ dataModelUpdate?: DataModelUpdatePayload
26
+ deleteSurface?: DeleteSurfacePayload
27
+ }
28
+
29
+ /**
30
+ * Resolved action payload sent to the action handler.
31
+ */
32
+ interface A2UIAction {
33
+ surfaceId: string
34
+ name: string
35
+ context: Record<string, unknown>
36
+ sourceComponentId: string
37
+ }
38
+ ```
39
+
40
+ ## Hooks
41
+
42
+ ```typescript
43
+ /**
44
+ * Returns a function to dispatch actions from custom components.
45
+ */
46
+ function useDispatchAction(): (
47
+ surfaceId: string,
48
+ componentId: string,
49
+ action: Action
50
+ ) => void
51
+
52
+ /**
53
+ * Resolves a ValueSource to its actual value.
54
+ * @param surfaceId - The surface ID for data model lookup
55
+ * @param source - The value source (literal or path reference)
56
+ * @param defaultValue - Default value if source is undefined or path not found
57
+ */
58
+ function useDataBinding<T = unknown>(
59
+ surfaceId: string,
60
+ source: ValueSource | undefined,
61
+ defaultValue?: T
62
+ ): T
63
+
64
+ /**
65
+ * Hook for two-way data binding in form components.
66
+ * @param surfaceId - The surface ID
67
+ * @param source - The value source (must be a path reference for setting)
68
+ * @param defaultValue - Default value if not found
69
+ * @returns Tuple of [value, setValue]
70
+ */
71
+ function useFormBinding<T = unknown>(
72
+ surfaceId: string,
73
+ source: ValueSource | undefined,
74
+ defaultValue?: T
75
+ ): [T, (value: T) => void]
76
+ ```
77
+
78
+ ## Others
79
+
80
+ ```typescript
81
+ /**
82
+ * Renders a component by ID from the component registry.
83
+ * @param surfaceId - The surface ID
84
+ * @param componentId - The component ID to render
85
+ */
86
+ function ComponentRenderer(props: {
87
+ surfaceId: string
88
+ componentId: string
89
+ }): React.ReactElement
90
+
91
+ /**
92
+ * Represents a value source - either a literal value or a reference to a data model path.
93
+ */
94
+ type ValueSource =
95
+ | { literalString: string }
96
+ | { literalNumber: number }
97
+ | { literalBoolean: boolean }
98
+ | { literalArray: string[] }
99
+ | { path: string }
100
+
101
+ /**
102
+ * Action definition (attached to Button components).
103
+ */
104
+ interface Action {
105
+ name: string
106
+ context?: ActionContextItem[]
107
+ }
108
+ ```
@@ -18,11 +18,11 @@ First, use the `@source` directive to tell Tailwind to scan the library code for
18
18
  @source "../node_modules/@easyops-cn/a2ui-react";
19
19
  ```
20
20
 
21
- Next, use the `A2UIRender` component to render A2UI messages:
21
+ Next, use the `A2UIRenderer` component to render A2UI messages:
22
22
 
23
23
  ```tsx
24
24
  import {
25
- A2UIRender,
25
+ A2UIRenderer,
26
26
  type A2UIMessage,
27
27
  type A2UIAction,
28
28
  } from '@easyops-cn/a2ui-react/0.8'
@@ -34,7 +34,7 @@ function App() {
34
34
  console.log('Action received:', action)
35
35
  }
36
36
 
37
- return <A2UIRender messages={messages} onAction={handleAction} />
37
+ return <A2UIRenderer messages={messages} onAction={handleAction} />
38
38
  }
39
39
  ```
40
40
 
@@ -43,7 +43,11 @@ function App() {
43
43
  You can override default components or add new custom components via the `components` prop, which takes a `Map<string, React.ComponentType>`.
44
44
 
45
45
  ```tsx
46
- import { A2UIRender, A2UIMessage, A2UIAction } from '@easyops-cn/a2ui-react/0.8'
46
+ import {
47
+ A2UIRenderer,
48
+ A2UIMessage,
49
+ A2UIAction,
50
+ } from '@easyops-cn/a2ui-react/0.8'
47
51
 
48
52
  const ComponentsMap = new Map<string, React.ComponentType<any>>([
49
53
  // Override default Button component with a custom one
@@ -55,7 +59,7 @@ const ComponentsMap = new Map<string, React.ComponentType<any>>([
55
59
 
56
60
  function App() {
57
61
  return (
58
- <A2UIRender
62
+ <A2UIRenderer
59
63
  components={ComponentsMap}
60
64
  messages={messages}
61
65
  onAction={handleAction}
@@ -1,6 +1,8 @@
1
1
  {
2
2
  "rewrites": [
3
3
  { "source": "a2ui-react/assets/:path*", "destination": "/assets/:path*" },
4
+ { "source": "a2ui-react/api/:path*", "destination": "/api/:path*" },
5
+ { "source": "a2ui-react/api/**", "destination": "/api/index.html" },
4
6
  { "source": "a2ui-react/**", "destination": "/index.html" }
5
7
  ]
6
8
  }
@@ -1,7 +1,7 @@
1
1
  import { useSiteContext } from 'plain-blog/SiteContext'
2
2
 
3
- export default function Header() {
4
- const { baseUrl, site } = useSiteContext()
3
+ export default function Header(props) {
4
+ const { baseUrl, site, url } = useSiteContext()
5
5
 
6
6
  return (
7
7
  <header>
@@ -10,9 +10,15 @@ export default function Header() {
10
10
  </h1>
11
11
  <div className="header-right">
12
12
  <nav className="header-nav">
13
- <a href={baseUrl} className="active">
13
+ <a href={baseUrl} className={url === baseUrl ? 'active' : ''}>
14
14
  Docs
15
15
  </a>
16
+ <a
17
+ href={`${baseUrl}api/`}
18
+ className={url === `${baseUrl}api/` ? 'active' : ''}
19
+ >
20
+ API Reference
21
+ </a>
16
22
  <a href={`${baseUrl}playground/`}>Playground</a>
17
23
  <a
18
24
  href="https://github.com/easyops-cn/a2ui-react"