@cloudbase/agent-react-ui 0.0.23

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 (109) hide show
  1. package/README.md +135 -0
  2. package/components.json +21 -0
  3. package/dist/index.css +4241 -0
  4. package/dist/index.css.map +1 -0
  5. package/dist/index.d.mts +59 -0
  6. package/dist/index.d.ts +59 -0
  7. package/dist/index.js +2169 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/index.mjs +2182 -0
  10. package/dist/index.mjs.map +1 -0
  11. package/example/.env.sample +2 -0
  12. package/example/App.tsx +368 -0
  13. package/example/app.css +1 -0
  14. package/example/index.html +12 -0
  15. package/example/main.tsx +9 -0
  16. package/example/vite.config.ts +34 -0
  17. package/package.json +75 -0
  18. package/postcss.config.cjs +3 -0
  19. package/src/components/ai-elements/agent.tsx +140 -0
  20. package/src/components/ai-elements/artifact.tsx +147 -0
  21. package/src/components/ai-elements/attachments.tsx +421 -0
  22. package/src/components/ai-elements/audio-player.tsx +228 -0
  23. package/src/components/ai-elements/canvas.tsx +22 -0
  24. package/src/components/ai-elements/chain-of-thought.tsx +228 -0
  25. package/src/components/ai-elements/checkpoint.tsx +71 -0
  26. package/src/components/ai-elements/code-block.tsx +532 -0
  27. package/src/components/ai-elements/commit.tsx +448 -0
  28. package/src/components/ai-elements/confirmation.tsx +176 -0
  29. package/src/components/ai-elements/connection.tsx +28 -0
  30. package/src/components/ai-elements/context.tsx +408 -0
  31. package/src/components/ai-elements/controls.tsx +18 -0
  32. package/src/components/ai-elements/conversation.tsx +100 -0
  33. package/src/components/ai-elements/edge.tsx +140 -0
  34. package/src/components/ai-elements/environment-variables.tsx +295 -0
  35. package/src/components/ai-elements/file-tree.tsx +258 -0
  36. package/src/components/ai-elements/image.tsx +24 -0
  37. package/src/components/ai-elements/inline-citation.tsx +287 -0
  38. package/src/components/ai-elements/message.tsx +336 -0
  39. package/src/components/ai-elements/mic-selector.tsx +370 -0
  40. package/src/components/ai-elements/model-selector.tsx +211 -0
  41. package/src/components/ai-elements/node.tsx +71 -0
  42. package/src/components/ai-elements/open-in-chat.tsx +365 -0
  43. package/src/components/ai-elements/package-info.tsx +233 -0
  44. package/src/components/ai-elements/panel.tsx +15 -0
  45. package/src/components/ai-elements/persona.tsx +270 -0
  46. package/src/components/ai-elements/plan.tsx +142 -0
  47. package/src/components/ai-elements/prompt-input.tsx +1263 -0
  48. package/src/components/ai-elements/queue.tsx +274 -0
  49. package/src/components/ai-elements/reasoning.tsx +193 -0
  50. package/src/components/ai-elements/sandbox.tsx +126 -0
  51. package/src/components/ai-elements/schema-display.tsx +458 -0
  52. package/src/components/ai-elements/shimmer.tsx +64 -0
  53. package/src/components/ai-elements/snippet.tsx +139 -0
  54. package/src/components/ai-elements/sources.tsx +77 -0
  55. package/src/components/ai-elements/speech-input.tsx +301 -0
  56. package/src/components/ai-elements/stack-trace.tsx +482 -0
  57. package/src/components/ai-elements/suggestion.tsx +53 -0
  58. package/src/components/ai-elements/task.tsx +87 -0
  59. package/src/components/ai-elements/terminal.tsx +261 -0
  60. package/src/components/ai-elements/test-results.tsx +485 -0
  61. package/src/components/ai-elements/tool.tsx +174 -0
  62. package/src/components/ai-elements/toolbar.tsx +16 -0
  63. package/src/components/ai-elements/transcription.tsx +124 -0
  64. package/src/components/ai-elements/voice-selector.tsx +479 -0
  65. package/src/components/ai-elements/web-preview.tsx +263 -0
  66. package/src/components/chat/Chat.tsx +178 -0
  67. package/src/components/chat/Input.tsx +98 -0
  68. package/src/components/chat/Message.tsx +276 -0
  69. package/src/components/chat/index.ts +2 -0
  70. package/src/components/index.ts +1 -0
  71. package/src/components/ui/accordion.tsx +64 -0
  72. package/src/components/ui/alert.tsx +66 -0
  73. package/src/components/ui/avatar.tsx +107 -0
  74. package/src/components/ui/badge.tsx +48 -0
  75. package/src/components/ui/button-group.tsx +83 -0
  76. package/src/components/ui/button.tsx +64 -0
  77. package/src/components/ui/card.tsx +92 -0
  78. package/src/components/ui/carousel.tsx +239 -0
  79. package/src/components/ui/collapsible.tsx +31 -0
  80. package/src/components/ui/command.tsx +184 -0
  81. package/src/components/ui/dialog.tsx +158 -0
  82. package/src/components/ui/dropdown-menu.tsx +257 -0
  83. package/src/components/ui/hover-card.tsx +42 -0
  84. package/src/components/ui/input-group.tsx +168 -0
  85. package/src/components/ui/input.tsx +21 -0
  86. package/src/components/ui/popover.tsx +87 -0
  87. package/src/components/ui/progress.tsx +31 -0
  88. package/src/components/ui/scroll-area.tsx +56 -0
  89. package/src/components/ui/select.tsx +190 -0
  90. package/src/components/ui/separator.tsx +28 -0
  91. package/src/components/ui/spinner.tsx +16 -0
  92. package/src/components/ui/switch.tsx +33 -0
  93. package/src/components/ui/tabs.tsx +91 -0
  94. package/src/components/ui/textarea.tsx +18 -0
  95. package/src/components/ui/tooltip.tsx +61 -0
  96. package/src/css/global.css +123 -0
  97. package/src/css/index.css +1 -0
  98. package/src/hooks/index.ts +1 -0
  99. package/src/hooks/use-copy-to-clipboard.ts +31 -0
  100. package/src/index.ts +4 -0
  101. package/src/lib/utils.ts +6 -0
  102. package/src/locales/context.ts +8 -0
  103. package/src/locales/hooks.ts +20 -0
  104. package/src/locales/index.ts +3 -0
  105. package/src/locales/langs/en.ts +17 -0
  106. package/src/locales/langs/index.ts +12 -0
  107. package/src/locales/langs/zh-cn.ts +18 -0
  108. package/tsconfig.json +21 -0
  109. package/tsup.config.ts +21 -0
@@ -0,0 +1,295 @@
1
+ "use client";
2
+
3
+ import { Badge } from "@/components/ui/badge";
4
+ import { Button } from "@/components/ui/button";
5
+ import { Switch } from "@/components/ui/switch";
6
+ import { cn } from "@/lib/utils";
7
+ import { CheckIcon, CopyIcon, EyeIcon, EyeOffIcon } from "lucide-react";
8
+ import {
9
+ type ComponentProps,
10
+ createContext,
11
+ type HTMLAttributes,
12
+ useContext,
13
+ useState,
14
+ } from "react";
15
+
16
+ interface EnvironmentVariablesContextType {
17
+ showValues: boolean;
18
+ setShowValues: (show: boolean) => void;
19
+ }
20
+
21
+ const EnvironmentVariablesContext =
22
+ createContext<EnvironmentVariablesContextType>({
23
+ showValues: false,
24
+ setShowValues: () => undefined,
25
+ });
26
+
27
+ export type EnvironmentVariablesProps = HTMLAttributes<HTMLDivElement> & {
28
+ showValues?: boolean;
29
+ defaultShowValues?: boolean;
30
+ onShowValuesChange?: (show: boolean) => void;
31
+ };
32
+
33
+ export const EnvironmentVariables = ({
34
+ showValues: controlledShowValues,
35
+ defaultShowValues = false,
36
+ onShowValuesChange,
37
+ className,
38
+ children,
39
+ ...props
40
+ }: EnvironmentVariablesProps) => {
41
+ const [internalShowValues, setInternalShowValues] =
42
+ useState(defaultShowValues);
43
+ const showValues = controlledShowValues ?? internalShowValues;
44
+
45
+ const setShowValues = (show: boolean) => {
46
+ setInternalShowValues(show);
47
+ onShowValuesChange?.(show);
48
+ };
49
+
50
+ return (
51
+ <EnvironmentVariablesContext.Provider value={{ showValues, setShowValues }}>
52
+ <div
53
+ className={cn("rounded-lg border bg-background", className)}
54
+ {...props}
55
+ >
56
+ {children}
57
+ </div>
58
+ </EnvironmentVariablesContext.Provider>
59
+ );
60
+ };
61
+
62
+ export type EnvironmentVariablesHeaderProps = HTMLAttributes<HTMLDivElement>;
63
+
64
+ export const EnvironmentVariablesHeader = ({
65
+ className,
66
+ children,
67
+ ...props
68
+ }: EnvironmentVariablesHeaderProps) => (
69
+ <div
70
+ className={cn(
71
+ "flex items-center justify-between border-b px-4 py-3",
72
+ className
73
+ )}
74
+ {...props}
75
+ >
76
+ {children}
77
+ </div>
78
+ );
79
+
80
+ export type EnvironmentVariablesTitleProps = HTMLAttributes<HTMLHeadingElement>;
81
+
82
+ export const EnvironmentVariablesTitle = ({
83
+ className,
84
+ children,
85
+ ...props
86
+ }: EnvironmentVariablesTitleProps) => (
87
+ <h3 className={cn("font-medium text-sm", className)} {...props}>
88
+ {children ?? "Environment Variables"}
89
+ </h3>
90
+ );
91
+
92
+ export type EnvironmentVariablesToggleProps = ComponentProps<typeof Switch>;
93
+
94
+ export const EnvironmentVariablesToggle = ({
95
+ className,
96
+ ...props
97
+ }: EnvironmentVariablesToggleProps) => {
98
+ const { showValues, setShowValues } = useContext(EnvironmentVariablesContext);
99
+
100
+ return (
101
+ <div className={cn("flex items-center gap-2", className)}>
102
+ <span className="text-muted-foreground text-xs">
103
+ {showValues ? <EyeIcon size={14} /> : <EyeOffIcon size={14} />}
104
+ </span>
105
+ <Switch
106
+ aria-label="Toggle value visibility"
107
+ checked={showValues}
108
+ onCheckedChange={setShowValues}
109
+ {...props}
110
+ />
111
+ </div>
112
+ );
113
+ };
114
+
115
+ export type EnvironmentVariablesContentProps = HTMLAttributes<HTMLDivElement>;
116
+
117
+ export const EnvironmentVariablesContent = ({
118
+ className,
119
+ children,
120
+ ...props
121
+ }: EnvironmentVariablesContentProps) => (
122
+ <div className={cn("divide-y", className)} {...props}>
123
+ {children}
124
+ </div>
125
+ );
126
+
127
+ interface EnvironmentVariableContextType {
128
+ name: string;
129
+ value: string;
130
+ }
131
+
132
+ const EnvironmentVariableContext =
133
+ createContext<EnvironmentVariableContextType>({
134
+ name: "",
135
+ value: "",
136
+ });
137
+
138
+ export type EnvironmentVariableProps = HTMLAttributes<HTMLDivElement> & {
139
+ name: string;
140
+ value: string;
141
+ };
142
+
143
+ export const EnvironmentVariable = ({
144
+ name,
145
+ value,
146
+ className,
147
+ children,
148
+ ...props
149
+ }: EnvironmentVariableProps) => (
150
+ <EnvironmentVariableContext.Provider value={{ name, value }}>
151
+ <div
152
+ className={cn(
153
+ "flex items-center justify-between gap-4 px-4 py-3",
154
+ className
155
+ )}
156
+ {...props}
157
+ >
158
+ {children ?? (
159
+ <>
160
+ <div className="flex items-center gap-2">
161
+ <EnvironmentVariableName />
162
+ </div>
163
+ <EnvironmentVariableValue />
164
+ </>
165
+ )}
166
+ </div>
167
+ </EnvironmentVariableContext.Provider>
168
+ );
169
+
170
+ export type EnvironmentVariableGroupProps = HTMLAttributes<HTMLDivElement>;
171
+
172
+ export const EnvironmentVariableGroup = ({
173
+ className,
174
+ children,
175
+ ...props
176
+ }: EnvironmentVariableGroupProps) => (
177
+ <div className={cn("flex items-center gap-2", className)} {...props}>
178
+ {children}
179
+ </div>
180
+ );
181
+
182
+ export type EnvironmentVariableNameProps = HTMLAttributes<HTMLSpanElement>;
183
+
184
+ export const EnvironmentVariableName = ({
185
+ className,
186
+ children,
187
+ ...props
188
+ }: EnvironmentVariableNameProps) => {
189
+ const { name } = useContext(EnvironmentVariableContext);
190
+
191
+ return (
192
+ <span className={cn("font-mono text-sm", className)} {...props}>
193
+ {children ?? name}
194
+ </span>
195
+ );
196
+ };
197
+
198
+ export type EnvironmentVariableValueProps = HTMLAttributes<HTMLSpanElement>;
199
+
200
+ export const EnvironmentVariableValue = ({
201
+ className,
202
+ children,
203
+ ...props
204
+ }: EnvironmentVariableValueProps) => {
205
+ const { value } = useContext(EnvironmentVariableContext);
206
+ const { showValues } = useContext(EnvironmentVariablesContext);
207
+
208
+ const displayValue = showValues
209
+ ? value
210
+ : "•".repeat(Math.min(value.length, 20));
211
+
212
+ return (
213
+ <span
214
+ className={cn(
215
+ "font-mono text-muted-foreground text-sm",
216
+ !showValues && "select-none",
217
+ className
218
+ )}
219
+ {...props}
220
+ >
221
+ {children ?? displayValue}
222
+ </span>
223
+ );
224
+ };
225
+
226
+ export type EnvironmentVariableCopyButtonProps = ComponentProps<
227
+ typeof Button
228
+ > & {
229
+ onCopy?: () => void;
230
+ onError?: (error: Error) => void;
231
+ timeout?: number;
232
+ copyFormat?: "name" | "value" | "export";
233
+ };
234
+
235
+ export const EnvironmentVariableCopyButton = ({
236
+ onCopy,
237
+ onError,
238
+ timeout = 2000,
239
+ copyFormat = "value",
240
+ children,
241
+ className,
242
+ ...props
243
+ }: EnvironmentVariableCopyButtonProps) => {
244
+ const [isCopied, setIsCopied] = useState(false);
245
+ const { name, value } = useContext(EnvironmentVariableContext);
246
+
247
+ const copyToClipboard = async () => {
248
+ if (typeof window === "undefined" || !navigator?.clipboard?.writeText) {
249
+ onError?.(new Error("Clipboard API not available"));
250
+ return;
251
+ }
252
+
253
+ let textToCopy = value;
254
+ if (copyFormat === "name") {
255
+ textToCopy = name;
256
+ } else if (copyFormat === "export") {
257
+ textToCopy = `export ${name}="${value}"`;
258
+ }
259
+
260
+ try {
261
+ await navigator.clipboard.writeText(textToCopy);
262
+ setIsCopied(true);
263
+ onCopy?.();
264
+ setTimeout(() => setIsCopied(false), timeout);
265
+ } catch (error) {
266
+ onError?.(error as Error);
267
+ }
268
+ };
269
+
270
+ const Icon = isCopied ? CheckIcon : CopyIcon;
271
+
272
+ return (
273
+ <Button
274
+ className={cn("size-6 shrink-0", className)}
275
+ onClick={copyToClipboard}
276
+ size="icon"
277
+ variant="ghost"
278
+ {...props}
279
+ >
280
+ {children ?? <Icon size={12} />}
281
+ </Button>
282
+ );
283
+ };
284
+
285
+ export type EnvironmentVariableRequiredProps = ComponentProps<typeof Badge>;
286
+
287
+ export const EnvironmentVariableRequired = ({
288
+ className,
289
+ children,
290
+ ...props
291
+ }: EnvironmentVariableRequiredProps) => (
292
+ <Badge className={cn("text-xs", className)} variant="secondary" {...props}>
293
+ {children ?? "Required"}
294
+ </Badge>
295
+ );
@@ -0,0 +1,258 @@
1
+ "use client";
2
+
3
+ import {
4
+ Collapsible,
5
+ CollapsibleContent,
6
+ CollapsibleTrigger,
7
+ } from "@/components/ui/collapsible";
8
+ import { cn } from "@/lib/utils";
9
+ import {
10
+ ChevronRightIcon,
11
+ FileIcon,
12
+ FolderIcon,
13
+ FolderOpenIcon,
14
+ } from "lucide-react";
15
+ import {
16
+ createContext,
17
+ type HTMLAttributes,
18
+ type ReactNode,
19
+ useContext,
20
+ useState,
21
+ } from "react";
22
+
23
+ interface FileTreeContextType {
24
+ expandedPaths: Set<string>;
25
+ togglePath: (path: string) => void;
26
+ selectedPath?: string;
27
+ onSelect?: (path: string) => void;
28
+ }
29
+
30
+ const FileTreeContext = createContext<FileTreeContextType>({
31
+ expandedPaths: new Set(),
32
+ togglePath: () => undefined,
33
+ });
34
+
35
+ export type FileTreeProps = HTMLAttributes<HTMLDivElement> & {
36
+ expanded?: Set<string>;
37
+ defaultExpanded?: Set<string>;
38
+ selectedPath?: string;
39
+ onSelect?: (path: string) => void;
40
+ onExpandedChange?: (expanded: Set<string>) => void;
41
+ };
42
+
43
+ export const FileTree = ({
44
+ expanded: controlledExpanded,
45
+ defaultExpanded = new Set(),
46
+ selectedPath,
47
+ onSelect,
48
+ onExpandedChange,
49
+ className,
50
+ children,
51
+ ...props
52
+ }: FileTreeProps) => {
53
+ const [internalExpanded, setInternalExpanded] = useState(defaultExpanded);
54
+ const expandedPaths = controlledExpanded ?? internalExpanded;
55
+
56
+ const togglePath = (path: string) => {
57
+ const newExpanded = new Set(expandedPaths);
58
+ if (newExpanded.has(path)) {
59
+ newExpanded.delete(path);
60
+ } else {
61
+ newExpanded.add(path);
62
+ }
63
+ setInternalExpanded(newExpanded);
64
+ onExpandedChange?.(newExpanded);
65
+ };
66
+
67
+ return (
68
+ <FileTreeContext.Provider
69
+ value={{ expandedPaths, togglePath, selectedPath, onSelect }}
70
+ >
71
+ <div
72
+ className={cn(
73
+ "rounded-lg border bg-background font-mono text-sm",
74
+ className
75
+ )}
76
+ role="tree"
77
+ {...props}
78
+ >
79
+ <div className="p-2">{children}</div>
80
+ </div>
81
+ </FileTreeContext.Provider>
82
+ );
83
+ };
84
+
85
+ interface FileTreeFolderContextType {
86
+ path: string;
87
+ name: string;
88
+ isExpanded: boolean;
89
+ }
90
+
91
+ const FileTreeFolderContext = createContext<FileTreeFolderContextType>({
92
+ path: "",
93
+ name: "",
94
+ isExpanded: false,
95
+ });
96
+
97
+ export type FileTreeFolderProps = HTMLAttributes<HTMLDivElement> & {
98
+ path: string;
99
+ name: string;
100
+ };
101
+
102
+ export const FileTreeFolder = ({
103
+ path,
104
+ name,
105
+ className,
106
+ children,
107
+ ...props
108
+ }: FileTreeFolderProps) => {
109
+ const { expandedPaths, togglePath, selectedPath, onSelect } =
110
+ useContext(FileTreeContext);
111
+ const isExpanded = expandedPaths.has(path);
112
+ const isSelected = selectedPath === path;
113
+
114
+ return (
115
+ <FileTreeFolderContext.Provider value={{ path, name, isExpanded }}>
116
+ <Collapsible onOpenChange={() => togglePath(path)} open={isExpanded}>
117
+ <div
118
+ className={cn("", className)}
119
+ role="treeitem"
120
+ tabIndex={0}
121
+ {...props}
122
+ >
123
+ <CollapsibleTrigger asChild>
124
+ <button
125
+ className={cn(
126
+ "flex w-full items-center gap-1 rounded px-2 py-1 text-left transition-colors hover:bg-muted/50",
127
+ isSelected && "bg-muted"
128
+ )}
129
+ onClick={() => onSelect?.(path)}
130
+ type="button"
131
+ >
132
+ <ChevronRightIcon
133
+ className={cn(
134
+ "size-4 shrink-0 text-muted-foreground transition-transform",
135
+ isExpanded && "rotate-90"
136
+ )}
137
+ />
138
+ <FileTreeIcon>
139
+ {isExpanded ? (
140
+ <FolderOpenIcon className="size-4 text-blue-500" />
141
+ ) : (
142
+ <FolderIcon className="size-4 text-blue-500" />
143
+ )}
144
+ </FileTreeIcon>
145
+ <FileTreeName>{name}</FileTreeName>
146
+ </button>
147
+ </CollapsibleTrigger>
148
+ <CollapsibleContent>
149
+ <div className="ml-4 border-l pl-2">{children}</div>
150
+ </CollapsibleContent>
151
+ </div>
152
+ </Collapsible>
153
+ </FileTreeFolderContext.Provider>
154
+ );
155
+ };
156
+
157
+ interface FileTreeFileContextType {
158
+ path: string;
159
+ name: string;
160
+ }
161
+
162
+ const FileTreeFileContext = createContext<FileTreeFileContextType>({
163
+ path: "",
164
+ name: "",
165
+ });
166
+
167
+ export type FileTreeFileProps = HTMLAttributes<HTMLDivElement> & {
168
+ path: string;
169
+ name: string;
170
+ icon?: ReactNode;
171
+ };
172
+
173
+ export const FileTreeFile = ({
174
+ path,
175
+ name,
176
+ icon,
177
+ className,
178
+ children,
179
+ ...props
180
+ }: FileTreeFileProps) => {
181
+ const { selectedPath, onSelect } = useContext(FileTreeContext);
182
+ const isSelected = selectedPath === path;
183
+
184
+ return (
185
+ <FileTreeFileContext.Provider value={{ path, name }}>
186
+ <div
187
+ className={cn(
188
+ "flex cursor-pointer items-center gap-1 rounded px-2 py-1 transition-colors hover:bg-muted/50",
189
+ isSelected && "bg-muted",
190
+ className
191
+ )}
192
+ onClick={() => onSelect?.(path)}
193
+ onKeyDown={(e) => {
194
+ if (e.key === "Enter" || e.key === " ") {
195
+ onSelect?.(path);
196
+ }
197
+ }}
198
+ role="treeitem"
199
+ tabIndex={0}
200
+ {...props}
201
+ >
202
+ {children ?? (
203
+ <>
204
+ <span className="size-4" /> {/* Spacer for alignment */}
205
+ <FileTreeIcon>
206
+ {icon ?? <FileIcon className="size-4 text-muted-foreground" />}
207
+ </FileTreeIcon>
208
+ <FileTreeName>{name}</FileTreeName>
209
+ </>
210
+ )}
211
+ </div>
212
+ </FileTreeFileContext.Provider>
213
+ );
214
+ };
215
+
216
+ export type FileTreeIconProps = HTMLAttributes<HTMLSpanElement>;
217
+
218
+ export const FileTreeIcon = ({
219
+ className,
220
+ children,
221
+ ...props
222
+ }: FileTreeIconProps) => (
223
+ <span className={cn("shrink-0", className)} {...props}>
224
+ {children}
225
+ </span>
226
+ );
227
+
228
+ export type FileTreeNameProps = HTMLAttributes<HTMLSpanElement>;
229
+
230
+ export const FileTreeName = ({
231
+ className,
232
+ children,
233
+ ...props
234
+ }: FileTreeNameProps) => (
235
+ <span className={cn("truncate", className)} {...props}>
236
+ {children}
237
+ </span>
238
+ );
239
+
240
+ export type FileTreeActionsProps = HTMLAttributes<HTMLDivElement>;
241
+
242
+ export const FileTreeActions = ({
243
+ className,
244
+ children,
245
+ ...props
246
+ }: FileTreeActionsProps) => (
247
+ // biome-ignore lint/a11y/noNoninteractiveElementInteractions: stopPropagation required for nested interactions
248
+ // biome-ignore lint/a11y/useSemanticElements: fieldset doesn't fit this UI pattern
249
+ <div
250
+ className={cn("ml-auto flex items-center gap-1", className)}
251
+ onClick={(e) => e.stopPropagation()}
252
+ onKeyDown={(e) => e.stopPropagation()}
253
+ role="group"
254
+ {...props}
255
+ >
256
+ {children}
257
+ </div>
258
+ );
@@ -0,0 +1,24 @@
1
+ import { cn } from "@/lib/utils";
2
+ import type { Experimental_GeneratedImage } from "ai";
3
+
4
+ export type ImageProps = Experimental_GeneratedImage & {
5
+ className?: string;
6
+ alt?: string;
7
+ };
8
+
9
+ export const Image = ({
10
+ base64,
11
+ uint8Array,
12
+ mediaType,
13
+ ...props
14
+ }: ImageProps) => (
15
+ <img
16
+ {...props}
17
+ alt={props.alt}
18
+ className={cn(
19
+ "h-auto max-w-full overflow-hidden rounded-md",
20
+ props.className
21
+ )}
22
+ src={`data:${mediaType};base64,${base64}`}
23
+ />
24
+ );