@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.
- package/.claude/commands/speckit.analyze.md +184 -0
- package/.claude/commands/speckit.checklist.md +294 -0
- package/.claude/commands/speckit.clarify.md +181 -0
- package/.claude/commands/speckit.constitution.md +82 -0
- package/.claude/commands/speckit.implement.md +135 -0
- package/.claude/commands/speckit.plan.md +89 -0
- package/.claude/commands/speckit.specify.md +256 -0
- package/.claude/commands/speckit.tasks.md +137 -0
- package/.claude/commands/speckit.taskstoissues.md +30 -0
- package/.github/workflows/deploy.yml +69 -0
- package/.husky/pre-commit +1 -0
- package/.prettierignore +4 -0
- package/.prettierrc +7 -0
- package/.specify/memory/constitution.md +73 -0
- package/.specify/scripts/bash/check-prerequisites.sh +166 -0
- package/.specify/scripts/bash/common.sh +156 -0
- package/.specify/scripts/bash/create-new-feature.sh +297 -0
- package/.specify/scripts/bash/setup-plan.sh +61 -0
- package/.specify/scripts/bash/update-agent-context.sh +799 -0
- package/.specify/templates/agent-file-template.md +28 -0
- package/.specify/templates/checklist-template.md +40 -0
- package/.specify/templates/plan-template.md +105 -0
- package/.specify/templates/spec-template.md +115 -0
- package/.specify/templates/tasks-template.md +250 -0
- package/CLAUDE.md +105 -0
- package/CONTRIBUTING.md +97 -0
- package/README.md +126 -0
- package/components.json +21 -0
- package/eslint.config.js +25 -0
- package/netlify.toml +50 -0
- package/package.json +94 -0
- package/playground/README.md +75 -0
- package/playground/index.html +22 -0
- package/playground/package.json +32 -0
- package/playground/public/favicon.svg +8 -0
- package/playground/src/App.css +256 -0
- package/playground/src/App.tsx +115 -0
- package/playground/src/assets/react.svg +1 -0
- package/playground/src/components/ErrorDisplay.tsx +13 -0
- package/playground/src/components/ExampleSelector.tsx +64 -0
- package/playground/src/components/Header.tsx +47 -0
- package/playground/src/components/JsonEditor.tsx +32 -0
- package/playground/src/components/Preview.tsx +78 -0
- package/playground/src/components/ThemeToggle.tsx +19 -0
- package/playground/src/data/examples.ts +1571 -0
- package/playground/src/hooks/useTheme.ts +55 -0
- package/playground/src/index.css +220 -0
- package/playground/src/main.tsx +10 -0
- package/playground/tsconfig.app.json +34 -0
- package/playground/tsconfig.json +13 -0
- package/playground/tsconfig.node.json +26 -0
- package/playground/vite.config.ts +31 -0
- package/specs/001-a2ui-renderer/checklists/requirements.md +41 -0
- package/specs/001-a2ui-renderer/data-model.md +140 -0
- package/specs/001-a2ui-renderer/plan.md +123 -0
- package/specs/001-a2ui-renderer/quickstart.md +141 -0
- package/specs/001-a2ui-renderer/research.md +140 -0
- package/specs/001-a2ui-renderer/spec.md +165 -0
- package/specs/001-a2ui-renderer/tasks.md +310 -0
- package/specs/002-playground/checklists/requirements.md +37 -0
- package/specs/002-playground/contracts/components.md +120 -0
- package/specs/002-playground/data-model.md +149 -0
- package/specs/002-playground/plan.md +73 -0
- package/specs/002-playground/quickstart.md +158 -0
- package/specs/002-playground/research.md +117 -0
- package/specs/002-playground/spec.md +109 -0
- package/specs/002-playground/tasks.md +224 -0
- package/src/0.8/A2UIRender.test.tsx +793 -0
- package/src/0.8/A2UIRender.tsx +142 -0
- package/src/0.8/components/ComponentRenderer.test.tsx +373 -0
- package/src/0.8/components/ComponentRenderer.tsx +163 -0
- package/src/0.8/components/UnknownComponent.tsx +49 -0
- package/src/0.8/components/display/AudioPlayerComponent.tsx +37 -0
- package/src/0.8/components/display/DividerComponent.tsx +23 -0
- package/src/0.8/components/display/IconComponent.tsx +137 -0
- package/src/0.8/components/display/ImageComponent.tsx +57 -0
- package/src/0.8/components/display/TextComponent.tsx +56 -0
- package/src/0.8/components/display/VideoComponent.tsx +31 -0
- package/src/0.8/components/display/display.test.tsx +660 -0
- package/src/0.8/components/display/index.ts +10 -0
- package/src/0.8/components/index.ts +14 -0
- package/src/0.8/components/interactive/ButtonComponent.tsx +44 -0
- package/src/0.8/components/interactive/CheckBoxComponent.tsx +45 -0
- package/src/0.8/components/interactive/DateTimeInputComponent.tsx +176 -0
- package/src/0.8/components/interactive/MultipleChoiceComponent.tsx +157 -0
- package/src/0.8/components/interactive/SliderComponent.tsx +53 -0
- package/src/0.8/components/interactive/TextFieldComponent.tsx +65 -0
- package/src/0.8/components/interactive/index.ts +10 -0
- package/src/0.8/components/interactive/interactive.test.tsx +618 -0
- package/src/0.8/components/layout/CardComponent.tsx +30 -0
- package/src/0.8/components/layout/ColumnComponent.tsx +93 -0
- package/src/0.8/components/layout/ListComponent.tsx +81 -0
- package/src/0.8/components/layout/ModalComponent.tsx +41 -0
- package/src/0.8/components/layout/RowComponent.tsx +94 -0
- package/src/0.8/components/layout/TabsComponent.tsx +59 -0
- package/src/0.8/components/layout/index.ts +10 -0
- package/src/0.8/components/layout/layout.test.tsx +558 -0
- package/src/0.8/contexts/A2UIProvider.test.tsx +226 -0
- package/src/0.8/contexts/A2UIProvider.tsx +54 -0
- package/src/0.8/contexts/ActionContext.test.tsx +242 -0
- package/src/0.8/contexts/ActionContext.tsx +105 -0
- package/src/0.8/contexts/ComponentsMapContext.tsx +125 -0
- package/src/0.8/contexts/DataModelContext.test.tsx +335 -0
- package/src/0.8/contexts/DataModelContext.tsx +184 -0
- package/src/0.8/contexts/SurfaceContext.test.tsx +339 -0
- package/src/0.8/contexts/SurfaceContext.tsx +197 -0
- package/src/0.8/hooks/useA2UIMessageHandler.test.tsx +399 -0
- package/src/0.8/hooks/useA2UIMessageHandler.ts +123 -0
- package/src/0.8/hooks/useComponent.test.tsx +148 -0
- package/src/0.8/hooks/useComponent.ts +39 -0
- package/src/0.8/hooks/useDataBinding.test.tsx +334 -0
- package/src/0.8/hooks/useDataBinding.ts +99 -0
- package/src/0.8/hooks/useDispatchAction.test.tsx +83 -0
- package/src/0.8/hooks/useDispatchAction.ts +35 -0
- package/src/0.8/hooks/useSurface.test.tsx +114 -0
- package/src/0.8/hooks/useSurface.ts +34 -0
- package/src/0.8/index.ts +38 -0
- package/src/0.8/schemas/client_to_server.json +50 -0
- package/src/0.8/schemas/server_to_client.json +148 -0
- package/src/0.8/schemas/standard_catalog_definition.json +661 -0
- package/src/0.8/types/index.ts +448 -0
- package/src/0.8/utils/dataBinding.test.ts +443 -0
- package/src/0.8/utils/dataBinding.ts +212 -0
- package/src/0.8/utils/pathUtils.test.ts +353 -0
- package/src/0.8/utils/pathUtils.ts +200 -0
- package/src/components/ui/button.tsx +62 -0
- package/src/components/ui/calendar.tsx +220 -0
- package/src/components/ui/card.tsx +92 -0
- package/src/components/ui/checkbox.tsx +30 -0
- package/src/components/ui/dialog.tsx +141 -0
- package/src/components/ui/input.tsx +21 -0
- package/src/components/ui/label.tsx +22 -0
- package/src/components/ui/native-select.tsx +53 -0
- package/src/components/ui/popover.tsx +46 -0
- package/src/components/ui/select.tsx +188 -0
- package/src/components/ui/separator.tsx +26 -0
- package/src/components/ui/slider.tsx +61 -0
- package/src/components/ui/tabs.tsx +64 -0
- package/src/components/ui/textarea.tsx +18 -0
- package/src/index.ts +1 -0
- package/src/lib/utils.ts +6 -0
- package/tsconfig.json +28 -0
- package/vite.config.ts +29 -0
- package/vitest.config.ts +22 -0
- package/vitest.setup.ts +8 -0
- package/website/README.md +4 -0
- package/website/assets/favicon.svg +8 -0
- package/website/content/.gitkeep +0 -0
- package/website/content/index.md +122 -0
- package/website/global.d.ts +9 -0
- package/website/package.json +17 -0
- package/website/plain.config.js +28 -0
- package/website/serve.json +6 -0
- package/website/src/client/color-mode-switch.css +47 -0
- package/website/src/client/index.js +61 -0
- package/website/src/client/moon.svg +1 -0
- package/website/src/client/sun.svg +1 -0
- package/website/src/components/Footer.jsx +9 -0
- package/website/src/components/Header.jsx +44 -0
- package/website/src/components/Page.jsx +28 -0
- 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,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,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
|