@gram-ai/elements 1.25.2 → 1.26.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/dist/components/Chat/stories/Charts.stories.d.ts +37 -0
- package/dist/components/Chat/stories/GenerativeUI.stories.d.ts +17 -0
- package/dist/components/ui/button.d.ts +1 -1
- package/dist/components/ui/buttonVariants.d.ts +1 -1
- package/dist/components/ui/charts.stories.d.ts +43 -0
- package/dist/components/ui/generative-ui.stories.d.ts +53 -0
- package/dist/elements.cjs +1 -1
- package/dist/elements.css +1 -1
- package/dist/elements.js +6 -6
- package/dist/index-BJnv49-A.js +37057 -0
- package/dist/index-BJnv49-A.js.map +1 -0
- package/dist/index-BpJstUh1.cjs +280 -0
- package/dist/index-BpJstUh1.cjs.map +1 -0
- package/dist/index-CUitXazZ.js +30426 -0
- package/dist/index-CUitXazZ.js.map +1 -0
- package/dist/index-ChW-CSuu.cjs +147 -0
- package/dist/index-ChW-CSuu.cjs.map +1 -0
- package/dist/plugins/chart/catalog.d.ts +123 -0
- package/dist/plugins/chart/index.d.ts +1 -1
- package/dist/plugins/chart/ui/area-chart.d.ts +16 -0
- package/dist/plugins/chart/ui/bar-chart.d.ts +16 -0
- package/dist/plugins/chart/ui/donut-chart.d.ts +17 -0
- package/dist/plugins/chart/ui/index.d.ts +7 -0
- package/dist/plugins/chart/ui/line-chart.d.ts +17 -0
- package/dist/plugins/chart/ui/pie-chart.d.ts +15 -0
- package/dist/plugins/chart/ui/radar-chart.d.ts +14 -0
- package/dist/plugins/chart/ui/scatter-chart.d.ts +18 -0
- package/dist/plugins/components/MacOSWindowFrame.d.ts +13 -0
- package/dist/plugins/components/PluginLoadingState.d.ts +1 -1
- package/dist/plugins/generative-ui/catalog.d.ts +293 -0
- package/dist/plugins/generative-ui/ui/accordion-wrapper.d.ts +18 -0
- package/dist/plugins/generative-ui/ui/accordion.d.ts +7 -0
- package/dist/plugins/generative-ui/ui/action-button.d.ts +10 -0
- package/dist/plugins/generative-ui/ui/alert-wrapper.d.ts +9 -0
- package/dist/plugins/generative-ui/ui/alert.d.ts +9 -0
- package/dist/plugins/generative-ui/ui/avatar-wrapper.d.ts +9 -0
- package/dist/plugins/generative-ui/ui/avatar.d.ts +11 -0
- package/dist/plugins/generative-ui/ui/badge.d.ts +12 -0
- package/dist/plugins/generative-ui/ui/button-wrapper.d.ts +15 -0
- package/dist/plugins/generative-ui/ui/button.d.ts +10 -0
- package/dist/plugins/generative-ui/ui/card-wrapper.d.ts +10 -0
- package/dist/plugins/generative-ui/ui/card.d.ts +9 -0
- package/dist/plugins/generative-ui/ui/checkbox-wrapper.d.ts +10 -0
- package/dist/plugins/generative-ui/ui/checkbox.d.ts +4 -0
- package/dist/plugins/generative-ui/ui/data-table.d.ts +10 -0
- package/dist/plugins/generative-ui/ui/dialog.d.ts +17 -0
- package/dist/plugins/generative-ui/ui/dropdown-menu.d.ts +25 -0
- package/dist/plugins/generative-ui/ui/grid.d.ts +6 -0
- package/dist/plugins/generative-ui/ui/index.d.ts +40 -0
- package/dist/plugins/generative-ui/ui/input-wrapper.d.ts +11 -0
- package/dist/plugins/generative-ui/ui/input.d.ts +3 -0
- package/dist/plugins/generative-ui/ui/label.d.ts +4 -0
- package/dist/plugins/generative-ui/ui/list.d.ts +6 -0
- package/dist/plugins/generative-ui/ui/metric.d.ts +7 -0
- package/dist/plugins/generative-ui/ui/pagination.d.ts +13 -0
- package/dist/plugins/generative-ui/ui/popover.d.ts +10 -0
- package/dist/plugins/generative-ui/ui/progress.d.ts +10 -0
- package/dist/plugins/generative-ui/ui/radio-group.d.ts +5 -0
- package/dist/plugins/generative-ui/ui/select-wrapper.d.ts +13 -0
- package/dist/plugins/generative-ui/ui/select.d.ts +15 -0
- package/dist/plugins/generative-ui/ui/separator.d.ts +4 -0
- package/dist/plugins/generative-ui/ui/skeleton-wrapper.d.ts +9 -0
- package/dist/plugins/generative-ui/ui/skeleton.d.ts +2 -0
- package/dist/plugins/generative-ui/ui/stack.d.ts +8 -0
- package/dist/plugins/generative-ui/ui/switch.d.ts +6 -0
- package/dist/plugins/generative-ui/ui/table.d.ts +10 -0
- package/dist/plugins/generative-ui/ui/tabs-wrapper.d.ts +21 -0
- package/dist/plugins/generative-ui/ui/tabs.d.ts +11 -0
- package/dist/plugins/generative-ui/ui/text.d.ts +7 -0
- package/dist/plugins/generative-ui/ui/textarea.d.ts +3 -0
- package/dist/plugins/generative-ui/ui/tooltip.d.ts +7 -0
- package/dist/plugins.cjs +1 -1
- package/dist/plugins.js +1 -1
- package/dist/{profiler-BaG0scxd.js → profiler-D4Tw5ecI.js} +2 -2
- package/dist/{profiler-BaG0scxd.js.map → profiler-D4Tw5ecI.js.map} +1 -1
- package/dist/{profiler-CuqENACf.cjs → profiler-DCWYDZ1F.cjs} +2 -2
- package/dist/{profiler-CuqENACf.cjs.map → profiler-DCWYDZ1F.cjs.map} +1 -1
- package/dist/{startRecording-BiLmoqZa.cjs → startRecording-3sTskM3H.cjs} +2 -2
- package/dist/{startRecording-BiLmoqZa.cjs.map → startRecording-3sTskM3H.cjs.map} +1 -1
- package/dist/{startRecording-86bHmd-l.js → startRecording-BHhcCWQE.js} +2 -2
- package/dist/{startRecording-86bHmd-l.js.map → startRecording-BHhcCWQE.js.map} +1 -1
- package/package.json +4 -1
- package/src/components/Chat/stories/Charts.stories.tsx +260 -0
- package/src/components/Chat/stories/ConnectionConfiguration.stories.tsx +6 -6
- package/src/components/Chat/stories/GenerativeUI.stories.tsx +113 -0
- package/src/components/Replay.stories.tsx +1 -1
- package/src/components/Replay.tsx +18 -13
- package/src/components/ui/charts.stories.tsx +246 -0
- package/src/components/ui/generative-ui.stories.tsx +557 -0
- package/src/components/ui/generative-ui.tsx +60 -360
- package/src/components/ui/tool-ui.stories.tsx +6 -3
- package/src/hooks/useAuth.ts +17 -1
- package/src/hooks/useFollowOnSuggestions.ts +6 -1
- package/src/plugins/chart/catalog.ts +141 -0
- package/src/plugins/chart/component.tsx +79 -125
- package/src/plugins/chart/index.ts +141 -89
- package/src/plugins/chart/ui/area-chart.tsx +133 -0
- package/src/plugins/chart/ui/bar-chart.tsx +137 -0
- package/src/plugins/chart/ui/donut-chart.tsx +167 -0
- package/src/plugins/chart/ui/index.ts +7 -0
- package/src/plugins/chart/ui/line-chart.tsx +135 -0
- package/src/plugins/chart/ui/pie-chart.tsx +148 -0
- package/src/plugins/chart/ui/radar-chart.tsx +105 -0
- package/src/plugins/chart/ui/scatter-chart.tsx +132 -0
- package/src/plugins/components/MacOSWindowFrame.tsx +55 -0
- package/src/plugins/components/PluginLoadingState.tsx +9 -13
- package/src/plugins/generative-ui/catalog.ts +277 -0
- package/src/plugins/generative-ui/component.tsx +112 -21
- package/src/plugins/generative-ui/index.ts +20 -141
- package/src/plugins/generative-ui/ui/accordion-wrapper.tsx +57 -0
- package/src/plugins/generative-ui/ui/accordion.tsx +66 -0
- package/src/plugins/generative-ui/ui/action-button.tsx +68 -0
- package/src/plugins/generative-ui/ui/alert-wrapper.tsx +26 -0
- package/src/plugins/generative-ui/ui/alert.tsx +66 -0
- package/src/plugins/generative-ui/ui/avatar-wrapper.tsx +22 -0
- package/src/plugins/generative-ui/ui/avatar.tsx +109 -0
- package/src/plugins/generative-ui/ui/badge.tsx +65 -0
- package/src/plugins/generative-ui/ui/button-wrapper.tsx +32 -0
- package/src/plugins/generative-ui/ui/button.tsx +65 -0
- package/src/plugins/generative-ui/ui/card-wrapper.tsx +36 -0
- package/src/plugins/generative-ui/ui/card.tsx +92 -0
- package/src/plugins/generative-ui/ui/checkbox-wrapper.tsx +39 -0
- package/src/plugins/generative-ui/ui/checkbox.tsx +32 -0
- package/src/plugins/generative-ui/ui/data-table.tsx +53 -0
- package/src/plugins/generative-ui/ui/dialog.tsx +158 -0
- package/src/plugins/generative-ui/ui/dropdown-menu.tsx +257 -0
- package/src/plugins/generative-ui/ui/grid.tsx +29 -0
- package/src/plugins/generative-ui/ui/index.ts +43 -0
- package/src/plugins/generative-ui/ui/input-wrapper.tsx +38 -0
- package/src/plugins/generative-ui/ui/input.tsx +21 -0
- package/src/plugins/generative-ui/ui/label.tsx +24 -0
- package/src/plugins/generative-ui/ui/list.tsx +34 -0
- package/src/plugins/generative-ui/ui/metric.tsx +53 -0
- package/src/plugins/generative-ui/ui/pagination.tsx +127 -0
- package/src/plugins/generative-ui/ui/popover.tsx +89 -0
- package/src/plugins/generative-ui/ui/progress.tsx +57 -0
- package/src/plugins/generative-ui/ui/radio-group.tsx +45 -0
- package/src/plugins/generative-ui/ui/select-wrapper.tsx +41 -0
- package/src/plugins/generative-ui/ui/select.tsx +190 -0
- package/src/plugins/generative-ui/ui/separator.tsx +28 -0
- package/src/plugins/generative-ui/ui/skeleton-wrapper.tsx +30 -0
- package/src/plugins/generative-ui/ui/skeleton.tsx +13 -0
- package/src/plugins/generative-ui/ui/stack.tsx +54 -0
- package/src/plugins/generative-ui/ui/switch.tsx +35 -0
- package/src/plugins/generative-ui/ui/table.tsx +116 -0
- package/src/plugins/generative-ui/ui/tabs-wrapper.tsx +51 -0
- package/src/plugins/generative-ui/ui/tabs.tsx +92 -0
- package/src/plugins/generative-ui/ui/text.tsx +33 -0
- package/src/plugins/generative-ui/ui/textarea.tsx +18 -0
- package/src/plugins/generative-ui/ui/tooltip.tsx +57 -0
- package/dist/components/Chat/stories/Plugins.stories.d.ts +0 -12
- package/dist/index-B8nSCdu4.cjs +0 -251
- package/dist/index-B8nSCdu4.cjs.map +0 -1
- package/dist/index-CAtaLV1E.cjs +0 -187
- package/dist/index-CAtaLV1E.cjs.map +0 -1
- package/dist/index-CJrwma08.js +0 -27232
- package/dist/index-CJrwma08.js.map +0 -1
- package/dist/index-DLWQ91ow.js +0 -40049
- package/dist/index-DLWQ91ow.js.map +0 -1
- package/src/components/Chat/stories/Plugins.stories.tsx +0 -158
|
@@ -1,18 +1,36 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import { useToolExecution } from '@/contexts/ToolExecutionContext'
|
|
4
3
|
import { useDensity } from '@/hooks/useDensity'
|
|
5
|
-
import { useRadius } from '@/hooks/useRadius'
|
|
6
4
|
import { cn } from '@/lib/utils'
|
|
7
5
|
import { isJsonRenderTree, type JsonRenderNode } from '@/lib/generative-ui'
|
|
8
|
-
import {
|
|
6
|
+
import { AlertCircleIcon } from 'lucide-react'
|
|
7
|
+
import { FC, useMemo } from 'react'
|
|
8
|
+
|
|
9
|
+
// Import all components from the generative-ui plugin ui directory
|
|
9
10
|
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
AccordionWrapper,
|
|
12
|
+
AccordionItemWrapper,
|
|
13
|
+
ActionButton,
|
|
14
|
+
AlertWrapper,
|
|
15
|
+
AvatarWrapper,
|
|
16
|
+
Badge,
|
|
17
|
+
ButtonWrapper,
|
|
18
|
+
CardWrapper,
|
|
19
|
+
CheckboxWrapper,
|
|
20
|
+
DataTable,
|
|
21
|
+
Grid,
|
|
22
|
+
InputWrapper,
|
|
23
|
+
List,
|
|
24
|
+
Metric,
|
|
25
|
+
Progress,
|
|
26
|
+
SelectWrapper,
|
|
27
|
+
Separator,
|
|
28
|
+
SkeletonWrapper,
|
|
29
|
+
Stack,
|
|
30
|
+
TabsWrapper,
|
|
31
|
+
TabContentWrapper,
|
|
32
|
+
Text,
|
|
33
|
+
} from '@/plugins/generative-ui/ui'
|
|
16
34
|
|
|
17
35
|
interface GenerativeUIProps {
|
|
18
36
|
/** The JSON content to render - can be a json-render tree or raw object */
|
|
@@ -25,357 +43,39 @@ interface GenerativeUIProps {
|
|
|
25
43
|
* Built-in components for rendering json-render trees.
|
|
26
44
|
* These provide a default set of UI primitives for tool results.
|
|
27
45
|
*/
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
style: 'percent',
|
|
62
|
-
minimumFractionDigits: 1,
|
|
63
|
-
}).format(numValue)
|
|
64
|
-
case 'number':
|
|
65
|
-
default:
|
|
66
|
-
return new Intl.NumberFormat('en-US').format(numValue)
|
|
67
|
-
}
|
|
68
|
-
}, [value, format])
|
|
69
|
-
|
|
70
|
-
return (
|
|
71
|
-
<div className={cn('flex flex-col gap-2', className as string)}>
|
|
72
|
-
<span className="text-muted-foreground text-sm">{String(label)}</span>
|
|
73
|
-
<span className="text-3xl font-bold">{formattedValue}</span>
|
|
74
|
-
</div>
|
|
75
|
-
)
|
|
76
|
-
},
|
|
77
|
-
|
|
78
|
-
Grid: ({ columns = 2, children, className }) => {
|
|
79
|
-
const d = useDensity()
|
|
80
|
-
return (
|
|
81
|
-
<div
|
|
82
|
-
className={cn('grid', d('gap-lg'), className as string)}
|
|
83
|
-
style={{
|
|
84
|
-
gridTemplateColumns: `repeat(${columns as number}, minmax(0, 1fr))`,
|
|
85
|
-
}}
|
|
86
|
-
>
|
|
87
|
-
{children as React.ReactNode}
|
|
88
|
-
</div>
|
|
89
|
-
)
|
|
90
|
-
},
|
|
91
|
-
|
|
92
|
-
Stack: ({ direction = 'vertical', children, className }) => {
|
|
93
|
-
const d = useDensity()
|
|
94
|
-
return (
|
|
95
|
-
<div
|
|
96
|
-
className={cn(
|
|
97
|
-
'flex',
|
|
98
|
-
direction === 'horizontal' ? 'flex-row' : 'flex-col',
|
|
99
|
-
d('gap-md'),
|
|
100
|
-
className as string
|
|
101
|
-
)}
|
|
102
|
-
>
|
|
103
|
-
{children as React.ReactNode}
|
|
104
|
-
</div>
|
|
105
|
-
)
|
|
106
|
-
},
|
|
107
|
-
|
|
108
|
-
Text: ({ children, content, variant = 'body', className }) => {
|
|
109
|
-
const variantClasses: Record<string, string> = {
|
|
110
|
-
heading: 'text-lg font-semibold',
|
|
111
|
-
body: 'text-sm',
|
|
112
|
-
caption: 'text-xs text-muted-foreground',
|
|
113
|
-
code: 'font-mono text-sm bg-muted px-1 rounded',
|
|
114
|
-
}
|
|
115
|
-
// Support both content prop (for direct text) and children (for nested components)
|
|
116
|
-
const textContent = content != null ? String(content) : null
|
|
117
|
-
return (
|
|
118
|
-
<span
|
|
119
|
-
className={cn(variantClasses[variant as string], className as string)}
|
|
120
|
-
>
|
|
121
|
-
{textContent ?? (children as React.ReactNode)}
|
|
122
|
-
</span>
|
|
123
|
-
)
|
|
124
|
-
},
|
|
125
|
-
|
|
126
|
-
Badge: ({ children, content, variant = 'default', className }) => {
|
|
127
|
-
const r = useRadius()
|
|
128
|
-
const variantClasses: Record<string, string> = {
|
|
129
|
-
default: 'bg-primary text-primary-foreground',
|
|
130
|
-
secondary: 'bg-secondary text-secondary-foreground',
|
|
131
|
-
success:
|
|
132
|
-
'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-100',
|
|
133
|
-
warning:
|
|
134
|
-
'bg-amber-100 text-amber-800 dark:bg-amber-900 dark:text-amber-100',
|
|
135
|
-
error: 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-100',
|
|
136
|
-
}
|
|
137
|
-
// Support both content prop (for direct text) and children (for nested components)
|
|
138
|
-
const textContent = content != null ? String(content) : null
|
|
139
|
-
return (
|
|
140
|
-
<span
|
|
141
|
-
className={cn(
|
|
142
|
-
'inline-flex items-center px-2 py-0.5 text-xs font-medium',
|
|
143
|
-
r('sm'),
|
|
144
|
-
variantClasses[variant as string] ?? variantClasses.default,
|
|
145
|
-
className as string
|
|
146
|
-
)}
|
|
147
|
-
>
|
|
148
|
-
{textContent ?? (children as React.ReactNode)}
|
|
149
|
-
</span>
|
|
150
|
-
)
|
|
151
|
-
},
|
|
152
|
-
|
|
153
|
-
Table: ({ headers, rows, className }) => {
|
|
154
|
-
const r = useRadius()
|
|
155
|
-
const headerArray = Array.isArray(headers) ? (headers as string[]) : []
|
|
156
|
-
const rowsArray = Array.isArray(rows) ? (rows as unknown[][]) : []
|
|
157
|
-
return (
|
|
158
|
-
<div className={cn('overflow-auto', className as string)}>
|
|
159
|
-
<table className={cn('w-full border-collapse text-sm', r('lg'))}>
|
|
160
|
-
{headerArray.length > 0 && (
|
|
161
|
-
<thead>
|
|
162
|
-
<tr className="border-border border-b">
|
|
163
|
-
{headerArray.map((header, i) => (
|
|
164
|
-
<th
|
|
165
|
-
key={i}
|
|
166
|
-
className="text-muted-foreground px-4 py-3 text-left font-medium"
|
|
167
|
-
>
|
|
168
|
-
{header}
|
|
169
|
-
</th>
|
|
170
|
-
))}
|
|
171
|
-
</tr>
|
|
172
|
-
</thead>
|
|
173
|
-
)}
|
|
174
|
-
<tbody>
|
|
175
|
-
{rowsArray.map((row, i) => (
|
|
176
|
-
<tr key={i} className="border-border border-b last:border-0">
|
|
177
|
-
{row.map((cell, j) => (
|
|
178
|
-
<td key={j} className="px-4 py-3">
|
|
179
|
-
{String(cell)}
|
|
180
|
-
</td>
|
|
181
|
-
))}
|
|
182
|
-
</tr>
|
|
183
|
-
))}
|
|
184
|
-
</tbody>
|
|
185
|
-
</table>
|
|
186
|
-
</div>
|
|
187
|
-
)
|
|
188
|
-
},
|
|
189
|
-
|
|
190
|
-
List: ({ items, ordered = false, className }) => {
|
|
191
|
-
const Tag = ordered ? 'ol' : 'ul'
|
|
192
|
-
const itemsArray = Array.isArray(items) ? (items as string[]) : []
|
|
193
|
-
return (
|
|
194
|
-
<Tag
|
|
195
|
-
className={cn(
|
|
196
|
-
'list-inside space-y-2 text-sm',
|
|
197
|
-
ordered ? 'list-decimal' : 'list-disc',
|
|
198
|
-
className as string
|
|
199
|
-
)}
|
|
200
|
-
>
|
|
201
|
-
{itemsArray.map((item, i) => (
|
|
202
|
-
<li key={i}>{item}</li>
|
|
203
|
-
))}
|
|
204
|
-
</Tag>
|
|
205
|
-
)
|
|
206
|
-
},
|
|
207
|
-
|
|
208
|
-
Divider: ({ className }) => (
|
|
209
|
-
<hr className={cn('border-border my-4', className as string)} />
|
|
210
|
-
),
|
|
211
|
-
|
|
212
|
-
Progress: ({ value, max = 100, label, className }) => {
|
|
213
|
-
const r = useRadius()
|
|
214
|
-
const numValue = Number(value)
|
|
215
|
-
const numMax = Number(max)
|
|
216
|
-
const percentage =
|
|
217
|
-
isNaN(numValue) || isNaN(numMax) || numMax === 0
|
|
218
|
-
? 0
|
|
219
|
-
: Math.min(100, Math.max(0, (numValue / numMax) * 100))
|
|
220
|
-
const labelStr = label != null ? String(label) : null
|
|
221
|
-
return (
|
|
222
|
-
<div className={cn('w-full space-y-2', className as string)}>
|
|
223
|
-
{labelStr && (
|
|
224
|
-
<div className="flex justify-between text-sm">
|
|
225
|
-
<span>{labelStr}</span>
|
|
226
|
-
<span className="text-muted-foreground">
|
|
227
|
-
{percentage.toFixed(0)}%
|
|
228
|
-
</span>
|
|
229
|
-
</div>
|
|
230
|
-
)}
|
|
231
|
-
<div className={cn('bg-muted h-3 overflow-hidden', r('sm'))}>
|
|
232
|
-
<div
|
|
233
|
-
className={cn('bg-primary h-full transition-all', r('sm'))}
|
|
234
|
-
style={{ width: `${percentage}%` }}
|
|
235
|
-
/>
|
|
236
|
-
</div>
|
|
237
|
-
</div>
|
|
238
|
-
)
|
|
239
|
-
},
|
|
240
|
-
|
|
241
|
-
ActionButton: ({ label, action, args, variant = 'default', className }) => {
|
|
242
|
-
const r = useRadius()
|
|
243
|
-
const { executeTool, isToolAvailable } = useToolExecution()
|
|
244
|
-
const runtime = useThreadRuntime({ optional: true })
|
|
245
|
-
const [isLoading, setIsLoading] = useState(false)
|
|
246
|
-
const [result, setResult] = useState<{
|
|
247
|
-
success: boolean
|
|
248
|
-
message?: string
|
|
249
|
-
} | null>(null)
|
|
250
|
-
|
|
251
|
-
const toolAvailable = action ? isToolAvailable(action as string) : false
|
|
252
|
-
|
|
253
|
-
const handleClick = useCallback(async () => {
|
|
254
|
-
if (!action) return
|
|
255
|
-
|
|
256
|
-
setIsLoading(true)
|
|
257
|
-
setResult(null)
|
|
258
|
-
|
|
259
|
-
try {
|
|
260
|
-
const toolResult = await executeTool(
|
|
261
|
-
action as string,
|
|
262
|
-
(args as Record<string, unknown>) ?? {}
|
|
263
|
-
)
|
|
264
|
-
|
|
265
|
-
if (toolResult.success) {
|
|
266
|
-
// Format the result message
|
|
267
|
-
let message = 'Done'
|
|
268
|
-
if (toolResult.result) {
|
|
269
|
-
if (typeof toolResult.result === 'string') {
|
|
270
|
-
message = toolResult.result
|
|
271
|
-
} else if (
|
|
272
|
-
typeof toolResult.result === 'object' &&
|
|
273
|
-
toolResult.result !== null &&
|
|
274
|
-
'content' in toolResult.result
|
|
275
|
-
) {
|
|
276
|
-
// Handle MCP tool result format
|
|
277
|
-
const content = (
|
|
278
|
-
toolResult.result as { content: Array<{ text?: string }> }
|
|
279
|
-
).content
|
|
280
|
-
if (Array.isArray(content) && content[0]?.text) {
|
|
281
|
-
message = content[0].text
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
setResult({ success: true, message })
|
|
286
|
-
|
|
287
|
-
// Notify the LLM of the action result so it can respond
|
|
288
|
-
if (runtime) {
|
|
289
|
-
await runtime.append({
|
|
290
|
-
role: 'user',
|
|
291
|
-
content: [
|
|
292
|
-
{
|
|
293
|
-
type: 'text',
|
|
294
|
-
text: `[Action completed] ${action}: ${message}`,
|
|
295
|
-
},
|
|
296
|
-
],
|
|
297
|
-
})
|
|
298
|
-
}
|
|
299
|
-
} else {
|
|
300
|
-
setResult({ success: false, message: toolResult.error })
|
|
301
|
-
|
|
302
|
-
// Also notify on failure so LLM can help
|
|
303
|
-
if (runtime) {
|
|
304
|
-
await runtime.append({
|
|
305
|
-
role: 'user',
|
|
306
|
-
content: [
|
|
307
|
-
{
|
|
308
|
-
type: 'text',
|
|
309
|
-
text: `[Action failed] ${action}: ${toolResult.error}`,
|
|
310
|
-
},
|
|
311
|
-
],
|
|
312
|
-
})
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
} catch (err) {
|
|
316
|
-
const errorMessage =
|
|
317
|
-
err instanceof Error ? err.message : 'Unknown error'
|
|
318
|
-
setResult({
|
|
319
|
-
success: false,
|
|
320
|
-
message: errorMessage,
|
|
321
|
-
})
|
|
322
|
-
} finally {
|
|
323
|
-
setIsLoading(false)
|
|
324
|
-
}
|
|
325
|
-
}, [action, args, executeTool, runtime])
|
|
326
|
-
|
|
327
|
-
const variantClasses: Record<string, string> = {
|
|
328
|
-
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
|
|
329
|
-
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
|
|
330
|
-
outline:
|
|
331
|
-
'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
|
|
332
|
-
destructive:
|
|
333
|
-
'bg-destructive text-destructive-foreground hover:bg-destructive/90',
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
// Show result state if we have one
|
|
337
|
-
if (result) {
|
|
338
|
-
return (
|
|
339
|
-
<div
|
|
340
|
-
className={cn(
|
|
341
|
-
'inline-flex items-center gap-2 px-4 py-2 text-sm',
|
|
342
|
-
r('md'),
|
|
343
|
-
result.success
|
|
344
|
-
? 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-100'
|
|
345
|
-
: 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-100',
|
|
346
|
-
className as string
|
|
347
|
-
)}
|
|
348
|
-
>
|
|
349
|
-
{result.success ? (
|
|
350
|
-
<CheckCircleIcon className="size-4" />
|
|
351
|
-
) : (
|
|
352
|
-
<XCircleIcon className="size-4" />
|
|
353
|
-
)}
|
|
354
|
-
<span className="max-w-[200px] truncate">
|
|
355
|
-
{result.message ?? (result.success ? 'Success' : 'Failed')}
|
|
356
|
-
</span>
|
|
357
|
-
</div>
|
|
358
|
-
)
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
return (
|
|
362
|
-
<button
|
|
363
|
-
onClick={handleClick}
|
|
364
|
-
disabled={isLoading || !toolAvailable}
|
|
365
|
-
title={!toolAvailable ? `Tool "${action}" not available` : undefined}
|
|
366
|
-
className={cn(
|
|
367
|
-
'inline-flex items-center justify-center gap-2 px-4 py-2 text-sm font-medium transition-colors',
|
|
368
|
-
'disabled:pointer-events-none disabled:opacity-50',
|
|
369
|
-
r('md'),
|
|
370
|
-
variantClasses[variant as string] ?? variantClasses.default,
|
|
371
|
-
className as string
|
|
372
|
-
)}
|
|
373
|
-
>
|
|
374
|
-
{isLoading && <Loader2Icon className="size-4 animate-spin" />}
|
|
375
|
-
{String(label)}
|
|
376
|
-
</button>
|
|
377
|
-
)
|
|
378
|
-
},
|
|
46
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
47
|
+
const components: Record<string, FC<any>> = {
|
|
48
|
+
// Layout
|
|
49
|
+
Card: CardWrapper,
|
|
50
|
+
Grid,
|
|
51
|
+
Stack,
|
|
52
|
+
// Typography
|
|
53
|
+
Text,
|
|
54
|
+
// Data Display
|
|
55
|
+
Metric,
|
|
56
|
+
Badge,
|
|
57
|
+
Table: DataTable,
|
|
58
|
+
List,
|
|
59
|
+
Progress,
|
|
60
|
+
Avatar: AvatarWrapper,
|
|
61
|
+
Skeleton: SkeletonWrapper,
|
|
62
|
+
// Feedback
|
|
63
|
+
Alert: AlertWrapper,
|
|
64
|
+
// Structure
|
|
65
|
+
Divider: Separator,
|
|
66
|
+
Separator,
|
|
67
|
+
// Interactive
|
|
68
|
+
Accordion: AccordionWrapper,
|
|
69
|
+
AccordionItem: AccordionItemWrapper,
|
|
70
|
+
Tabs: TabsWrapper,
|
|
71
|
+
TabContent: TabContentWrapper,
|
|
72
|
+
// Actions
|
|
73
|
+
Button: ButtonWrapper,
|
|
74
|
+
ActionButton,
|
|
75
|
+
// Form Elements
|
|
76
|
+
Input: InputWrapper,
|
|
77
|
+
Checkbox: CheckboxWrapper,
|
|
78
|
+
Select: SelectWrapper,
|
|
379
79
|
}
|
|
380
80
|
|
|
381
81
|
/**
|
|
@@ -7,14 +7,17 @@ import { ToolUI } from './tool-ui'
|
|
|
7
7
|
// However, to use this design variant, we'd have to add lots of metadata to the tool parts
|
|
8
8
|
|
|
9
9
|
const meta: Meta<typeof ToolUI> = {
|
|
10
|
+
title: 'Components/Tool UI',
|
|
10
11
|
component: ToolUI,
|
|
11
12
|
parameters: {
|
|
12
|
-
layout: '
|
|
13
|
+
layout: 'fullscreen',
|
|
13
14
|
},
|
|
14
15
|
decorators: [
|
|
15
16
|
(Story) => (
|
|
16
|
-
<div className="
|
|
17
|
-
<
|
|
17
|
+
<div className="bg-background text-foreground flex min-h-screen items-start justify-center p-6">
|
|
18
|
+
<div className="w-[400px]">
|
|
19
|
+
<Story />
|
|
20
|
+
</div>
|
|
18
21
|
</div>
|
|
19
22
|
),
|
|
20
23
|
],
|
package/src/hooks/useAuth.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useReplayContext } from '@/contexts/ReplayContext'
|
|
1
2
|
import { hasExplicitSessionAuth, isStaticSessionAuth } from '@/lib/auth'
|
|
2
3
|
import { useMemo } from 'react'
|
|
3
4
|
import { ApiConfig } from '../types'
|
|
@@ -40,14 +41,21 @@ export const useAuth = ({
|
|
|
40
41
|
auth?: ApiConfig
|
|
41
42
|
projectSlug: string
|
|
42
43
|
}): Auth => {
|
|
44
|
+
const replayCtx = useReplayContext()
|
|
45
|
+
const isReplay = replayCtx?.isReplay ?? false
|
|
46
|
+
|
|
43
47
|
const getSession = useMemo(() => {
|
|
48
|
+
// In replay mode, skip session fetching entirely
|
|
49
|
+
if (isReplay) {
|
|
50
|
+
return null
|
|
51
|
+
}
|
|
44
52
|
if (isStaticSessionAuth(auth)) {
|
|
45
53
|
return () => Promise.resolve(auth.sessionToken)
|
|
46
54
|
}
|
|
47
55
|
return !isStaticSessionAuth(auth) && hasExplicitSessionAuth(auth)
|
|
48
56
|
? auth.sessionFn
|
|
49
57
|
: defaultGetSession
|
|
50
|
-
}, [auth])
|
|
58
|
+
}, [auth, isReplay])
|
|
51
59
|
|
|
52
60
|
// The session request is only neccessary if we are not using static session auth
|
|
53
61
|
// configuration. If a custom session fetcher is provided, we use it,
|
|
@@ -57,6 +65,14 @@ export const useAuth = ({
|
|
|
57
65
|
projectSlug,
|
|
58
66
|
})
|
|
59
67
|
|
|
68
|
+
// In replay mode, return immediately without waiting for session
|
|
69
|
+
if (isReplay) {
|
|
70
|
+
return {
|
|
71
|
+
headers: {},
|
|
72
|
+
isLoading: false,
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
60
76
|
return !session
|
|
61
77
|
? {
|
|
62
78
|
isLoading: true,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useReplayContext } from '@/contexts/ReplayContext'
|
|
1
2
|
import { useAssistantState } from '@assistant-ui/react'
|
|
2
3
|
import { useCallback, useEffect, useRef, useState } from 'react'
|
|
3
4
|
import { useElements } from './useElements'
|
|
@@ -37,13 +38,17 @@ export function useFollowOnSuggestions(): {
|
|
|
37
38
|
isLoading: boolean
|
|
38
39
|
} {
|
|
39
40
|
const { config } = useElements()
|
|
41
|
+
const replayCtx = useReplayContext()
|
|
42
|
+
const isReplay = replayCtx?.isReplay ?? false
|
|
43
|
+
|
|
40
44
|
const auth = useAuth({
|
|
41
45
|
auth: config.api,
|
|
42
46
|
projectSlug: config.projectSlug,
|
|
43
47
|
})
|
|
44
48
|
|
|
45
49
|
// Check if follow-up suggestions are enabled (default: true)
|
|
46
|
-
|
|
50
|
+
// Disable in replay mode since we don't need AI-generated suggestions
|
|
51
|
+
const isEnabled = !isReplay && config.thread?.followUpSuggestions !== false
|
|
47
52
|
|
|
48
53
|
const [suggestions, setSuggestions] = useState<FollowOnSuggestion[]>([])
|
|
49
54
|
const [isLoading, setIsLoading] = useState(false)
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { createCatalog } from '@json-render/core'
|
|
2
|
+
import { z } from 'zod'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Data point schema - common structure for all chart types
|
|
6
|
+
*/
|
|
7
|
+
const dataPointSchema = z.object({
|
|
8
|
+
label: z.string(),
|
|
9
|
+
value: z.number(),
|
|
10
|
+
color: z.string().optional(),
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Multi-series data point for line/area charts
|
|
15
|
+
*/
|
|
16
|
+
const seriesDataPointSchema = z
|
|
17
|
+
.object({
|
|
18
|
+
label: z.string(),
|
|
19
|
+
})
|
|
20
|
+
.catchall(z.number())
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Chart Catalog
|
|
24
|
+
*
|
|
25
|
+
* Defines all available chart components for LLM-generated visualizations.
|
|
26
|
+
* Uses Recharts under the hood for rendering.
|
|
27
|
+
*/
|
|
28
|
+
export const chartCatalog = createCatalog({
|
|
29
|
+
name: 'chart',
|
|
30
|
+
components: {
|
|
31
|
+
BarChart: {
|
|
32
|
+
props: z.object({
|
|
33
|
+
title: z.string().optional(),
|
|
34
|
+
data: z.array(dataPointSchema),
|
|
35
|
+
layout: z.enum(['vertical', 'horizontal']).optional(),
|
|
36
|
+
showGrid: z.boolean().optional(),
|
|
37
|
+
showLegend: z.boolean().optional(),
|
|
38
|
+
className: z.string().optional(),
|
|
39
|
+
}),
|
|
40
|
+
description:
|
|
41
|
+
'Bar chart for comparing categorical data. Use vertical for few categories, horizontal for many or long labels.',
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
LineChart: {
|
|
45
|
+
props: z.object({
|
|
46
|
+
title: z.string().optional(),
|
|
47
|
+
data: z.array(seriesDataPointSchema),
|
|
48
|
+
series: z.array(z.string()).optional(),
|
|
49
|
+
showGrid: z.boolean().optional(),
|
|
50
|
+
showLegend: z.boolean().optional(),
|
|
51
|
+
showDots: z.boolean().optional(),
|
|
52
|
+
curved: z.boolean().optional(),
|
|
53
|
+
className: z.string().optional(),
|
|
54
|
+
}),
|
|
55
|
+
description:
|
|
56
|
+
'Line chart for showing trends over time or continuous data. Supports multiple series.',
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
AreaChart: {
|
|
60
|
+
props: z.object({
|
|
61
|
+
title: z.string().optional(),
|
|
62
|
+
data: z.array(seriesDataPointSchema),
|
|
63
|
+
series: z.array(z.string()).optional(),
|
|
64
|
+
stacked: z.boolean().optional(),
|
|
65
|
+
showGrid: z.boolean().optional(),
|
|
66
|
+
showLegend: z.boolean().optional(),
|
|
67
|
+
className: z.string().optional(),
|
|
68
|
+
}),
|
|
69
|
+
description:
|
|
70
|
+
'Area chart for showing volume/magnitude over time. Use stacked for part-to-whole relationships.',
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
PieChart: {
|
|
74
|
+
props: z.object({
|
|
75
|
+
title: z.string().optional(),
|
|
76
|
+
data: z.array(dataPointSchema),
|
|
77
|
+
showLabels: z.boolean().optional(),
|
|
78
|
+
showLegend: z.boolean().optional(),
|
|
79
|
+
className: z.string().optional(),
|
|
80
|
+
}),
|
|
81
|
+
description:
|
|
82
|
+
'Pie chart for showing proportions of a whole. Best for 2-6 categories.',
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
DonutChart: {
|
|
86
|
+
props: z.object({
|
|
87
|
+
title: z.string().optional(),
|
|
88
|
+
data: z.array(dataPointSchema),
|
|
89
|
+
showLabels: z.boolean().optional(),
|
|
90
|
+
showLegend: z.boolean().optional(),
|
|
91
|
+
innerLabel: z.string().optional(),
|
|
92
|
+
innerValue: z.union([z.string(), z.number()]).optional(),
|
|
93
|
+
className: z.string().optional(),
|
|
94
|
+
}),
|
|
95
|
+
description:
|
|
96
|
+
'Donut chart (pie with center hole). Good for showing a key metric in the center.',
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
ScatterChart: {
|
|
100
|
+
props: z.object({
|
|
101
|
+
title: z.string().optional(),
|
|
102
|
+
data: z.array(
|
|
103
|
+
z.object({
|
|
104
|
+
x: z.number(),
|
|
105
|
+
y: z.number(),
|
|
106
|
+
label: z.string().optional(),
|
|
107
|
+
size: z.number().optional(),
|
|
108
|
+
color: z.string().optional(),
|
|
109
|
+
})
|
|
110
|
+
),
|
|
111
|
+
xLabel: z.string().optional(),
|
|
112
|
+
yLabel: z.string().optional(),
|
|
113
|
+
showGrid: z.boolean().optional(),
|
|
114
|
+
className: z.string().optional(),
|
|
115
|
+
}),
|
|
116
|
+
description:
|
|
117
|
+
'Scatter plot for showing correlation between two variables.',
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
RadarChart: {
|
|
121
|
+
props: z.object({
|
|
122
|
+
title: z.string().optional(),
|
|
123
|
+
data: z.array(dataPointSchema),
|
|
124
|
+
showLegend: z.boolean().optional(),
|
|
125
|
+
className: z.string().optional(),
|
|
126
|
+
}),
|
|
127
|
+
description:
|
|
128
|
+
'Radar/spider chart for comparing multiple attributes. Best for 3-8 dimensions.',
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
export type ChartCatalogComponentProps = typeof chartCatalog extends {
|
|
134
|
+
components: infer C
|
|
135
|
+
}
|
|
136
|
+
? {
|
|
137
|
+
[K in keyof C]: C[K] extends { props: infer P }
|
|
138
|
+
? z.infer<P extends z.ZodType ? P : never>
|
|
139
|
+
: never
|
|
140
|
+
}
|
|
141
|
+
: never
|