@openconsole/shadcn 0.2.4 → 0.2.6

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 (117) hide show
  1. package/README.md +460 -380
  2. package/components/ai-elements/agent.tsx +141 -0
  3. package/components/ai-elements/artifact.tsx +148 -0
  4. package/components/ai-elements/attachments.tsx +426 -0
  5. package/components/ai-elements/audio-player.tsx +231 -0
  6. package/components/ai-elements/canvas.tsx +26 -0
  7. package/components/ai-elements/chain-of-thought.tsx +222 -0
  8. package/components/ai-elements/checkpoint.tsx +71 -0
  9. package/components/ai-elements/code-block.tsx +562 -0
  10. package/components/ai-elements/commit.tsx +458 -0
  11. package/components/ai-elements/confirmation.tsx +174 -0
  12. package/components/ai-elements/connection.tsx +28 -0
  13. package/components/ai-elements/context.tsx +409 -0
  14. package/components/ai-elements/controls.tsx +18 -0
  15. package/components/ai-elements/conversation.tsx +168 -0
  16. package/components/ai-elements/edge.tsx +143 -0
  17. package/components/ai-elements/environment-variables.tsx +324 -0
  18. package/components/ai-elements/file-tree.tsx +304 -0
  19. package/components/ai-elements/image.tsx +24 -0
  20. package/components/ai-elements/index.ts +51 -0
  21. package/components/ai-elements/inline-citation.tsx +296 -0
  22. package/components/ai-elements/jsx-preview.tsx +310 -0
  23. package/components/ai-elements/message.tsx +360 -0
  24. package/components/ai-elements/mic-selector.tsx +375 -0
  25. package/components/ai-elements/model-selector.tsx +213 -0
  26. package/components/ai-elements/node.tsx +71 -0
  27. package/components/ai-elements/open-in-chat.tsx +370 -0
  28. package/components/ai-elements/package-info.tsx +239 -0
  29. package/components/ai-elements/panel.tsx +15 -0
  30. package/components/ai-elements/persona.tsx +306 -0
  31. package/components/ai-elements/plan.tsx +147 -0
  32. package/components/ai-elements/prompt-input.tsx +1463 -0
  33. package/components/ai-elements/queue.tsx +274 -0
  34. package/components/ai-elements/reasoning.tsx +228 -0
  35. package/components/ai-elements/sandbox.tsx +132 -0
  36. package/components/ai-elements/schema-display.tsx +471 -0
  37. package/components/ai-elements/shimmer.tsx +77 -0
  38. package/components/ai-elements/snippet.tsx +145 -0
  39. package/components/ai-elements/sources.tsx +77 -0
  40. package/components/ai-elements/speech-input.tsx +323 -0
  41. package/components/ai-elements/stack-trace.tsx +528 -0
  42. package/components/ai-elements/suggestion.tsx +57 -0
  43. package/components/ai-elements/task.tsx +87 -0
  44. package/components/ai-elements/terminal.tsx +273 -0
  45. package/components/ai-elements/test-results.tsx +496 -0
  46. package/components/ai-elements/tool.tsx +173 -0
  47. package/components/ai-elements/toolbar.tsx +16 -0
  48. package/components/ai-elements/transcription.tsx +125 -0
  49. package/components/ai-elements/voice-selector.tsx +524 -0
  50. package/components/ai-elements/web-preview.tsx +281 -0
  51. package/components/index.ts +3 -0
  52. package/{accordion.tsx → components/ui/accordion.tsx} +66 -66
  53. package/{alert-dialog.tsx → components/ui/alert-dialog.tsx} +196 -196
  54. package/{alert.tsx → components/ui/alert.tsx} +66 -66
  55. package/{aspect-ratio.tsx → components/ui/aspect-ratio.tsx} +11 -11
  56. package/{avatar.tsx → components/ui/avatar.tsx} +53 -53
  57. package/{badge.tsx → components/ui/badge.tsx} +46 -46
  58. package/{breadcrumb.tsx → components/ui/breadcrumb.tsx} +109 -109
  59. package/{button-group.tsx → components/ui/button-group.tsx} +83 -83
  60. package/{button.tsx → components/ui/button.tsx} +60 -60
  61. package/{calendar.tsx → components/ui/calendar.tsx} +219 -219
  62. package/{card.tsx → components/ui/card.tsx} +92 -92
  63. package/{carousel.tsx → components/ui/carousel.tsx} +241 -241
  64. package/{chart.tsx → components/ui/chart.tsx} +374 -374
  65. package/{checkbox.tsx → components/ui/checkbox.tsx} +32 -32
  66. package/{collapsible.tsx → components/ui/collapsible.tsx} +33 -33
  67. package/{command.tsx → components/ui/command.tsx} +184 -184
  68. package/{context-menu.tsx → components/ui/context-menu.tsx} +252 -252
  69. package/{dialog.tsx → components/ui/dialog.tsx} +143 -143
  70. package/{direction.tsx → components/ui/direction.tsx} +22 -22
  71. package/{drawer.tsx → components/ui/drawer.tsx} +135 -135
  72. package/{dropdown-menu.tsx → components/ui/dropdown-menu.tsx} +257 -257
  73. package/{empty.tsx → components/ui/empty.tsx} +104 -104
  74. package/{field.tsx → components/ui/field.tsx} +248 -248
  75. package/{form.tsx → components/ui/form.tsx} +167 -167
  76. package/{hover-card.tsx → components/ui/hover-card.tsx} +44 -44
  77. package/{icon.tsx → components/ui/icon.tsx} +55 -55
  78. package/components/ui/index.ts +59 -0
  79. package/{input-group.tsx → components/ui/input-group.tsx} +170 -170
  80. package/{input-otp.tsx → components/ui/input-otp.tsx} +77 -77
  81. package/{input.tsx → components/ui/input.tsx} +21 -21
  82. package/{item.tsx → components/ui/item.tsx} +193 -193
  83. package/{kbd.tsx → components/ui/kbd.tsx} +28 -28
  84. package/{label.tsx → components/ui/label.tsx} +24 -24
  85. package/{menubar.tsx → components/ui/menubar.tsx} +276 -276
  86. package/{native-select.tsx → components/ui/native-select.tsx} +62 -62
  87. package/{navigation-menu.tsx → components/ui/navigation-menu.tsx} +168 -168
  88. package/{pagination.tsx → components/ui/pagination.tsx} +127 -127
  89. package/{popover.tsx → components/ui/popover.tsx} +89 -89
  90. package/{progress.tsx → components/ui/progress.tsx} +31 -31
  91. package/{radio-group.tsx → components/ui/radio-group.tsx} +45 -45
  92. package/{resizable.tsx → components/ui/resizable.tsx} +53 -53
  93. package/{scroll-area.tsx → components/ui/scroll-area.tsx} +58 -58
  94. package/{select.tsx → components/ui/select.tsx} +187 -187
  95. package/{separator.tsx → components/ui/separator.tsx} +28 -28
  96. package/{sheet.tsx → components/ui/sheet.tsx} +139 -139
  97. package/{sidebar.tsx → components/ui/sidebar.tsx} +724 -724
  98. package/{skeleton.tsx → components/ui/skeleton.tsx} +13 -13
  99. package/{slider.tsx → components/ui/slider.tsx} +63 -63
  100. package/{sonner.tsx → components/ui/sonner.tsx} +40 -40
  101. package/{spinner.tsx → components/ui/spinner.tsx} +16 -16
  102. package/{switch.tsx → components/ui/switch.tsx} +35 -35
  103. package/{table.tsx → components/ui/table.tsx} +116 -116
  104. package/{tabs.tsx → components/ui/tabs.tsx} +66 -66
  105. package/{textarea.tsx → components/ui/textarea.tsx} +18 -18
  106. package/{toggle-group.tsx → components/ui/toggle-group.tsx} +83 -83
  107. package/{toggle.tsx → components/ui/toggle.tsx} +47 -47
  108. package/{tooltip.tsx → components/ui/tooltip.tsx} +61 -61
  109. package/hooks/index.ts +1 -1
  110. package/hooks/use-mobile.ts +19 -19
  111. package/index.ts +3 -59
  112. package/lib/index.ts +1 -1
  113. package/lib/utils.ts +6 -6
  114. package/package.json +79 -1
  115. package/styles.css +124 -124
  116. package/tsconfig.json +0 -12
  117. package/tsconfig.tsbuildinfo +0 -1
@@ -0,0 +1,458 @@
1
+ "use client";
2
+
3
+ import { Avatar, AvatarFallback } from "../ui/avatar";
4
+ import { Button } from "../ui/button";
5
+ import {
6
+ Collapsible,
7
+ CollapsibleContent,
8
+ CollapsibleTrigger,
9
+ } from "../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 type { ComponentProps, HTMLAttributes } from "react";
20
+ import { useCallback, useEffect, useRef, useState } from "react";
21
+
22
+ export type CommitProps = ComponentProps<typeof Collapsible>;
23
+
24
+ export const Commit = ({ className, children, ...props }: CommitProps) => (
25
+ <Collapsible
26
+ className={cn("rounded-lg border bg-background", className)}
27
+ {...props}
28
+ >
29
+ {children}
30
+ </Collapsible>
31
+ );
32
+
33
+ export type CommitHeaderProps = ComponentProps<typeof CollapsibleTrigger>;
34
+
35
+ export const CommitHeader = ({
36
+ className,
37
+ children,
38
+ ...props
39
+ }: CommitHeaderProps) => (
40
+ <CollapsibleTrigger asChild {...props}>
41
+ <div
42
+ className={cn(
43
+ "group flex cursor-pointer items-center justify-between gap-4 p-3 text-left transition-colors hover:opacity-80",
44
+ className
45
+ )}
46
+ >
47
+ {children}
48
+ </div>
49
+ </CollapsibleTrigger>
50
+ );
51
+
52
+ export type CommitHashProps = HTMLAttributes<HTMLSpanElement>;
53
+
54
+ export const CommitHash = ({
55
+ className,
56
+ children,
57
+ ...props
58
+ }: CommitHashProps) => (
59
+ <span className={cn("font-mono text-xs", className)} {...props}>
60
+ <GitCommitIcon className="mr-1 inline-block size-3" />
61
+ {children}
62
+ </span>
63
+ );
64
+
65
+ export type CommitMessageProps = HTMLAttributes<HTMLSpanElement>;
66
+
67
+ export const CommitMessage = ({
68
+ className,
69
+ children,
70
+ ...props
71
+ }: CommitMessageProps) => (
72
+ <span className={cn("font-medium text-sm", className)} {...props}>
73
+ {children}
74
+ </span>
75
+ );
76
+
77
+ export type CommitMetadataProps = HTMLAttributes<HTMLDivElement>;
78
+
79
+ export const CommitMetadata = ({
80
+ className,
81
+ children,
82
+ ...props
83
+ }: CommitMetadataProps) => (
84
+ <div
85
+ className={cn(
86
+ "flex items-center gap-2 text-muted-foreground text-xs",
87
+ className
88
+ )}
89
+ {...props}
90
+ >
91
+ {children}
92
+ </div>
93
+ );
94
+
95
+ export type CommitSeparatorProps = HTMLAttributes<HTMLSpanElement>;
96
+
97
+ export const CommitSeparator = ({
98
+ className,
99
+ children,
100
+ ...props
101
+ }: CommitSeparatorProps) => (
102
+ <span className={className} {...props}>
103
+ {children ?? "•"}
104
+ </span>
105
+ );
106
+
107
+ export type CommitInfoProps = HTMLAttributes<HTMLDivElement>;
108
+
109
+ export const CommitInfo = ({
110
+ className,
111
+ children,
112
+ ...props
113
+ }: CommitInfoProps) => (
114
+ <div className={cn("flex flex-1 flex-col", className)} {...props}>
115
+ {children}
116
+ </div>
117
+ );
118
+
119
+ export type CommitAuthorProps = HTMLAttributes<HTMLDivElement>;
120
+
121
+ export const CommitAuthor = ({
122
+ className,
123
+ children,
124
+ ...props
125
+ }: CommitAuthorProps) => (
126
+ <div className={cn("flex items-center", className)} {...props}>
127
+ {children}
128
+ </div>
129
+ );
130
+
131
+ export type CommitAuthorAvatarProps = ComponentProps<typeof Avatar> & {
132
+ initials: string;
133
+ };
134
+
135
+ export const CommitAuthorAvatar = ({
136
+ initials,
137
+ className,
138
+ ...props
139
+ }: CommitAuthorAvatarProps) => (
140
+ <Avatar className={cn("size-8", className)} {...props}>
141
+ <AvatarFallback className="text-xs">{initials}</AvatarFallback>
142
+ </Avatar>
143
+ );
144
+
145
+ export type CommitTimestampProps = HTMLAttributes<HTMLTimeElement> & {
146
+ date: Date;
147
+ };
148
+
149
+ const relativeTimeFormat = new Intl.RelativeTimeFormat("en", {
150
+ numeric: "auto",
151
+ });
152
+
153
+ const formatRelativeDate = (date: Date) => {
154
+ const days = Math.round(
155
+ (date.getTime() - Date.now()) / (1000 * 60 * 60 * 24)
156
+ );
157
+ return relativeTimeFormat.format(days, "day");
158
+ };
159
+
160
+ export const CommitTimestamp = ({
161
+ date,
162
+ className,
163
+ children,
164
+ ...props
165
+ }: CommitTimestampProps) => {
166
+ const [formatted, setFormatted] = useState("");
167
+
168
+ const updateFormatted = useCallback(() => {
169
+ setFormatted(formatRelativeDate(date));
170
+ }, [date]);
171
+
172
+ useEffect(() => {
173
+ updateFormatted();
174
+ }, [updateFormatted]);
175
+
176
+ return (
177
+ <time
178
+ className={cn("text-xs", className)}
179
+ dateTime={date.toISOString()}
180
+ {...props}
181
+ >
182
+ {children ?? formatted}
183
+ </time>
184
+ );
185
+ };
186
+
187
+ export type CommitActionsProps = HTMLAttributes<HTMLDivElement>;
188
+
189
+ const handleActionsClick = (e: React.MouseEvent) => e.stopPropagation();
190
+ const handleActionsKeyDown = (e: React.KeyboardEvent) => e.stopPropagation();
191
+
192
+ export const CommitActions = ({
193
+ className,
194
+ children,
195
+ ...props
196
+ }: CommitActionsProps) => (
197
+ <div
198
+ className={cn("flex items-center gap-1", className)}
199
+ onClick={handleActionsClick}
200
+ onKeyDown={handleActionsKeyDown}
201
+ role="group"
202
+ {...props}
203
+ >
204
+ {children}
205
+ </div>
206
+ );
207
+
208
+ export type CommitCopyButtonProps = ComponentProps<typeof Button> & {
209
+ hash: string;
210
+ onCopy?: () => void;
211
+ onError?: (error: Error) => void;
212
+ timeout?: number;
213
+ };
214
+
215
+ export const CommitCopyButton = ({
216
+ hash,
217
+ onCopy,
218
+ onError,
219
+ timeout = 2000,
220
+ children,
221
+ className,
222
+ ...props
223
+ }: CommitCopyButtonProps) => {
224
+ const [isCopied, setIsCopied] = useState(false);
225
+ const timeoutRef = useRef<number>(0);
226
+
227
+ const copyToClipboard = useCallback(async () => {
228
+ if (typeof window === "undefined" || !navigator?.clipboard?.writeText) {
229
+ onError?.(new Error("Clipboard API not available"));
230
+ return;
231
+ }
232
+
233
+ try {
234
+ if (!isCopied) {
235
+ await navigator.clipboard.writeText(hash);
236
+ setIsCopied(true);
237
+ onCopy?.();
238
+ timeoutRef.current = window.setTimeout(
239
+ () => setIsCopied(false),
240
+ timeout
241
+ );
242
+ }
243
+ } catch (error) {
244
+ onError?.(error as Error);
245
+ }
246
+ }, [hash, onCopy, onError, timeout, isCopied]);
247
+
248
+ useEffect(
249
+ () => () => {
250
+ window.clearTimeout(timeoutRef.current);
251
+ },
252
+ []
253
+ );
254
+
255
+ const Icon = isCopied ? CheckIcon : CopyIcon;
256
+
257
+ return (
258
+ <Button
259
+ className={cn("size-7 shrink-0", className)}
260
+ onClick={copyToClipboard}
261
+ size="icon"
262
+ variant="ghost"
263
+ {...props}
264
+ >
265
+ {children ?? <Icon size={14} />}
266
+ </Button>
267
+ );
268
+ };
269
+
270
+ export type CommitContentProps = ComponentProps<typeof CollapsibleContent>;
271
+
272
+ export const CommitContent = ({
273
+ className,
274
+ children,
275
+ ...props
276
+ }: CommitContentProps) => (
277
+ <CollapsibleContent className={cn("border-t p-3", className)} {...props}>
278
+ {children}
279
+ </CollapsibleContent>
280
+ );
281
+
282
+ export type CommitFilesProps = HTMLAttributes<HTMLDivElement>;
283
+
284
+ export const CommitFiles = ({
285
+ className,
286
+ children,
287
+ ...props
288
+ }: CommitFilesProps) => (
289
+ <div className={cn("space-y-1", className)} {...props}>
290
+ {children}
291
+ </div>
292
+ );
293
+
294
+ export type CommitFileProps = HTMLAttributes<HTMLDivElement>;
295
+
296
+ export const CommitFile = ({
297
+ className,
298
+ children,
299
+ ...props
300
+ }: CommitFileProps) => (
301
+ <div
302
+ className={cn(
303
+ "flex items-center justify-between gap-2 rounded px-2 py-1 text-sm hover:bg-muted/50",
304
+ className
305
+ )}
306
+ {...props}
307
+ >
308
+ {children}
309
+ </div>
310
+ );
311
+
312
+ export type CommitFileInfoProps = HTMLAttributes<HTMLDivElement>;
313
+
314
+ export const CommitFileInfo = ({
315
+ className,
316
+ children,
317
+ ...props
318
+ }: CommitFileInfoProps) => (
319
+ <div className={cn("flex min-w-0 items-center gap-2", className)} {...props}>
320
+ {children}
321
+ </div>
322
+ );
323
+
324
+ const fileStatusStyles = {
325
+ added: "text-green-600 dark:text-green-400",
326
+ deleted: "text-red-600 dark:text-red-400",
327
+ modified: "text-yellow-600 dark:text-yellow-400",
328
+ renamed: "text-blue-600 dark:text-blue-400",
329
+ };
330
+
331
+ const fileStatusLabels = {
332
+ added: "A",
333
+ deleted: "D",
334
+ modified: "M",
335
+ renamed: "R",
336
+ };
337
+
338
+ export type CommitFileStatusProps = HTMLAttributes<HTMLSpanElement> & {
339
+ status: "added" | "modified" | "deleted" | "renamed";
340
+ };
341
+
342
+ export const CommitFileStatus = ({
343
+ status,
344
+ className,
345
+ children,
346
+ ...props
347
+ }: CommitFileStatusProps) => (
348
+ <span
349
+ className={cn(
350
+ "font-medium font-mono text-xs",
351
+ fileStatusStyles[status],
352
+ className
353
+ )}
354
+ {...props}
355
+ >
356
+ {children ?? fileStatusLabels[status]}
357
+ </span>
358
+ );
359
+
360
+ export type CommitFileIconProps = ComponentProps<typeof FileIcon>;
361
+
362
+ export const CommitFileIcon = ({
363
+ className,
364
+ ...props
365
+ }: CommitFileIconProps) => (
366
+ <FileIcon
367
+ className={cn("size-3.5 shrink-0 text-muted-foreground", className)}
368
+ {...props}
369
+ />
370
+ );
371
+
372
+ export type CommitFilePathProps = HTMLAttributes<HTMLSpanElement>;
373
+
374
+ export const CommitFilePath = ({
375
+ className,
376
+ children,
377
+ ...props
378
+ }: CommitFilePathProps) => (
379
+ <span className={cn("truncate font-mono text-xs", className)} {...props}>
380
+ {children}
381
+ </span>
382
+ );
383
+
384
+ export type CommitFileChangesProps = HTMLAttributes<HTMLDivElement>;
385
+
386
+ export const CommitFileChanges = ({
387
+ className,
388
+ children,
389
+ ...props
390
+ }: CommitFileChangesProps) => (
391
+ <div
392
+ className={cn(
393
+ "flex shrink-0 items-center gap-1 font-mono text-xs",
394
+ className
395
+ )}
396
+ {...props}
397
+ >
398
+ {children}
399
+ </div>
400
+ );
401
+
402
+ export type CommitFileAdditionsProps = HTMLAttributes<HTMLSpanElement> & {
403
+ count: number;
404
+ };
405
+
406
+ export const CommitFileAdditions = ({
407
+ count,
408
+ className,
409
+ children,
410
+ ...props
411
+ }: CommitFileAdditionsProps) => {
412
+ if (count <= 0) {
413
+ return null;
414
+ }
415
+
416
+ return (
417
+ <span
418
+ className={cn("text-green-600 dark:text-green-400", className)}
419
+ {...props}
420
+ >
421
+ {children ?? (
422
+ <>
423
+ <PlusIcon className="inline-block size-3" />
424
+ {count}
425
+ </>
426
+ )}
427
+ </span>
428
+ );
429
+ };
430
+
431
+ export type CommitFileDeletionsProps = HTMLAttributes<HTMLSpanElement> & {
432
+ count: number;
433
+ };
434
+
435
+ export const CommitFileDeletions = ({
436
+ count,
437
+ className,
438
+ children,
439
+ ...props
440
+ }: CommitFileDeletionsProps) => {
441
+ if (count <= 0) {
442
+ return null;
443
+ }
444
+
445
+ return (
446
+ <span
447
+ className={cn("text-red-600 dark:text-red-400", className)}
448
+ {...props}
449
+ >
450
+ {children ?? (
451
+ <>
452
+ <MinusIcon className="inline-block size-3" />
453
+ {count}
454
+ </>
455
+ )}
456
+ </span>
457
+ );
458
+ };
@@ -0,0 +1,174 @@
1
+ "use client";
2
+
3
+ import { Alert, AlertDescription } from "../ui/alert";
4
+ import { Button } from "../ui/button";
5
+ import { cn } from "../../lib/utils";
6
+ import type { ToolUIPart } from "ai";
7
+ import type { ComponentProps, ReactNode } from "react";
8
+ import { createContext, useContext, useMemo } from "react";
9
+
10
+ type ToolUIPartApproval =
11
+ | {
12
+ id: string;
13
+ approved?: never;
14
+ reason?: never;
15
+ }
16
+ | {
17
+ id: string;
18
+ approved: boolean;
19
+ reason?: string;
20
+ }
21
+ | {
22
+ id: string;
23
+ approved: true;
24
+ reason?: string;
25
+ }
26
+ | {
27
+ id: string;
28
+ approved: true;
29
+ reason?: string;
30
+ }
31
+ | {
32
+ id: string;
33
+ approved: false;
34
+ reason?: string;
35
+ }
36
+ | undefined;
37
+
38
+ interface ConfirmationContextValue {
39
+ approval: ToolUIPartApproval;
40
+ state: ToolUIPart["state"];
41
+ }
42
+
43
+ const ConfirmationContext = createContext<ConfirmationContextValue | null>(
44
+ null
45
+ );
46
+
47
+ const useConfirmation = () => {
48
+ const context = useContext(ConfirmationContext);
49
+
50
+ if (!context) {
51
+ throw new Error("Confirmation components must be used within Confirmation");
52
+ }
53
+
54
+ return context;
55
+ };
56
+
57
+ export type ConfirmationProps = ComponentProps<typeof Alert> & {
58
+ approval?: ToolUIPartApproval;
59
+ state: ToolUIPart["state"];
60
+ };
61
+
62
+ export const Confirmation = ({
63
+ className,
64
+ approval,
65
+ state,
66
+ ...props
67
+ }: ConfirmationProps) => {
68
+ const contextValue = useMemo(() => ({ approval, state }), [approval, state]);
69
+
70
+ if (!approval || state === "input-streaming" || state === "input-available") {
71
+ return null;
72
+ }
73
+
74
+ return (
75
+ <ConfirmationContext.Provider value={contextValue}>
76
+ <Alert className={cn("flex flex-col gap-2", className)} {...props} />
77
+ </ConfirmationContext.Provider>
78
+ );
79
+ };
80
+
81
+ export type ConfirmationTitleProps = ComponentProps<typeof AlertDescription>;
82
+
83
+ export const ConfirmationTitle = ({
84
+ className,
85
+ ...props
86
+ }: ConfirmationTitleProps) => (
87
+ <AlertDescription className={cn("inline", className)} {...props} />
88
+ );
89
+
90
+ export interface ConfirmationRequestProps {
91
+ children?: ReactNode;
92
+ }
93
+
94
+ export const ConfirmationRequest = ({ children }: ConfirmationRequestProps) => {
95
+ const { state } = useConfirmation();
96
+
97
+ // Only show when approval is requested
98
+ if (state !== "approval-requested") {
99
+ return null;
100
+ }
101
+
102
+ return children;
103
+ };
104
+
105
+ export interface ConfirmationAcceptedProps {
106
+ children?: ReactNode;
107
+ }
108
+
109
+ export const ConfirmationAccepted = ({
110
+ children,
111
+ }: ConfirmationAcceptedProps) => {
112
+ const { approval, state } = useConfirmation();
113
+
114
+ // Only show when approved and in response states
115
+ if (
116
+ !approval?.approved ||
117
+ (state !== "approval-responded" &&
118
+ state !== "output-denied" &&
119
+ state !== "output-available")
120
+ ) {
121
+ return null;
122
+ }
123
+
124
+ return children;
125
+ };
126
+
127
+ export interface ConfirmationRejectedProps {
128
+ children?: ReactNode;
129
+ }
130
+
131
+ export const ConfirmationRejected = ({
132
+ children,
133
+ }: ConfirmationRejectedProps) => {
134
+ const { approval, state } = useConfirmation();
135
+
136
+ // Only show when rejected and in response states
137
+ if (
138
+ approval?.approved !== false ||
139
+ (state !== "approval-responded" &&
140
+ state !== "output-denied" &&
141
+ state !== "output-available")
142
+ ) {
143
+ return null;
144
+ }
145
+
146
+ return children;
147
+ };
148
+
149
+ export type ConfirmationActionsProps = ComponentProps<"div">;
150
+
151
+ export const ConfirmationActions = ({
152
+ className,
153
+ ...props
154
+ }: ConfirmationActionsProps) => {
155
+ const { state } = useConfirmation();
156
+
157
+ // Only show when approval is requested
158
+ if (state !== "approval-requested") {
159
+ return null;
160
+ }
161
+
162
+ return (
163
+ <div
164
+ className={cn("flex items-center justify-end gap-2 self-end", className)}
165
+ {...props}
166
+ />
167
+ );
168
+ };
169
+
170
+ export type ConfirmationActionProps = ComponentProps<typeof Button>;
171
+
172
+ export const ConfirmationAction = (props: ConfirmationActionProps) => (
173
+ <Button className="h-8 px-3 text-sm" type="button" {...props} />
174
+ );
@@ -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
+ );