@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,448 @@
1
+ "use client";
2
+
3
+ import { Avatar, AvatarFallback } from "@/components/ui/avatar";
4
+ import { Button } from "@/components/ui/button";
5
+ import {
6
+ Collapsible,
7
+ CollapsibleContent,
8
+ CollapsibleTrigger,
9
+ } from "@/components/ui/collapsible";
10
+ import { cn } from "@/lib/utils";
11
+ import {
12
+ CheckIcon,
13
+ CopyIcon,
14
+ FileIcon,
15
+ GitCommitIcon,
16
+ MinusIcon,
17
+ PlusIcon,
18
+ } from "lucide-react";
19
+ import {
20
+ type ComponentProps,
21
+ type HTMLAttributes,
22
+ useEffect,
23
+ useRef,
24
+ useState,
25
+ } from "react";
26
+
27
+ export type CommitProps = ComponentProps<typeof Collapsible>;
28
+
29
+ export const Commit = ({ className, children, ...props }: CommitProps) => (
30
+ <Collapsible
31
+ className={cn("rounded-lg border bg-background", className)}
32
+ {...props}
33
+ >
34
+ {children}
35
+ </Collapsible>
36
+ );
37
+
38
+ export type CommitHeaderProps = ComponentProps<typeof CollapsibleTrigger>;
39
+
40
+ export const CommitHeader = ({
41
+ className,
42
+ children,
43
+ ...props
44
+ }: CommitHeaderProps) => (
45
+ <CollapsibleTrigger asChild {...props}>
46
+ <div
47
+ className={cn(
48
+ "group flex cursor-pointer items-center justify-between gap-4 p-3 text-left transition-colors hover:opacity-80",
49
+ className
50
+ )}
51
+ >
52
+ {children}
53
+ </div>
54
+ </CollapsibleTrigger>
55
+ );
56
+
57
+ export type CommitHashProps = HTMLAttributes<HTMLSpanElement>;
58
+
59
+ export const CommitHash = ({
60
+ className,
61
+ children,
62
+ ...props
63
+ }: CommitHashProps) => (
64
+ <span className={cn("font-mono text-xs", className)} {...props}>
65
+ <GitCommitIcon className="mr-1 inline-block size-3" />
66
+ {children}
67
+ </span>
68
+ );
69
+
70
+ export type CommitMessageProps = HTMLAttributes<HTMLSpanElement>;
71
+
72
+ export const CommitMessage = ({
73
+ className,
74
+ children,
75
+ ...props
76
+ }: CommitMessageProps) => (
77
+ <span className={cn("font-medium text-sm", className)} {...props}>
78
+ {children}
79
+ </span>
80
+ );
81
+
82
+ export type CommitMetadataProps = HTMLAttributes<HTMLDivElement>;
83
+
84
+ export const CommitMetadata = ({
85
+ className,
86
+ children,
87
+ ...props
88
+ }: CommitMetadataProps) => (
89
+ <div
90
+ className={cn(
91
+ "flex items-center gap-2 text-muted-foreground text-xs",
92
+ className
93
+ )}
94
+ {...props}
95
+ >
96
+ {children}
97
+ </div>
98
+ );
99
+
100
+ export type CommitSeparatorProps = HTMLAttributes<HTMLSpanElement>;
101
+
102
+ export const CommitSeparator = ({
103
+ className,
104
+ children,
105
+ ...props
106
+ }: CommitSeparatorProps) => (
107
+ <span className={className} {...props}>
108
+ {children ?? "•"}
109
+ </span>
110
+ );
111
+
112
+ export type CommitInfoProps = HTMLAttributes<HTMLDivElement>;
113
+
114
+ export const CommitInfo = ({
115
+ className,
116
+ children,
117
+ ...props
118
+ }: CommitInfoProps) => (
119
+ <div className={cn("flex flex-1 flex-col", className)} {...props}>
120
+ {children}
121
+ </div>
122
+ );
123
+
124
+ export type CommitAuthorProps = HTMLAttributes<HTMLDivElement>;
125
+
126
+ export const CommitAuthor = ({
127
+ className,
128
+ children,
129
+ ...props
130
+ }: CommitAuthorProps) => (
131
+ <div className={cn("flex items-center", className)} {...props}>
132
+ {children}
133
+ </div>
134
+ );
135
+
136
+ export type CommitAuthorAvatarProps = ComponentProps<typeof Avatar> & {
137
+ initials: string;
138
+ };
139
+
140
+ export const CommitAuthorAvatar = ({
141
+ initials,
142
+ className,
143
+ ...props
144
+ }: CommitAuthorAvatarProps) => (
145
+ <Avatar className={cn("size-8", className)} {...props}>
146
+ <AvatarFallback className="text-xs">{initials}</AvatarFallback>
147
+ </Avatar>
148
+ );
149
+
150
+ export type CommitTimestampProps = HTMLAttributes<HTMLTimeElement> & {
151
+ date: Date;
152
+ };
153
+
154
+ export const CommitTimestamp = ({
155
+ date,
156
+ className,
157
+ children,
158
+ ...props
159
+ }: CommitTimestampProps) => {
160
+ const formatted = new Intl.RelativeTimeFormat("en", {
161
+ numeric: "auto",
162
+ }).format(
163
+ Math.round((date.getTime() - Date.now()) / (1000 * 60 * 60 * 24)),
164
+ "day"
165
+ );
166
+
167
+ return (
168
+ <time
169
+ className={cn("text-xs", className)}
170
+ dateTime={date.toISOString()}
171
+ {...props}
172
+ >
173
+ {children ?? formatted}
174
+ </time>
175
+ );
176
+ };
177
+
178
+ export type CommitActionsProps = HTMLAttributes<HTMLDivElement>;
179
+
180
+ export const CommitActions = ({
181
+ className,
182
+ children,
183
+ ...props
184
+ }: CommitActionsProps) => (
185
+ // biome-ignore lint/a11y/noNoninteractiveElementInteractions: stopPropagation required for nested interactions
186
+ // biome-ignore lint/a11y/useSemanticElements: fieldset doesn't fit this UI pattern
187
+ <div
188
+ className={cn("flex items-center gap-1", className)}
189
+ onClick={(e) => e.stopPropagation()}
190
+ onKeyDown={(e) => e.stopPropagation()}
191
+ role="group"
192
+ {...props}
193
+ >
194
+ {children}
195
+ </div>
196
+ );
197
+
198
+ export type CommitCopyButtonProps = ComponentProps<typeof Button> & {
199
+ hash: string;
200
+ onCopy?: () => void;
201
+ onError?: (error: Error) => void;
202
+ timeout?: number;
203
+ };
204
+
205
+ export const CommitCopyButton = ({
206
+ hash,
207
+ onCopy,
208
+ onError,
209
+ timeout = 2000,
210
+ children,
211
+ className,
212
+ ...props
213
+ }: CommitCopyButtonProps) => {
214
+ const [isCopied, setIsCopied] = useState(false);
215
+ const timeoutRef = useRef<number>(0);
216
+
217
+ const copyToClipboard = async () => {
218
+ if (typeof window === "undefined" || !navigator?.clipboard?.writeText) {
219
+ onError?.(new Error("Clipboard API not available"));
220
+ return;
221
+ }
222
+
223
+ try {
224
+ if (!isCopied) {
225
+ await navigator.clipboard.writeText(hash);
226
+ setIsCopied(true);
227
+ onCopy?.();
228
+ timeoutRef.current = window.setTimeout(
229
+ () => setIsCopied(false),
230
+ timeout
231
+ );
232
+ }
233
+ } catch (error) {
234
+ onError?.(error as Error);
235
+ }
236
+ };
237
+
238
+ useEffect(
239
+ () => () => {
240
+ window.clearTimeout(timeoutRef.current);
241
+ },
242
+ []
243
+ );
244
+
245
+ const Icon = isCopied ? CheckIcon : CopyIcon;
246
+
247
+ return (
248
+ <Button
249
+ className={cn("size-7 shrink-0", className)}
250
+ onClick={copyToClipboard}
251
+ size="icon"
252
+ variant="ghost"
253
+ {...props}
254
+ >
255
+ {children ?? <Icon size={14} />}
256
+ </Button>
257
+ );
258
+ };
259
+
260
+ export type CommitContentProps = ComponentProps<typeof CollapsibleContent>;
261
+
262
+ export const CommitContent = ({
263
+ className,
264
+ children,
265
+ ...props
266
+ }: CommitContentProps) => (
267
+ <CollapsibleContent className={cn("border-t p-3", className)} {...props}>
268
+ {children}
269
+ </CollapsibleContent>
270
+ );
271
+
272
+ export type CommitFilesProps = HTMLAttributes<HTMLDivElement>;
273
+
274
+ export const CommitFiles = ({
275
+ className,
276
+ children,
277
+ ...props
278
+ }: CommitFilesProps) => (
279
+ <div className={cn("space-y-1", className)} {...props}>
280
+ {children}
281
+ </div>
282
+ );
283
+
284
+ export type CommitFileProps = HTMLAttributes<HTMLDivElement>;
285
+
286
+ export const CommitFile = ({
287
+ className,
288
+ children,
289
+ ...props
290
+ }: CommitFileProps) => (
291
+ <div
292
+ className={cn(
293
+ "flex items-center justify-between gap-2 rounded px-2 py-1 text-sm hover:bg-muted/50",
294
+ className
295
+ )}
296
+ {...props}
297
+ >
298
+ {children}
299
+ </div>
300
+ );
301
+
302
+ export type CommitFileInfoProps = HTMLAttributes<HTMLDivElement>;
303
+
304
+ export const CommitFileInfo = ({
305
+ className,
306
+ children,
307
+ ...props
308
+ }: CommitFileInfoProps) => (
309
+ <div className={cn("flex min-w-0 items-center gap-2", className)} {...props}>
310
+ {children}
311
+ </div>
312
+ );
313
+
314
+ const fileStatusStyles = {
315
+ added: "text-green-600 dark:text-green-400",
316
+ modified: "text-yellow-600 dark:text-yellow-400",
317
+ deleted: "text-red-600 dark:text-red-400",
318
+ renamed: "text-blue-600 dark:text-blue-400",
319
+ };
320
+
321
+ const fileStatusLabels = {
322
+ added: "A",
323
+ modified: "M",
324
+ deleted: "D",
325
+ renamed: "R",
326
+ };
327
+
328
+ export type CommitFileStatusProps = HTMLAttributes<HTMLSpanElement> & {
329
+ status: "added" | "modified" | "deleted" | "renamed";
330
+ };
331
+
332
+ export const CommitFileStatus = ({
333
+ status,
334
+ className,
335
+ children,
336
+ ...props
337
+ }: CommitFileStatusProps) => (
338
+ <span
339
+ className={cn(
340
+ "font-medium font-mono text-xs",
341
+ fileStatusStyles[status],
342
+ className
343
+ )}
344
+ {...props}
345
+ >
346
+ {children ?? fileStatusLabels[status]}
347
+ </span>
348
+ );
349
+
350
+ export type CommitFileIconProps = ComponentProps<typeof FileIcon>;
351
+
352
+ export const CommitFileIcon = ({
353
+ className,
354
+ ...props
355
+ }: CommitFileIconProps) => (
356
+ <FileIcon
357
+ className={cn("size-3.5 shrink-0 text-muted-foreground", className)}
358
+ {...props}
359
+ />
360
+ );
361
+
362
+ export type CommitFilePathProps = HTMLAttributes<HTMLSpanElement>;
363
+
364
+ export const CommitFilePath = ({
365
+ className,
366
+ children,
367
+ ...props
368
+ }: CommitFilePathProps) => (
369
+ <span className={cn("truncate font-mono text-xs", className)} {...props}>
370
+ {children}
371
+ </span>
372
+ );
373
+
374
+ export type CommitFileChangesProps = HTMLAttributes<HTMLDivElement>;
375
+
376
+ export const CommitFileChanges = ({
377
+ className,
378
+ children,
379
+ ...props
380
+ }: CommitFileChangesProps) => (
381
+ <div
382
+ className={cn(
383
+ "flex shrink-0 items-center gap-1 font-mono text-xs",
384
+ className
385
+ )}
386
+ {...props}
387
+ >
388
+ {children}
389
+ </div>
390
+ );
391
+
392
+ export type CommitFileAdditionsProps = HTMLAttributes<HTMLSpanElement> & {
393
+ count: number;
394
+ };
395
+
396
+ export const CommitFileAdditions = ({
397
+ count,
398
+ className,
399
+ children,
400
+ ...props
401
+ }: CommitFileAdditionsProps) => {
402
+ if (count <= 0) {
403
+ return null;
404
+ }
405
+
406
+ return (
407
+ <span
408
+ className={cn("text-green-600 dark:text-green-400", className)}
409
+ {...props}
410
+ >
411
+ {children ?? (
412
+ <>
413
+ <PlusIcon className="inline-block size-3" />
414
+ {count}
415
+ </>
416
+ )}
417
+ </span>
418
+ );
419
+ };
420
+
421
+ export type CommitFileDeletionsProps = HTMLAttributes<HTMLSpanElement> & {
422
+ count: number;
423
+ };
424
+
425
+ export const CommitFileDeletions = ({
426
+ count,
427
+ className,
428
+ children,
429
+ ...props
430
+ }: CommitFileDeletionsProps) => {
431
+ if (count <= 0) {
432
+ return null;
433
+ }
434
+
435
+ return (
436
+ <span
437
+ className={cn("text-red-600 dark:text-red-400", className)}
438
+ {...props}
439
+ >
440
+ {children ?? (
441
+ <>
442
+ <MinusIcon className="inline-block size-3" />
443
+ {count}
444
+ </>
445
+ )}
446
+ </span>
447
+ );
448
+ };
@@ -0,0 +1,176 @@
1
+ "use client";
2
+
3
+ import { Alert, AlertDescription } from "@/components/ui/alert";
4
+ import { Button } from "@/components/ui/button";
5
+ import { cn } from "@/lib/utils";
6
+ import type { ToolUIPart } from "ai";
7
+ import {
8
+ type ComponentProps,
9
+ createContext,
10
+ type ReactNode,
11
+ useContext,
12
+ } from "react";
13
+
14
+ type ToolUIPartApproval =
15
+ | {
16
+ id: string;
17
+ approved?: never;
18
+ reason?: never;
19
+ }
20
+ | {
21
+ id: string;
22
+ approved: boolean;
23
+ reason?: string;
24
+ }
25
+ | {
26
+ id: string;
27
+ approved: true;
28
+ reason?: string;
29
+ }
30
+ | {
31
+ id: string;
32
+ approved: true;
33
+ reason?: string;
34
+ }
35
+ | {
36
+ id: string;
37
+ approved: false;
38
+ reason?: string;
39
+ }
40
+ | undefined;
41
+
42
+ interface ConfirmationContextValue {
43
+ approval: ToolUIPartApproval;
44
+ state: ToolUIPart["state"];
45
+ }
46
+
47
+ const ConfirmationContext = createContext<ConfirmationContextValue | null>(
48
+ null
49
+ );
50
+
51
+ const useConfirmation = () => {
52
+ const context = useContext(ConfirmationContext);
53
+
54
+ if (!context) {
55
+ throw new Error("Confirmation components must be used within Confirmation");
56
+ }
57
+
58
+ return context;
59
+ };
60
+
61
+ export type ConfirmationProps = ComponentProps<typeof Alert> & {
62
+ approval?: ToolUIPartApproval;
63
+ state: ToolUIPart["state"];
64
+ };
65
+
66
+ export const Confirmation = ({
67
+ className,
68
+ approval,
69
+ state,
70
+ ...props
71
+ }: ConfirmationProps) => {
72
+ if (!approval || state === "input-streaming" || state === "input-available") {
73
+ return null;
74
+ }
75
+
76
+ return (
77
+ <ConfirmationContext.Provider value={{ approval, state }}>
78
+ <Alert className={cn("flex flex-col gap-2", className)} {...props} />
79
+ </ConfirmationContext.Provider>
80
+ );
81
+ };
82
+
83
+ export type ConfirmationTitleProps = ComponentProps<typeof AlertDescription>;
84
+
85
+ export const ConfirmationTitle = ({
86
+ className,
87
+ ...props
88
+ }: ConfirmationTitleProps) => (
89
+ <AlertDescription className={cn("inline", className)} {...props} />
90
+ );
91
+
92
+ export interface ConfirmationRequestProps {
93
+ children?: ReactNode;
94
+ }
95
+
96
+ export const ConfirmationRequest = ({ children }: ConfirmationRequestProps) => {
97
+ const { state } = useConfirmation();
98
+
99
+ // Only show when approval is requested
100
+ if (state !== "approval-requested") {
101
+ return null;
102
+ }
103
+
104
+ return children;
105
+ };
106
+
107
+ export interface ConfirmationAcceptedProps {
108
+ children?: ReactNode;
109
+ }
110
+
111
+ export const ConfirmationAccepted = ({
112
+ children,
113
+ }: ConfirmationAcceptedProps) => {
114
+ const { approval, state } = useConfirmation();
115
+
116
+ // Only show when approved and in response states
117
+ if (
118
+ !approval?.approved ||
119
+ (state !== "approval-responded" &&
120
+ state !== "output-denied" &&
121
+ state !== "output-available")
122
+ ) {
123
+ return null;
124
+ }
125
+
126
+ return children;
127
+ };
128
+
129
+ export interface ConfirmationRejectedProps {
130
+ children?: ReactNode;
131
+ }
132
+
133
+ export const ConfirmationRejected = ({
134
+ children,
135
+ }: ConfirmationRejectedProps) => {
136
+ const { approval, state } = useConfirmation();
137
+
138
+ // Only show when rejected and in response states
139
+ if (
140
+ approval?.approved !== false ||
141
+ (state !== "approval-responded" &&
142
+ state !== "output-denied" &&
143
+ state !== "output-available")
144
+ ) {
145
+ return null;
146
+ }
147
+
148
+ return children;
149
+ };
150
+
151
+ export type ConfirmationActionsProps = ComponentProps<"div">;
152
+
153
+ export const ConfirmationActions = ({
154
+ className,
155
+ ...props
156
+ }: ConfirmationActionsProps) => {
157
+ const { state } = useConfirmation();
158
+
159
+ // Only show when approval is requested
160
+ if (state !== "approval-requested") {
161
+ return null;
162
+ }
163
+
164
+ return (
165
+ <div
166
+ className={cn("flex items-center justify-end gap-2 self-end", className)}
167
+ {...props}
168
+ />
169
+ );
170
+ };
171
+
172
+ export type ConfirmationActionProps = ComponentProps<typeof Button>;
173
+
174
+ export const ConfirmationAction = (props: ConfirmationActionProps) => (
175
+ <Button className="h-8 px-3 text-sm" type="button" {...props} />
176
+ );
@@ -0,0 +1,28 @@
1
+ import type { ConnectionLineComponent } from "@xyflow/react";
2
+
3
+ const HALF = 0.5;
4
+
5
+ export const Connection: ConnectionLineComponent = ({
6
+ fromX,
7
+ fromY,
8
+ toX,
9
+ toY,
10
+ }) => (
11
+ <g>
12
+ <path
13
+ className="animated"
14
+ d={`M${fromX},${fromY} C ${fromX + (toX - fromX) * HALF},${fromY} ${fromX + (toX - fromX) * HALF},${toY} ${toX},${toY}`}
15
+ fill="none"
16
+ stroke="var(--color-ring)"
17
+ strokeWidth={1}
18
+ />
19
+ <circle
20
+ cx={toX}
21
+ cy={toY}
22
+ fill="#fff"
23
+ r={3}
24
+ stroke="var(--color-ring)"
25
+ strokeWidth={1}
26
+ />
27
+ </g>
28
+ );