@cloudbase/agent-react-ui 1.0.1-alpha.32

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 +123 -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,228 @@
1
+ "use client";
2
+
3
+ import { Button } from "@/components/ui/button";
4
+ import { ButtonGroup, ButtonGroupText } from "@/components/ui/button-group";
5
+ import { cn } from "@/lib/utils";
6
+ import type { Experimental_SpeechResult as SpeechResult } from "ai";
7
+ import {
8
+ MediaControlBar,
9
+ MediaController,
10
+ MediaDurationDisplay,
11
+ MediaMuteButton,
12
+ MediaPlayButton,
13
+ MediaSeekBackwardButton,
14
+ MediaSeekForwardButton,
15
+ MediaTimeDisplay,
16
+ MediaTimeRange,
17
+ MediaVolumeRange,
18
+ } from "media-chrome/react";
19
+ import type { ComponentProps, CSSProperties } from "react";
20
+
21
+ export type AudioPlayerProps = Omit<
22
+ ComponentProps<typeof MediaController>,
23
+ "audio"
24
+ >;
25
+
26
+ export const AudioPlayer = ({
27
+ className,
28
+ children,
29
+ style,
30
+ ...props
31
+ }: AudioPlayerProps) => (
32
+ <MediaController
33
+ audio
34
+ data-slot="audio-player"
35
+ style={
36
+ {
37
+ "--media-button-icon-width": "1rem",
38
+ "--media-button-icon-height": "1rem",
39
+ "--media-icon-color": "currentColor",
40
+ "--media-font": "var(--font-sans)",
41
+ "--media-font-size": "10px",
42
+ "--media-control-background": "transparent",
43
+ "--media-control-hover-background": "var(--color-accent)",
44
+ "--media-control-padding": "0",
45
+ "--media-background-color": "transparent",
46
+ "--media-primary-color": "var(--color-primary)",
47
+ "--media-secondary-color": "var(--color-secondary)",
48
+ "--media-text-color": "var(--color-foreground)",
49
+ "--media-tooltip-background": "var(--color-background)",
50
+ "--media-range-bar-color": "var(--color-primary)",
51
+ "--media-tooltip-arrow-display": "none",
52
+ "--media-tooltip-border-radius": "var(--radius-md)",
53
+ "--media-preview-time-text-shadow": "none",
54
+ "--media-preview-time-background": "var(--color-background)",
55
+ "--media-preview-time-border-radius": "var(--radius-md)",
56
+ "--media-range-track-background": "var(--color-secondary)",
57
+ ...style,
58
+ } as CSSProperties
59
+ }
60
+ {...props}
61
+ >
62
+ {children}
63
+ </MediaController>
64
+ );
65
+
66
+ export type AudioPlayerElementProps = Omit<ComponentProps<"audio">, "src"> &
67
+ (
68
+ | {
69
+ data: SpeechResult["audio"];
70
+ }
71
+ | {
72
+ src: string;
73
+ }
74
+ );
75
+
76
+ export const AudioPlayerElement = ({ ...props }: AudioPlayerElementProps) => (
77
+ <audio
78
+ data-slot="audio-player-element"
79
+ slot="media"
80
+ src={
81
+ "src" in props
82
+ ? props.src
83
+ : `data:${props.data.mediaType};base64,${props.data.base64}`
84
+ }
85
+ {...props}
86
+ />
87
+ );
88
+
89
+ export type AudioPlayerControlBarProps = ComponentProps<typeof MediaControlBar>;
90
+
91
+ export const AudioPlayerControlBar = ({
92
+ children,
93
+ ...props
94
+ }: AudioPlayerControlBarProps) => (
95
+ <MediaControlBar data-slot="audio-player-control-bar" {...props}>
96
+ <ButtonGroup orientation="horizontal">{children}</ButtonGroup>
97
+ </MediaControlBar>
98
+ );
99
+
100
+ export type AudioPlayerPlayButtonProps = ComponentProps<typeof MediaPlayButton>;
101
+
102
+ export const AudioPlayerPlayButton = ({
103
+ className,
104
+ ...props
105
+ }: AudioPlayerPlayButtonProps) => (
106
+ <Button asChild size="icon-sm" variant="outline">
107
+ <MediaPlayButton
108
+ className={cn("bg-transparent", className)}
109
+ data-slot="audio-player-play-button"
110
+ {...props}
111
+ />
112
+ </Button>
113
+ );
114
+
115
+ export type AudioPlayerSeekBackwardButtonProps = ComponentProps<
116
+ typeof MediaSeekBackwardButton
117
+ >;
118
+
119
+ export const AudioPlayerSeekBackwardButton = ({
120
+ seekOffset = 10,
121
+ ...props
122
+ }: AudioPlayerSeekBackwardButtonProps) => (
123
+ <Button asChild size="icon-sm" variant="outline">
124
+ <MediaSeekBackwardButton
125
+ data-slot="audio-player-seek-backward-button"
126
+ seekOffset={seekOffset}
127
+ {...props}
128
+ />
129
+ </Button>
130
+ );
131
+
132
+ export type AudioPlayerSeekForwardButtonProps = ComponentProps<
133
+ typeof MediaSeekForwardButton
134
+ >;
135
+
136
+ export const AudioPlayerSeekForwardButton = ({
137
+ seekOffset = 10,
138
+ ...props
139
+ }: AudioPlayerSeekForwardButtonProps) => (
140
+ <Button asChild size="icon-sm" variant="outline">
141
+ <MediaSeekForwardButton
142
+ data-slot="audio-player-seek-forward-button"
143
+ seekOffset={seekOffset}
144
+ {...props}
145
+ />
146
+ </Button>
147
+ );
148
+
149
+ export type AudioPlayerTimeDisplayProps = ComponentProps<
150
+ typeof MediaTimeDisplay
151
+ >;
152
+
153
+ export const AudioPlayerTimeDisplay = ({
154
+ className,
155
+ ...props
156
+ }: AudioPlayerTimeDisplayProps) => (
157
+ <ButtonGroupText asChild className="bg-transparent">
158
+ <MediaTimeDisplay
159
+ className={cn("tabular-nums", className)}
160
+ data-slot="audio-player-time-display"
161
+ {...props}
162
+ />
163
+ </ButtonGroupText>
164
+ );
165
+
166
+ export type AudioPlayerTimeRangeProps = ComponentProps<typeof MediaTimeRange>;
167
+
168
+ export const AudioPlayerTimeRange = ({
169
+ className,
170
+ ...props
171
+ }: AudioPlayerTimeRangeProps) => (
172
+ <ButtonGroupText asChild className="bg-transparent">
173
+ <MediaTimeRange
174
+ className={cn("", className)}
175
+ data-slot="audio-player-time-range"
176
+ {...props}
177
+ />
178
+ </ButtonGroupText>
179
+ );
180
+
181
+ export type AudioPlayerDurationDisplayProps = ComponentProps<
182
+ typeof MediaDurationDisplay
183
+ >;
184
+
185
+ export const AudioPlayerDurationDisplay = ({
186
+ className,
187
+ ...props
188
+ }: AudioPlayerDurationDisplayProps) => (
189
+ <ButtonGroupText asChild className="bg-transparent">
190
+ <MediaDurationDisplay
191
+ className={cn("tabular-nums", className)}
192
+ data-slot="audio-player-duration-display"
193
+ {...props}
194
+ />
195
+ </ButtonGroupText>
196
+ );
197
+
198
+ export type AudioPlayerMuteButtonProps = ComponentProps<typeof MediaMuteButton>;
199
+
200
+ export const AudioPlayerMuteButton = ({
201
+ className,
202
+ ...props
203
+ }: AudioPlayerMuteButtonProps) => (
204
+ <ButtonGroupText asChild className="bg-transparent">
205
+ <MediaMuteButton
206
+ className={cn("", className)}
207
+ data-slot="audio-player-mute-button"
208
+ {...props}
209
+ />
210
+ </ButtonGroupText>
211
+ );
212
+
213
+ export type AudioPlayerVolumeRangeProps = ComponentProps<
214
+ typeof MediaVolumeRange
215
+ >;
216
+
217
+ export const AudioPlayerVolumeRange = ({
218
+ className,
219
+ ...props
220
+ }: AudioPlayerVolumeRangeProps) => (
221
+ <ButtonGroupText asChild className="bg-transparent">
222
+ <MediaVolumeRange
223
+ className={cn("", className)}
224
+ data-slot="audio-player-volume-range"
225
+ {...props}
226
+ />
227
+ </ButtonGroupText>
228
+ );
@@ -0,0 +1,22 @@
1
+ import { Background, ReactFlow, type ReactFlowProps } from "@xyflow/react";
2
+ import type { ReactNode } from "react";
3
+ import "@xyflow/react/dist/style.css";
4
+
5
+ type CanvasProps = ReactFlowProps & {
6
+ children?: ReactNode;
7
+ };
8
+
9
+ export const Canvas = ({ children, ...props }: CanvasProps) => (
10
+ <ReactFlow
11
+ deleteKeyCode={["Backspace", "Delete"]}
12
+ fitView
13
+ panOnDrag={false}
14
+ panOnScroll
15
+ selectionOnDrag={true}
16
+ zoomOnDoubleClick={false}
17
+ {...props}
18
+ >
19
+ <Background bgColor="var(--sidebar)" />
20
+ {children}
21
+ </ReactFlow>
22
+ );
@@ -0,0 +1,228 @@
1
+ "use client";
2
+
3
+ import { useControllableState } from "@radix-ui/react-use-controllable-state";
4
+ import { Badge } from "@/components/ui/badge";
5
+ import {
6
+ Collapsible,
7
+ CollapsibleContent,
8
+ CollapsibleTrigger,
9
+ } from "@/components/ui/collapsible";
10
+ import { cn } from "@/lib/utils";
11
+ import {
12
+ BrainIcon,
13
+ ChevronDownIcon,
14
+ DotIcon,
15
+ type LucideIcon,
16
+ } from "lucide-react";
17
+ import type { ComponentProps, ReactNode } from "react";
18
+ import { createContext, memo, useContext, useMemo } from "react";
19
+
20
+ interface ChainOfThoughtContextValue {
21
+ isOpen: boolean;
22
+ setIsOpen: (open: boolean) => void;
23
+ }
24
+
25
+ const ChainOfThoughtContext = createContext<ChainOfThoughtContextValue | null>(
26
+ null
27
+ );
28
+
29
+ const useChainOfThought = () => {
30
+ const context = useContext(ChainOfThoughtContext);
31
+ if (!context) {
32
+ throw new Error(
33
+ "ChainOfThought components must be used within ChainOfThought"
34
+ );
35
+ }
36
+ return context;
37
+ };
38
+
39
+ export type ChainOfThoughtProps = ComponentProps<"div"> & {
40
+ open?: boolean;
41
+ defaultOpen?: boolean;
42
+ onOpenChange?: (open: boolean) => void;
43
+ };
44
+
45
+ export const ChainOfThought = memo(
46
+ ({
47
+ className,
48
+ open,
49
+ defaultOpen = false,
50
+ onOpenChange,
51
+ children,
52
+ ...props
53
+ }: ChainOfThoughtProps) => {
54
+ const [isOpen, setIsOpen] = useControllableState({
55
+ prop: open,
56
+ defaultProp: defaultOpen,
57
+ onChange: onOpenChange,
58
+ });
59
+
60
+ const chainOfThoughtContext = useMemo(
61
+ () => ({ isOpen, setIsOpen }),
62
+ [isOpen, setIsOpen]
63
+ );
64
+
65
+ return (
66
+ <ChainOfThoughtContext.Provider value={chainOfThoughtContext}>
67
+ <div className={cn("not-prose w-full space-y-4", className)} {...props}>
68
+ {children}
69
+ </div>
70
+ </ChainOfThoughtContext.Provider>
71
+ );
72
+ }
73
+ );
74
+
75
+ export type ChainOfThoughtHeaderProps = ComponentProps<
76
+ typeof CollapsibleTrigger
77
+ >;
78
+
79
+ export const ChainOfThoughtHeader = memo(
80
+ ({ className, children, ...props }: ChainOfThoughtHeaderProps) => {
81
+ const { isOpen, setIsOpen } = useChainOfThought();
82
+
83
+ return (
84
+ <Collapsible onOpenChange={setIsOpen} open={isOpen}>
85
+ <CollapsibleTrigger
86
+ className={cn(
87
+ "flex w-full items-center gap-2 text-muted-foreground text-sm transition-colors hover:text-foreground",
88
+ className
89
+ )}
90
+ {...props}
91
+ >
92
+ <BrainIcon className="size-4" />
93
+ <span className="flex-1 text-left">
94
+ {children ?? "Chain of Thought"}
95
+ </span>
96
+ <ChevronDownIcon
97
+ className={cn(
98
+ "size-4 transition-transform",
99
+ isOpen ? "rotate-180" : "rotate-0"
100
+ )}
101
+ />
102
+ </CollapsibleTrigger>
103
+ </Collapsible>
104
+ );
105
+ }
106
+ );
107
+
108
+ export type ChainOfThoughtStepProps = ComponentProps<"div"> & {
109
+ icon?: LucideIcon;
110
+ label: ReactNode;
111
+ description?: ReactNode;
112
+ status?: "complete" | "active" | "pending";
113
+ };
114
+
115
+ export const ChainOfThoughtStep = memo(
116
+ ({
117
+ className,
118
+ icon: Icon = DotIcon,
119
+ label,
120
+ description,
121
+ status = "complete",
122
+ children,
123
+ ...props
124
+ }: ChainOfThoughtStepProps) => {
125
+ const statusStyles = {
126
+ complete: "text-muted-foreground",
127
+ active: "text-foreground",
128
+ pending: "text-muted-foreground/50",
129
+ };
130
+
131
+ return (
132
+ <div
133
+ className={cn(
134
+ "flex gap-2 text-sm",
135
+ statusStyles[status],
136
+ "fade-in-0 slide-in-from-top-2 animate-in",
137
+ className
138
+ )}
139
+ {...props}
140
+ >
141
+ <div className="relative mt-0.5">
142
+ <Icon className="size-4" />
143
+ <div className="absolute top-7 bottom-0 left-1/2 -mx-px w-px bg-border" />
144
+ </div>
145
+ <div className="flex-1 space-y-2 overflow-hidden">
146
+ <div>{label}</div>
147
+ {description && (
148
+ <div className="text-muted-foreground text-xs">{description}</div>
149
+ )}
150
+ {children}
151
+ </div>
152
+ </div>
153
+ );
154
+ }
155
+ );
156
+
157
+ export type ChainOfThoughtSearchResultsProps = ComponentProps<"div">;
158
+
159
+ export const ChainOfThoughtSearchResults = memo(
160
+ ({ className, ...props }: ChainOfThoughtSearchResultsProps) => (
161
+ <div
162
+ className={cn("flex flex-wrap items-center gap-2", className)}
163
+ {...props}
164
+ />
165
+ )
166
+ );
167
+
168
+ export type ChainOfThoughtSearchResultProps = ComponentProps<typeof Badge>;
169
+
170
+ export const ChainOfThoughtSearchResult = memo(
171
+ ({ className, children, ...props }: ChainOfThoughtSearchResultProps) => (
172
+ <Badge
173
+ className={cn("gap-1 px-2 py-0.5 font-normal text-xs", className)}
174
+ variant="secondary"
175
+ {...props}
176
+ >
177
+ {children}
178
+ </Badge>
179
+ )
180
+ );
181
+
182
+ export type ChainOfThoughtContentProps = ComponentProps<
183
+ typeof CollapsibleContent
184
+ >;
185
+
186
+ export const ChainOfThoughtContent = memo(
187
+ ({ className, children, ...props }: ChainOfThoughtContentProps) => {
188
+ const { isOpen } = useChainOfThought();
189
+
190
+ return (
191
+ <Collapsible open={isOpen}>
192
+ <CollapsibleContent
193
+ className={cn(
194
+ "mt-2 space-y-3",
195
+ "data-[state=closed]:fade-out-0 data-[state=closed]:slide-out-to-top-2 data-[state=open]:slide-in-from-top-2 text-popover-foreground outline-none data-[state=closed]:animate-out data-[state=open]:animate-in",
196
+ className
197
+ )}
198
+ {...props}
199
+ >
200
+ {children}
201
+ </CollapsibleContent>
202
+ </Collapsible>
203
+ );
204
+ }
205
+ );
206
+
207
+ export type ChainOfThoughtImageProps = ComponentProps<"div"> & {
208
+ caption?: string;
209
+ };
210
+
211
+ export const ChainOfThoughtImage = memo(
212
+ ({ className, children, caption, ...props }: ChainOfThoughtImageProps) => (
213
+ <div className={cn("mt-2 space-y-2", className)} {...props}>
214
+ <div className="relative flex max-h-[22rem] items-center justify-center overflow-hidden rounded-lg bg-muted p-3">
215
+ {children}
216
+ </div>
217
+ {caption && <p className="text-muted-foreground text-xs">{caption}</p>}
218
+ </div>
219
+ )
220
+ );
221
+
222
+ ChainOfThought.displayName = "ChainOfThought";
223
+ ChainOfThoughtHeader.displayName = "ChainOfThoughtHeader";
224
+ ChainOfThoughtStep.displayName = "ChainOfThoughtStep";
225
+ ChainOfThoughtSearchResults.displayName = "ChainOfThoughtSearchResults";
226
+ ChainOfThoughtSearchResult.displayName = "ChainOfThoughtSearchResult";
227
+ ChainOfThoughtContent.displayName = "ChainOfThoughtContent";
228
+ ChainOfThoughtImage.displayName = "ChainOfThoughtImage";
@@ -0,0 +1,71 @@
1
+ "use client";
2
+
3
+ import { Button } from "@/components/ui/button";
4
+ import { Separator } from "@/components/ui/separator";
5
+ import {
6
+ Tooltip,
7
+ TooltipContent,
8
+ TooltipTrigger,
9
+ } from "@/components/ui/tooltip";
10
+ import { cn } from "@/lib/utils";
11
+ import { BookmarkIcon, type LucideProps } from "lucide-react";
12
+ import type { ComponentProps, HTMLAttributes } from "react";
13
+
14
+ export type CheckpointProps = HTMLAttributes<HTMLDivElement>;
15
+
16
+ export const Checkpoint = ({
17
+ className,
18
+ children,
19
+ ...props
20
+ }: CheckpointProps) => (
21
+ <div
22
+ className={cn(
23
+ "flex items-center gap-0.5 overflow-hidden text-muted-foreground",
24
+ className
25
+ )}
26
+ {...props}
27
+ >
28
+ {children}
29
+ <Separator />
30
+ </div>
31
+ );
32
+
33
+ export type CheckpointIconProps = LucideProps;
34
+
35
+ export const CheckpointIcon = ({
36
+ className,
37
+ children,
38
+ ...props
39
+ }: CheckpointIconProps) =>
40
+ children ?? (
41
+ <BookmarkIcon className={cn("size-4 shrink-0", className)} {...props} />
42
+ );
43
+
44
+ export type CheckpointTriggerProps = ComponentProps<typeof Button> & {
45
+ tooltip?: string;
46
+ };
47
+
48
+ export const CheckpointTrigger = ({
49
+ children,
50
+ className,
51
+ variant = "ghost",
52
+ size = "sm",
53
+ tooltip,
54
+ ...props
55
+ }: CheckpointTriggerProps) =>
56
+ tooltip ? (
57
+ <Tooltip>
58
+ <TooltipTrigger asChild>
59
+ <Button size={size} type="button" variant={variant} {...props}>
60
+ {children}
61
+ </Button>
62
+ </TooltipTrigger>
63
+ <TooltipContent align="start" side="bottom">
64
+ {tooltip}
65
+ </TooltipContent>
66
+ </Tooltip>
67
+ ) : (
68
+ <Button size={size} type="button" variant={variant} {...props}>
69
+ {children}
70
+ </Button>
71
+ );