@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,55 @@
1
+ import { useState, useEffect, useCallback } from 'react'
2
+
3
+ type Theme = 'light' | 'dark'
4
+
5
+ function getInitialTheme(): Theme {
6
+ // Check localStorage first
7
+ const stored = localStorage.getItem('theme')
8
+ if (stored === 'light' || stored === 'dark') {
9
+ return stored
10
+ }
11
+ // Fall back to system preference
12
+ if (typeof window !== 'undefined' && window.matchMedia) {
13
+ return window.matchMedia('(prefers-color-scheme: dark)').matches
14
+ ? 'dark'
15
+ : 'light'
16
+ }
17
+ return 'light'
18
+ }
19
+
20
+ export function useTheme() {
21
+ const [theme, setThemeState] = useState<Theme>(getInitialTheme)
22
+
23
+ const setTheme = useCallback((newTheme: Theme) => {
24
+ setThemeState(newTheme)
25
+ document.documentElement.dataset.theme = newTheme
26
+ document.documentElement.classList.remove('light', 'dark')
27
+ document.documentElement.classList.add(newTheme)
28
+ localStorage.setItem('theme', newTheme)
29
+ }, [])
30
+
31
+ const toggleTheme = useCallback(() => {
32
+ setTheme(theme === 'dark' ? 'light' : 'dark')
33
+ }, [theme, setTheme])
34
+
35
+ // Initialize theme on mount
36
+ useEffect(() => {
37
+ document.documentElement.dataset.theme = theme
38
+ document.documentElement.classList.add(theme)
39
+ }, [])
40
+
41
+ // Listen for system theme changes
42
+ useEffect(() => {
43
+ const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
44
+ const handleChange = (e: MediaQueryListEvent) => {
45
+ // Only update if no explicit preference is stored
46
+ if (!localStorage.getItem('theme')) {
47
+ setTheme(e.matches ? 'dark' : 'light')
48
+ }
49
+ }
50
+ mediaQuery.addEventListener('change', handleChange)
51
+ return () => mediaQuery.removeEventListener('change', handleChange)
52
+ }, [setTheme])
53
+
54
+ return { theme, toggleTheme, setTheme }
55
+ }
@@ -0,0 +1,220 @@
1
+ @import 'tailwindcss';
2
+ @import 'tw-animate-css';
3
+ /* @source "../../src*.{ts,tsx}"; */
4
+ @source "../../node_modules/@easyops-cn/a2ui-react";
5
+
6
+ @custom-variant dark (&:is(.dark *));
7
+
8
+ :root {
9
+ --background: hsl(10, 10%, 98%);
10
+ --foreground: hsl(10, 20%, 20%);
11
+ --card: oklch(1 0 0);
12
+ --card-foreground: oklch(0.145 0 0);
13
+ --popover: oklch(1 0 0);
14
+ --popover-foreground: oklch(0.145 0 0);
15
+ --primary: #009488;
16
+ --primary-foreground: oklch(0.985 0 0);
17
+ --secondary: oklch(0.97 0 0);
18
+ --secondary-foreground: oklch(0.205 0 0);
19
+ --muted: oklch(0.97 0 0);
20
+ --muted-foreground: oklch(0.556 0 0);
21
+ --accent: oklch(0.97 0 0);
22
+ --accent-foreground: oklch(0.205 0 0);
23
+ --destructive: oklch(0.577 0.245 27.325);
24
+ --destructive-foreground: oklch(0.577 0.245 27.325);
25
+ --border: oklch(0.922 0 0);
26
+ --input: oklch(0.922 0 0);
27
+ --ring: oklch(0.708 0 0);
28
+ --chart-1: oklch(0.646 0.222 41.116);
29
+ --chart-2: oklch(0.6 0.118 184.704);
30
+ --chart-3: oklch(0.398 0.07 227.392);
31
+ --chart-4: oklch(0.828 0.189 84.429);
32
+ --chart-5: oklch(0.769 0.188 70.08);
33
+ --radius: 0.625rem;
34
+ --sidebar: oklch(0.985 0 0);
35
+ --sidebar-foreground: oklch(0.145 0 0);
36
+ --sidebar-primary: oklch(0.205 0 0);
37
+ --sidebar-primary-foreground: oklch(0.985 0 0);
38
+ --sidebar-accent: oklch(0.97 0 0);
39
+ --sidebar-accent-foreground: oklch(0.205 0 0);
40
+ --sidebar-border: oklch(0.922 0 0);
41
+ --sidebar-ring: oklch(0.708 0 0);
42
+ }
43
+
44
+ .dark {
45
+ --background: hsl(230, 25%, 18%);
46
+ --foreground: hsl(210, 50%, 96%);
47
+ --card: oklch(0.145 0 0);
48
+ --card-foreground: oklch(0.985 0 0);
49
+ --popover: oklch(0.145 0 0);
50
+ --popover-foreground: oklch(0.985 0 0);
51
+ --primary: #80cbc4;
52
+ --primary-foreground: oklch(0.205 0 0);
53
+ --secondary: oklch(0.269 0 0);
54
+ --secondary-foreground: oklch(0.985 0 0);
55
+ --muted: oklch(0.269 0 0);
56
+ --muted-foreground: oklch(0.708 0 0);
57
+ --accent: oklch(0.269 0 0);
58
+ --accent-foreground: oklch(0.985 0 0);
59
+ --destructive: oklch(0.396 0.141 25.723);
60
+ --destructive-foreground: oklch(0.637 0.237 25.331);
61
+ --border: #ffffff1a;
62
+ --input: #ffffff1a;
63
+ --ring: oklch(0.439 0 0);
64
+ --chart-1: oklch(0.488 0.243 264.376);
65
+ --chart-2: oklch(0.696 0.17 162.48);
66
+ --chart-3: oklch(0.769 0.188 70.08);
67
+ --chart-4: oklch(0.627 0.265 303.9);
68
+ --chart-5: oklch(0.645 0.246 16.439);
69
+ --sidebar: oklch(0.205 0 0);
70
+ --sidebar-foreground: oklch(0.985 0 0);
71
+ --sidebar-primary: oklch(0.488 0.243 264.376);
72
+ --sidebar-primary-foreground: oklch(0.985 0 0);
73
+ --sidebar-accent: oklch(0.269 0 0);
74
+ --sidebar-accent-foreground: oklch(0.985 0 0);
75
+ --sidebar-border: oklch(0.269 0 0);
76
+ --sidebar-ring: oklch(0.439 0 0);
77
+ }
78
+
79
+ @theme inline {
80
+ --color-background: var(--background);
81
+ --color-foreground: var(--foreground);
82
+ --color-card: var(--card);
83
+ --color-card-foreground: var(--card-foreground);
84
+ --color-popover: var(--popover);
85
+ --color-popover-foreground: var(--popover-foreground);
86
+ --color-primary: var(--primary);
87
+ --color-primary-foreground: var(--primary-foreground);
88
+ --color-secondary: var(--secondary);
89
+ --color-secondary-foreground: var(--secondary-foreground);
90
+ --color-muted: var(--muted);
91
+ --color-muted-foreground: var(--muted-foreground);
92
+ --color-accent: var(--accent);
93
+ --color-accent-foreground: var(--accent-foreground);
94
+ --color-destructive: var(--destructive);
95
+ --color-destructive-foreground: var(--destructive-foreground);
96
+ --color-border: var(--border);
97
+ --color-input: var(--input);
98
+ --color-ring: var(--ring);
99
+ --color-chart-1: var(--chart-1);
100
+ --color-chart-2: var(--chart-2);
101
+ --color-chart-3: var(--chart-3);
102
+ --color-chart-4: var(--chart-4);
103
+ --color-chart-5: var(--chart-5);
104
+ --radius-sm: calc(var(--radius) - 4px);
105
+ --radius-md: calc(var(--radius) - 2px);
106
+ --radius-lg: var(--radius);
107
+ --radius-xl: calc(var(--radius) + 4px);
108
+ --color-sidebar: var(--sidebar);
109
+ --color-sidebar-foreground: var(--sidebar-foreground);
110
+ --color-sidebar-primary: var(--sidebar-primary);
111
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
112
+ --color-sidebar-accent: var(--sidebar-accent);
113
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
114
+ --color-sidebar-border: var(--sidebar-border);
115
+ --color-sidebar-ring: var(--sidebar-ring);
116
+ }
117
+
118
+ @layer base {
119
+ * {
120
+ @apply border-border outline-ring/50;
121
+ }
122
+ body {
123
+ @apply bg-background text-foreground;
124
+ }
125
+ }
126
+
127
+ html {
128
+ --primary-color: var(--primary);
129
+ --text-color: var(--foreground);
130
+ --muted-color: var(--muted);
131
+ --background-color: var(--background);
132
+ --border-color: var(--border);
133
+ --editor-bg: hsl(0, 0%, 100%);
134
+ --preview-bg: hsl(0, 0%, 98%);
135
+
136
+ color-scheme: light;
137
+ }
138
+
139
+ @media (prefers-color-scheme: dark) {
140
+ html:not([data-theme='light']) {
141
+ --editor-bg: oklch(0.145 0 0);
142
+ --preview-bg: oklch(0.145 0 0);
143
+
144
+ color-scheme: dark;
145
+ -webkit-font-smoothing: antialiased;
146
+ -moz-osx-font-smoothing: grayscale;
147
+ }
148
+ }
149
+
150
+ html[data-theme='dark'] {
151
+ --editor-bg: oklch(0.145 0 0);
152
+ --preview-bg: oklch(0.145 0 0);
153
+
154
+ color-scheme: dark;
155
+ -webkit-font-smoothing: antialiased;
156
+ -moz-osx-font-smoothing: grayscale;
157
+ }
158
+
159
+ * {
160
+ box-sizing: border-box;
161
+ }
162
+
163
+ body {
164
+ font-family:
165
+ system-ui,
166
+ -apple-system,
167
+ Segoe UI,
168
+ Roboto,
169
+ Ubuntu,
170
+ Cantarell,
171
+ Noto Sans,
172
+ sans-serif;
173
+ line-height: 1.5;
174
+ font-weight: 400;
175
+ font-size: 16px;
176
+ margin: 0;
177
+ }
178
+
179
+ #root {
180
+ min-height: 100vh;
181
+ display: flex;
182
+ flex-direction: column;
183
+ }
184
+
185
+ /* Error Display */
186
+ .error-display {
187
+ padding: 16px;
188
+ background-color: hsl(0, 70%, 95%);
189
+ border: 1px solid hsl(0, 70%, 80%);
190
+ border-radius: 8px;
191
+ color: hsl(0, 70%, 30%);
192
+ }
193
+
194
+ html[data-theme='dark'] .error-display,
195
+ html:not([data-theme='light']) .error-display {
196
+ background-color: hsl(0, 50%, 20%);
197
+ border-color: hsl(0, 50%, 35%);
198
+ color: hsl(0, 70%, 80%);
199
+ }
200
+
201
+ @media (prefers-color-scheme: dark) {
202
+ html:not([data-theme='light']) .error-display {
203
+ background-color: hsl(0, 50%, 20%);
204
+ border-color: hsl(0, 50%, 35%);
205
+ color: hsl(0, 70%, 80%);
206
+ }
207
+ }
208
+
209
+ .error-title {
210
+ font-weight: 600;
211
+ margin-bottom: 8px;
212
+ }
213
+
214
+ .error-message {
215
+ font-family: monospace;
216
+ font-size: 14px;
217
+ white-space: pre-wrap;
218
+ word-break: break-word;
219
+ margin: 0;
220
+ }
@@ -0,0 +1,10 @@
1
+ import { StrictMode } from 'react'
2
+ import { createRoot } from 'react-dom/client'
3
+ import './index.css'
4
+ import App from './App.tsx'
5
+
6
+ createRoot(document.getElementById('root')!).render(
7
+ <StrictMode>
8
+ <App />
9
+ </StrictMode>
10
+ )
@@ -0,0 +1,34 @@
1
+ {
2
+ "compilerOptions": {
3
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
4
+ "target": "ES2022",
5
+ "useDefineForClassFields": true,
6
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
7
+ "module": "ESNext",
8
+ "types": ["vite/client"],
9
+ "skipLibCheck": true,
10
+
11
+ /* Bundler mode */
12
+ "moduleResolution": "bundler",
13
+ "allowImportingTsExtensions": true,
14
+ "verbatimModuleSyntax": true,
15
+ "moduleDetection": "force",
16
+ "noEmit": true,
17
+ "jsx": "react-jsx",
18
+
19
+ /* Linting */
20
+ "strict": true,
21
+ "noUnusedLocals": true,
22
+ "noUnusedParameters": true,
23
+ "erasableSyntaxOnly": true,
24
+ "noFallthroughCasesInSwitch": true,
25
+ "noUncheckedSideEffectImports": true,
26
+
27
+ "baseUrl": ".",
28
+ "paths": {
29
+ "@/*": ["../src/*"],
30
+ "@easyops-cn/a2ui-react/0.8": ["../src/0.8/index.ts"]
31
+ }
32
+ },
33
+ "include": ["src"]
34
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "files": [],
3
+ "references": [
4
+ { "path": "./tsconfig.app.json" },
5
+ { "path": "./tsconfig.node.json" }
6
+ ],
7
+ "compilerOptions": {
8
+ "baseUrl": ".",
9
+ "paths": {
10
+ "@/*": ["./src/*"]
11
+ }
12
+ }
13
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "compilerOptions": {
3
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
4
+ "target": "ES2023",
5
+ "lib": ["ES2023"],
6
+ "module": "ESNext",
7
+ "types": ["node"],
8
+ "skipLibCheck": true,
9
+
10
+ /* Bundler mode */
11
+ "moduleResolution": "bundler",
12
+ "allowImportingTsExtensions": true,
13
+ "verbatimModuleSyntax": true,
14
+ "moduleDetection": "force",
15
+ "noEmit": true,
16
+
17
+ /* Linting */
18
+ "strict": true,
19
+ "noUnusedLocals": true,
20
+ "noUnusedParameters": true,
21
+ "erasableSyntaxOnly": true,
22
+ "noFallthroughCasesInSwitch": true,
23
+ "noUncheckedSideEffectImports": true
24
+ },
25
+ "include": ["vite.config.ts"]
26
+ }
@@ -0,0 +1,31 @@
1
+ import path from 'path'
2
+ import { defineConfig } from 'vite'
3
+ import react from '@vitejs/plugin-react'
4
+ import tailwindcss from '@tailwindcss/vite'
5
+
6
+ // https://vite.dev/config/
7
+ export default defineConfig({
8
+ base: '/a2ui-react/playground/',
9
+ plugins: [
10
+ react({
11
+ babel: {
12
+ plugins: [['babel-plugin-react-compiler']],
13
+ },
14
+ }),
15
+ tailwindcss(),
16
+ ],
17
+ server: {
18
+ proxy: {
19
+ '^/a2ui-react/(?!playground/)': {
20
+ target: 'http://localhost:3000',
21
+ changeOrigin: true,
22
+ },
23
+ },
24
+ },
25
+ resolve: {
26
+ alias: {
27
+ '@': path.resolve(__dirname, '../src'),
28
+ '@easyops-cn/a2ui-react/0.8': path.resolve(__dirname, '../src/0.8'),
29
+ },
30
+ },
31
+ })
@@ -0,0 +1,41 @@
1
+ # Specification Quality Checklist: A2UIRenderer Component Library
2
+
3
+ **Purpose**: Validate specification completeness and quality before proceeding to planning
4
+ **Created**: 2026-01-10
5
+ **Feature**: [spec.md](./spec.md)
6
+
7
+ ## Content Quality
8
+
9
+ - [x] No implementation details (languages, frameworks, APIs)
10
+ - [x] Focused on user value and business needs
11
+ - [x] Written for non-technical stakeholders
12
+ - [x] All mandatory sections completed
13
+
14
+ ## Requirement Completeness
15
+
16
+ - [x] No [NEEDS CLARIFICATION] markers remain
17
+ - [x] Requirements are testable and unambiguous
18
+ - [x] Success criteria are measurable
19
+ - [x] Success criteria are technology-agnostic (no implementation details)
20
+ - [x] All acceptance scenarios are defined
21
+ - [x] Edge cases are identified
22
+ - [x] Scope is clearly bounded
23
+ - [x] Dependencies and assumptions identified
24
+
25
+ ## Feature Readiness
26
+
27
+ - [x] All functional requirements have clear acceptance criteria
28
+ - [x] User scenarios cover primary flows
29
+ - [x] Feature meets measurable outcomes defined in Success Criteria
30
+ - [x] No implementation details leak into specification
31
+
32
+ ## Notes
33
+
34
+ - All items pass validation
35
+ - Specification is ready for `/speckit.clarify` or `/speckit.plan`
36
+ - The spec covers all usage patterns shown in README.md:
37
+ - Basic rendering with A2UIRender component
38
+ - Action handling via onAction callback
39
+ - Custom component overrides via ComponentsMap
40
+ - Custom component creation with hooks (useDispatchAction, useDataBinding, useFormBinding)
41
+ - Child component rendering via ComponentRenderer
@@ -0,0 +1,140 @@
1
+ # Data Model: A2UIRenderer Component Library
2
+
3
+ **Date**: 2026-01-10
4
+ **Feature**: 001-a2ui-renderer
5
+
6
+ ## Overview
7
+
8
+ This document describes the data model for the A2UI React renderer. The types are already defined in `src/0.8/types/index.ts`.
9
+
10
+ ## Core Entities
11
+
12
+ ### A2UIMessage
13
+
14
+ The primary input to the renderer. Messages are processed to build surfaces and data models.
15
+
16
+ ```
17
+ A2UIMessage
18
+ ├── beginRendering?: BeginRenderingPayload
19
+ │ ├── surfaceId: string (required)
20
+ │ ├── root: string (required) - root component ID
21
+ │ ├── catalogId?: string
22
+ │ └── styles?: SurfaceStyles
23
+ ├── surfaceUpdate?: SurfaceUpdatePayload
24
+ │ ├── surfaceId: string (required)
25
+ │ └── components: ComponentDefinition[] (required)
26
+ ├── dataModelUpdate?: DataModelUpdatePayload
27
+ │ ├── surfaceId: string (required)
28
+ │ ├── path?: string (default: "/")
29
+ │ └── contents: DataEntry[] (required)
30
+ └── deleteSurface?: DeleteSurfacePayload
31
+ └── surfaceId: string (required)
32
+ ```
33
+
34
+ ### Surface
35
+
36
+ Runtime representation of a renderable UI surface.
37
+
38
+ ```
39
+ Surface
40
+ ├── surfaceId: string (required) - unique identifier
41
+ ├── root: string (required) - root component ID
42
+ ├── components: Map<string, ComponentDefinition> (required)
43
+ └── styles?: SurfaceStyles
44
+ ├── font?: string
45
+ └── primaryColor?: string
46
+ ```
47
+
48
+ ### ComponentDefinition
49
+
50
+ Definition of a single UI component.
51
+
52
+ ```
53
+ ComponentDefinition
54
+ ├── id: string (required) - unique within surface
55
+ ├── weight?: number - for ordering siblings
56
+ └── component: { [type: string]: ComponentProps }
57
+ └── [type]: Record<string, unknown>
58
+ ```
59
+
60
+ ### DataModel
61
+
62
+ Hierarchical key-value store for dynamic data.
63
+
64
+ ```
65
+ DataModel
66
+ └── [key: string]: DataModelValue
67
+ where DataModelValue = string | number | boolean | DataModel | unknown[]
68
+ ```
69
+
70
+ ### Action / ActionPayload
71
+
72
+ User interaction events.
73
+
74
+ ```
75
+ Action (input from component definition)
76
+ ├── name: string (required)
77
+ └── context?: ActionContextItem[]
78
+ ├── key: string
79
+ └── value: ValueSource
80
+
81
+ ActionPayload (output to onAction callback)
82
+ ├── surfaceId: string (required)
83
+ ├── name: string (required)
84
+ ├── context: Record<string, unknown> (resolved values)
85
+ └── sourceComponentId: string (required)
86
+ ```
87
+
88
+ ### ValueSource
89
+
90
+ Reference to data - either literal or path-based.
91
+
92
+ ```
93
+ ValueSource (discriminated union)
94
+ ├── { literalString: string }
95
+ ├── { literalNumber: number }
96
+ ├── { literalBoolean: boolean }
97
+ ├── { literalArray: string[] }
98
+ └── { path: string } - reference to DataModel path
99
+ ```
100
+
101
+ ## Entity Relationships
102
+
103
+ ```
104
+ ┌─────────────────┐
105
+ │ A2UIMessage │
106
+ └────────┬────────┘
107
+ │ processes
108
+
109
+ ┌─────────────────┐ contains ┌─────────────────────┐
110
+ │ Surface │◄─────────────────►│ ComponentDefinition │
111
+ └────────┬────────┘ └──────────┬──────────┘
112
+ │ │
113
+ │ references │ may contain
114
+ ▼ ▼
115
+ ┌─────────────────┐ ┌─────────────────────┐
116
+ │ DataModel │◄──────────────────│ Action │
117
+ └─────────────────┘ resolves └──────────┬──────────┘
118
+ context │
119
+ │ dispatches
120
+
121
+ ┌─────────────────────┐
122
+ │ ActionPayload │
123
+ └─────────────────────┘
124
+ ```
125
+
126
+ ## State Management
127
+
128
+ | State | Provider | Access Hook |
129
+ | -------------- | ----------------- | --------------------------------------------------- |
130
+ | Surfaces | SurfaceProvider | useSurfaceContext, useSurface |
131
+ | Data Models | DataModelProvider | useDataModelContext, useDataBinding, useFormBinding |
132
+ | Action Handler | ActionProvider | useActionContext, useDispatchAction |
133
+
134
+ ## Validation Rules
135
+
136
+ 1. **surfaceId**: Must be unique across all surfaces
137
+ 2. **componentId**: Must be unique within a surface
138
+ 3. **root**: Must reference a valid componentId in the surface
139
+ 4. **path**: Must start with "/" for DataModel references
140
+ 5. **ValueSource**: Exactly one variant must be set