@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,93 @@
1
+ /**
2
+ * ColumnComponent - Vertical flex container.
3
+ */
4
+
5
+ import { memo } from 'react'
6
+ import type { ColumnComponentProps, Distribution, Alignment } from '@/0.8/types'
7
+ import { useDataModel } from '@/0.8/hooks/useDataBinding'
8
+ import { cn } from '@/lib/utils'
9
+ import { getValueByPath } from '@/0.8/utils/pathUtils'
10
+ import { ComponentRenderer } from '../ComponentRenderer'
11
+
12
+ /**
13
+ * Maps distribution values to Tailwind justify-content classes.
14
+ */
15
+ const distributionStyles: Record<Distribution, string> = {
16
+ start: 'justify-start',
17
+ center: 'justify-center',
18
+ end: 'justify-end',
19
+ spaceBetween: 'justify-between',
20
+ spaceAround: 'justify-around',
21
+ spaceEvenly: 'justify-evenly',
22
+ }
23
+
24
+ /**
25
+ * Maps alignment values to Tailwind align-items classes.
26
+ */
27
+ const alignmentStyles: Record<Alignment, string> = {
28
+ start: 'items-start',
29
+ center: 'items-center',
30
+ end: 'items-end',
31
+ stretch: 'items-stretch',
32
+ }
33
+
34
+ /**
35
+ * Column component - vertical flex container.
36
+ */
37
+ export const ColumnComponent = memo(function ColumnComponent({
38
+ surfaceId,
39
+ children,
40
+ distribution = 'start',
41
+ alignment = 'stretch',
42
+ }: ColumnComponentProps) {
43
+ const dataModel = useDataModel(surfaceId)
44
+
45
+ const className = cn(
46
+ 'flex flex-col gap-4',
47
+ distributionStyles[distribution],
48
+ alignmentStyles[alignment]
49
+ )
50
+
51
+ // Render explicit list of children
52
+ if (children?.explicitList) {
53
+ return (
54
+ <div className={className}>
55
+ {children.explicitList.map((childId) => (
56
+ <ComponentRenderer
57
+ key={childId}
58
+ surfaceId={surfaceId}
59
+ componentId={childId}
60
+ />
61
+ ))}
62
+ </div>
63
+ )
64
+ }
65
+
66
+ // Render template-based children
67
+ if (children?.template) {
68
+ const { componentId, dataBinding } = children.template
69
+ const listData = getValueByPath(dataModel, dataBinding)
70
+
71
+ if (!listData || typeof listData !== 'object') {
72
+ return <div className={className} />
73
+ }
74
+
75
+ const items = Object.entries(listData as Record<string, unknown>)
76
+
77
+ return (
78
+ <div className={className}>
79
+ {items.map(([key]) => (
80
+ <ComponentRenderer
81
+ key={key}
82
+ surfaceId={surfaceId}
83
+ componentId={componentId}
84
+ />
85
+ ))}
86
+ </div>
87
+ )
88
+ }
89
+
90
+ return <div className={className} />
91
+ })
92
+
93
+ ColumnComponent.displayName = 'A2UI.Column'
@@ -0,0 +1,81 @@
1
+ /**
2
+ * ListComponent - List container with vertical or horizontal direction.
3
+ */
4
+
5
+ import { memo } from 'react'
6
+ import type { ListComponentProps, Alignment } from '@/0.8/types'
7
+ import { useDataModel } from '@/0.8/hooks/useDataBinding'
8
+ import { cn } from '@/lib/utils'
9
+ import { getValueByPath } from '@/0.8/utils/pathUtils'
10
+ import { ComponentRenderer } from '../ComponentRenderer'
11
+
12
+ /**
13
+ * Maps alignment values to Tailwind align-items classes.
14
+ */
15
+ const alignmentStyles: Record<Alignment, string> = {
16
+ start: 'items-start',
17
+ center: 'items-center',
18
+ end: 'items-end',
19
+ stretch: 'items-stretch',
20
+ }
21
+
22
+ /**
23
+ * List component - container for list items.
24
+ */
25
+ export const ListComponent = memo(function ListComponent({
26
+ surfaceId,
27
+ children,
28
+ direction = 'vertical',
29
+ alignment = 'stretch',
30
+ }: ListComponentProps) {
31
+ const dataModel = useDataModel(surfaceId)
32
+
33
+ const className = cn(
34
+ 'flex gap-3',
35
+ direction === 'horizontal' ? 'flex-row' : 'flex-col',
36
+ alignmentStyles[alignment]
37
+ )
38
+
39
+ // Render explicit list of children
40
+ if (children?.explicitList) {
41
+ return (
42
+ <div className={className}>
43
+ {children.explicitList.map((childId) => (
44
+ <ComponentRenderer
45
+ key={childId}
46
+ surfaceId={surfaceId}
47
+ componentId={childId}
48
+ />
49
+ ))}
50
+ </div>
51
+ )
52
+ }
53
+
54
+ // Render template-based children
55
+ if (children?.template) {
56
+ const { componentId, dataBinding } = children.template
57
+ const listData = getValueByPath(dataModel, dataBinding)
58
+
59
+ if (!listData || typeof listData !== 'object') {
60
+ return <div className={className} />
61
+ }
62
+
63
+ const items = Object.entries(listData as Record<string, unknown>)
64
+
65
+ return (
66
+ <div className={className}>
67
+ {items.map(([key]) => (
68
+ <ComponentRenderer
69
+ key={key}
70
+ surfaceId={surfaceId}
71
+ componentId={componentId}
72
+ />
73
+ ))}
74
+ </div>
75
+ )
76
+ }
77
+
78
+ return <div className={className} />
79
+ })
80
+
81
+ ListComponent.displayName = 'A2UI.List'
@@ -0,0 +1,41 @@
1
+ /**
2
+ * ModalComponent - Modal/Dialog container.
3
+ */
4
+
5
+ import { memo, useState } from 'react'
6
+ import type { ModalComponentProps } from '@/0.8/types'
7
+ import { Dialog, DialogContent, DialogTrigger } from '@/components/ui/dialog'
8
+ import { ComponentRenderer } from '../ComponentRenderer'
9
+
10
+ /**
11
+ * Modal component - dialog container with trigger.
12
+ */
13
+ export const ModalComponent = memo(function ModalComponent({
14
+ surfaceId,
15
+ entryPointChild,
16
+ contentChild,
17
+ }: ModalComponentProps) {
18
+ const [open, setOpen] = useState(false)
19
+
20
+ if (!entryPointChild || !contentChild) {
21
+ return null
22
+ }
23
+
24
+ return (
25
+ <Dialog open={open} onOpenChange={setOpen}>
26
+ <DialogTrigger asChild>
27
+ <div className="cursor-pointer">
28
+ <ComponentRenderer
29
+ surfaceId={surfaceId}
30
+ componentId={entryPointChild}
31
+ />
32
+ </div>
33
+ </DialogTrigger>
34
+ <DialogContent>
35
+ <ComponentRenderer surfaceId={surfaceId} componentId={contentChild} />
36
+ </DialogContent>
37
+ </Dialog>
38
+ )
39
+ })
40
+
41
+ ModalComponent.displayName = 'A2UI.Modal'
@@ -0,0 +1,94 @@
1
+ /**
2
+ * RowComponent - Horizontal flex container.
3
+ */
4
+
5
+ import { memo } from 'react'
6
+ import type { RowComponentProps, Distribution, Alignment } from '@/0.8/types'
7
+ import { useDataModel } from '@/0.8/hooks/useDataBinding'
8
+ import { cn } from '@/lib/utils'
9
+ import { getValueByPath } from '@/0.8/utils/pathUtils'
10
+ import { ComponentRenderer } from '../ComponentRenderer'
11
+
12
+ /**
13
+ * Maps distribution values to Tailwind justify-content classes.
14
+ */
15
+ const distributionStyles: Record<Distribution, string> = {
16
+ start: 'justify-start',
17
+ center: 'justify-center',
18
+ end: 'justify-end',
19
+ spaceBetween: 'justify-between',
20
+ spaceAround: 'justify-around',
21
+ spaceEvenly: 'justify-evenly',
22
+ }
23
+
24
+ /**
25
+ * Maps alignment values to Tailwind align-items classes.
26
+ */
27
+ const alignmentStyles: Record<Alignment, string> = {
28
+ start: 'items-start',
29
+ center: 'items-center',
30
+ end: 'items-end',
31
+ stretch: 'items-stretch',
32
+ }
33
+
34
+ /**
35
+ * Row component - horizontal flex container.
36
+ */
37
+ export const RowComponent = memo(function RowComponent({
38
+ surfaceId,
39
+ children,
40
+ distribution = 'start',
41
+ alignment = 'stretch',
42
+ }: RowComponentProps) {
43
+ const dataModel = useDataModel(surfaceId)
44
+
45
+ const className = cn(
46
+ 'flex flex-row gap-3',
47
+ distributionStyles[distribution],
48
+ alignmentStyles[alignment]
49
+ )
50
+
51
+ // Render explicit list of children
52
+ if (children?.explicitList) {
53
+ return (
54
+ <div className={className}>
55
+ {children.explicitList.map((childId) => (
56
+ <ComponentRenderer
57
+ key={childId}
58
+ surfaceId={surfaceId}
59
+ componentId={childId}
60
+ />
61
+ ))}
62
+ </div>
63
+ )
64
+ }
65
+
66
+ // Render template-based children
67
+ if (children?.template) {
68
+ const { componentId, dataBinding } = children.template
69
+ const listData = getValueByPath(dataModel, dataBinding)
70
+
71
+ if (!listData || typeof listData !== 'object') {
72
+ return <div className={className} />
73
+ }
74
+
75
+ // listData is a Map-like object where each value represents an item
76
+ const items = Object.entries(listData as Record<string, unknown>)
77
+
78
+ return (
79
+ <div className={className}>
80
+ {items.map(([key]) => (
81
+ <ComponentRenderer
82
+ key={key}
83
+ surfaceId={surfaceId}
84
+ componentId={componentId}
85
+ />
86
+ ))}
87
+ </div>
88
+ )
89
+ }
90
+
91
+ return <div className={className} />
92
+ })
93
+
94
+ RowComponent.displayName = 'A2UI.Row'
@@ -0,0 +1,59 @@
1
+ /**
2
+ * TabsComponent - Tabbed content container.
3
+ */
4
+
5
+ import { memo } from 'react'
6
+ import type { TabsComponentProps, ValueSource } from '@/0.8/types'
7
+ import { useDataBinding } from '@/0.8/hooks/useDataBinding'
8
+ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
9
+ import { ComponentRenderer } from '../ComponentRenderer'
10
+
11
+ /**
12
+ * Tabs component - tabbed content container.
13
+ */
14
+ export const TabsComponent = memo(function TabsComponent({
15
+ surfaceId,
16
+ tabItems,
17
+ }: TabsComponentProps) {
18
+ if (!tabItems || tabItems.length === 0) {
19
+ return null
20
+ }
21
+
22
+ // Get the first tab as default
23
+ const defaultTab = tabItems[0].child
24
+
25
+ return (
26
+ <Tabs defaultValue={defaultTab} className="w-full">
27
+ <TabsList>
28
+ {tabItems.map((item, index) => (
29
+ <TabsTrigger key={item.child} value={item.child}>
30
+ <TabTitle surfaceId={surfaceId} title={item.title} index={index} />
31
+ </TabsTrigger>
32
+ ))}
33
+ </TabsList>
34
+ {tabItems.map((item) => (
35
+ <TabsContent key={item.child} value={item.child}>
36
+ <ComponentRenderer surfaceId={surfaceId} componentId={item.child} />
37
+ </TabsContent>
38
+ ))}
39
+ </Tabs>
40
+ )
41
+ })
42
+
43
+ /**
44
+ * Helper component to resolve tab titles.
45
+ */
46
+ function TabTitle({
47
+ surfaceId,
48
+ title,
49
+ index,
50
+ }: {
51
+ surfaceId: string
52
+ title: ValueSource | undefined
53
+ index: number
54
+ }) {
55
+ const titleText = useDataBinding<string>(surfaceId, title, `Tab ${index + 1}`)
56
+ return <>{titleText}</>
57
+ }
58
+
59
+ TabsComponent.displayName = 'A2UI.Tabs'
@@ -0,0 +1,10 @@
1
+ /**
2
+ * A2UI React Renderer - Layout Components
3
+ */
4
+
5
+ export { RowComponent } from './RowComponent'
6
+ export { ColumnComponent } from './ColumnComponent'
7
+ export { ListComponent } from './ListComponent'
8
+ export { CardComponent } from './CardComponent'
9
+ export { TabsComponent } from './TabsComponent'
10
+ export { ModalComponent } from './ModalComponent'