@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,287 @@
1
+ "use client";
2
+
3
+ import { Badge } from "@/components/ui/badge";
4
+ import {
5
+ Carousel,
6
+ type CarouselApi,
7
+ CarouselContent,
8
+ CarouselItem,
9
+ } from "@/components/ui/carousel";
10
+ import {
11
+ HoverCard,
12
+ HoverCardContent,
13
+ HoverCardTrigger,
14
+ } from "@/components/ui/hover-card";
15
+ import { cn } from "@/lib/utils";
16
+ import { ArrowLeftIcon, ArrowRightIcon } from "lucide-react";
17
+ import {
18
+ type ComponentProps,
19
+ createContext,
20
+ useCallback,
21
+ useContext,
22
+ useEffect,
23
+ useState,
24
+ } from "react";
25
+
26
+ export type InlineCitationProps = ComponentProps<"span">;
27
+
28
+ export const InlineCitation = ({
29
+ className,
30
+ ...props
31
+ }: InlineCitationProps) => (
32
+ <span
33
+ className={cn("group inline items-center gap-1", className)}
34
+ {...props}
35
+ />
36
+ );
37
+
38
+ export type InlineCitationTextProps = ComponentProps<"span">;
39
+
40
+ export const InlineCitationText = ({
41
+ className,
42
+ ...props
43
+ }: InlineCitationTextProps) => (
44
+ <span
45
+ className={cn("transition-colors group-hover:bg-accent", className)}
46
+ {...props}
47
+ />
48
+ );
49
+
50
+ export type InlineCitationCardProps = ComponentProps<typeof HoverCard>;
51
+
52
+ export const InlineCitationCard = (props: InlineCitationCardProps) => (
53
+ <HoverCard closeDelay={0} openDelay={0} {...props} />
54
+ );
55
+
56
+ export type InlineCitationCardTriggerProps = ComponentProps<typeof Badge> & {
57
+ sources: string[];
58
+ };
59
+
60
+ export const InlineCitationCardTrigger = ({
61
+ sources,
62
+ className,
63
+ ...props
64
+ }: InlineCitationCardTriggerProps) => (
65
+ <HoverCardTrigger asChild>
66
+ <Badge
67
+ className={cn("ml-1 rounded-full", className)}
68
+ variant="secondary"
69
+ {...props}
70
+ >
71
+ {sources[0] ? (
72
+ <>
73
+ {new URL(sources[0]).hostname}{" "}
74
+ {sources.length > 1 && `+${sources.length - 1}`}
75
+ </>
76
+ ) : (
77
+ "unknown"
78
+ )}
79
+ </Badge>
80
+ </HoverCardTrigger>
81
+ );
82
+
83
+ export type InlineCitationCardBodyProps = ComponentProps<"div">;
84
+
85
+ export const InlineCitationCardBody = ({
86
+ className,
87
+ ...props
88
+ }: InlineCitationCardBodyProps) => (
89
+ <HoverCardContent className={cn("relative w-80 p-0", className)} {...props} />
90
+ );
91
+
92
+ const CarouselApiContext = createContext<CarouselApi | undefined>(undefined);
93
+
94
+ const useCarouselApi = () => {
95
+ const context = useContext(CarouselApiContext);
96
+ return context;
97
+ };
98
+
99
+ export type InlineCitationCarouselProps = ComponentProps<typeof Carousel>;
100
+
101
+ export const InlineCitationCarousel = ({
102
+ className,
103
+ children,
104
+ ...props
105
+ }: InlineCitationCarouselProps) => {
106
+ const [api, setApi] = useState<CarouselApi>();
107
+
108
+ return (
109
+ <CarouselApiContext.Provider value={api}>
110
+ <Carousel className={cn("w-full", className)} setApi={setApi} {...props}>
111
+ {children}
112
+ </Carousel>
113
+ </CarouselApiContext.Provider>
114
+ );
115
+ };
116
+
117
+ export type InlineCitationCarouselContentProps = ComponentProps<"div">;
118
+
119
+ export const InlineCitationCarouselContent = (
120
+ props: InlineCitationCarouselContentProps
121
+ ) => <CarouselContent {...props} />;
122
+
123
+ export type InlineCitationCarouselItemProps = ComponentProps<"div">;
124
+
125
+ export const InlineCitationCarouselItem = ({
126
+ className,
127
+ ...props
128
+ }: InlineCitationCarouselItemProps) => (
129
+ <CarouselItem
130
+ className={cn("w-full space-y-2 p-4 pl-8", className)}
131
+ {...props}
132
+ />
133
+ );
134
+
135
+ export type InlineCitationCarouselHeaderProps = ComponentProps<"div">;
136
+
137
+ export const InlineCitationCarouselHeader = ({
138
+ className,
139
+ ...props
140
+ }: InlineCitationCarouselHeaderProps) => (
141
+ <div
142
+ className={cn(
143
+ "flex items-center justify-between gap-2 rounded-t-md bg-secondary p-2",
144
+ className
145
+ )}
146
+ {...props}
147
+ />
148
+ );
149
+
150
+ export type InlineCitationCarouselIndexProps = ComponentProps<"div">;
151
+
152
+ export const InlineCitationCarouselIndex = ({
153
+ children,
154
+ className,
155
+ ...props
156
+ }: InlineCitationCarouselIndexProps) => {
157
+ const api = useCarouselApi();
158
+ const [current, setCurrent] = useState(0);
159
+ const [count, setCount] = useState(0);
160
+
161
+ useEffect(() => {
162
+ if (!api) {
163
+ return;
164
+ }
165
+
166
+ setCount(api.scrollSnapList().length);
167
+ setCurrent(api.selectedScrollSnap() + 1);
168
+
169
+ api.on("select", () => {
170
+ setCurrent(api.selectedScrollSnap() + 1);
171
+ });
172
+ }, [api]);
173
+
174
+ return (
175
+ <div
176
+ className={cn(
177
+ "flex flex-1 items-center justify-end px-3 py-1 text-muted-foreground text-xs",
178
+ className
179
+ )}
180
+ {...props}
181
+ >
182
+ {children ?? `${current}/${count}`}
183
+ </div>
184
+ );
185
+ };
186
+
187
+ export type InlineCitationCarouselPrevProps = ComponentProps<"button">;
188
+
189
+ export const InlineCitationCarouselPrev = ({
190
+ className,
191
+ ...props
192
+ }: InlineCitationCarouselPrevProps) => {
193
+ const api = useCarouselApi();
194
+
195
+ const handleClick = useCallback(() => {
196
+ if (api) {
197
+ api.scrollPrev();
198
+ }
199
+ }, [api]);
200
+
201
+ return (
202
+ <button
203
+ aria-label="Previous"
204
+ className={cn("shrink-0", className)}
205
+ onClick={handleClick}
206
+ type="button"
207
+ {...props}
208
+ >
209
+ <ArrowLeftIcon className="size-4 text-muted-foreground" />
210
+ </button>
211
+ );
212
+ };
213
+
214
+ export type InlineCitationCarouselNextProps = ComponentProps<"button">;
215
+
216
+ export const InlineCitationCarouselNext = ({
217
+ className,
218
+ ...props
219
+ }: InlineCitationCarouselNextProps) => {
220
+ const api = useCarouselApi();
221
+
222
+ const handleClick = useCallback(() => {
223
+ if (api) {
224
+ api.scrollNext();
225
+ }
226
+ }, [api]);
227
+
228
+ return (
229
+ <button
230
+ aria-label="Next"
231
+ className={cn("shrink-0", className)}
232
+ onClick={handleClick}
233
+ type="button"
234
+ {...props}
235
+ >
236
+ <ArrowRightIcon className="size-4 text-muted-foreground" />
237
+ </button>
238
+ );
239
+ };
240
+
241
+ export type InlineCitationSourceProps = ComponentProps<"div"> & {
242
+ title?: string;
243
+ url?: string;
244
+ description?: string;
245
+ };
246
+
247
+ export const InlineCitationSource = ({
248
+ title,
249
+ url,
250
+ description,
251
+ className,
252
+ children,
253
+ ...props
254
+ }: InlineCitationSourceProps) => (
255
+ <div className={cn("space-y-1", className)} {...props}>
256
+ {title && (
257
+ <h4 className="truncate font-medium text-sm leading-tight">{title}</h4>
258
+ )}
259
+ {url && (
260
+ <p className="truncate break-all text-muted-foreground text-xs">{url}</p>
261
+ )}
262
+ {description && (
263
+ <p className="line-clamp-3 text-muted-foreground text-sm leading-relaxed">
264
+ {description}
265
+ </p>
266
+ )}
267
+ {children}
268
+ </div>
269
+ );
270
+
271
+ export type InlineCitationQuoteProps = ComponentProps<"blockquote">;
272
+
273
+ export const InlineCitationQuote = ({
274
+ children,
275
+ className,
276
+ ...props
277
+ }: InlineCitationQuoteProps) => (
278
+ <blockquote
279
+ className={cn(
280
+ "border-muted border-l-2 pl-3 text-muted-foreground text-sm italic",
281
+ className
282
+ )}
283
+ {...props}
284
+ >
285
+ {children}
286
+ </blockquote>
287
+ );
@@ -0,0 +1,336 @@
1
+ "use client";
2
+
3
+ import { Button } from "@/components/ui/button";
4
+ import { ButtonGroup, ButtonGroupText } from "@/components/ui/button-group";
5
+ import {
6
+ Tooltip,
7
+ TooltipContent,
8
+ TooltipProvider,
9
+ TooltipTrigger,
10
+ } from "@/components/ui/tooltip";
11
+ import { cn } from "@/lib/utils";
12
+ import { cjk } from "@streamdown/cjk";
13
+ import { code } from "@streamdown/code";
14
+ import { math } from "@streamdown/math";
15
+ import { mermaid } from "@streamdown/mermaid";
16
+ import type { UIMessage } from "ai";
17
+ import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react";
18
+ import type { ComponentProps, HTMLAttributes, ReactElement } from "react";
19
+ import { createContext, memo, useContext, useEffect, useState } from "react";
20
+ import { Streamdown } from "streamdown";
21
+
22
+ export type MessageProps = HTMLAttributes<HTMLDivElement> & {
23
+ from: UIMessage["role"];
24
+ };
25
+
26
+ export const Message = ({ className, from, ...props }: MessageProps) => (
27
+ <div
28
+ className={cn(
29
+ "group flex w-full max-w-[95%] flex-col gap-2",
30
+ from === "user" ? "is-user ml-auto justify-end" : "is-assistant",
31
+ className
32
+ )}
33
+ {...props}
34
+ />
35
+ );
36
+
37
+ export type MessageContentProps = HTMLAttributes<HTMLDivElement>;
38
+
39
+ export const MessageContent = ({
40
+ children,
41
+ className,
42
+ ...props
43
+ }: MessageContentProps) => (
44
+ <div
45
+ className={cn(
46
+ "is-user:dark flex w-fit min-w-0 max-w-full flex-col gap-2 overflow-hidden text-sm",
47
+ "group-[.is-user]:ml-auto group-[.is-user]:rounded-lg group-[.is-user]:bg-secondary group-[.is-user]:px-4 group-[.is-user]:py-3 group-[.is-user]:text-foreground",
48
+ "group-[.is-assistant]:text-foreground",
49
+ className
50
+ )}
51
+ {...props}
52
+ >
53
+ {children}
54
+ </div>
55
+ );
56
+
57
+ export type MessageActionsProps = ComponentProps<"div">;
58
+
59
+ export const MessageActions = ({
60
+ className,
61
+ children,
62
+ ...props
63
+ }: MessageActionsProps) => (
64
+ <div className={cn("flex items-center gap-1", className)} {...props}>
65
+ {children}
66
+ </div>
67
+ );
68
+
69
+ export type MessageActionProps = ComponentProps<typeof Button> & {
70
+ tooltip?: string;
71
+ label?: string;
72
+ };
73
+
74
+ export const MessageAction = ({
75
+ tooltip,
76
+ children,
77
+ label,
78
+ variant = "ghost",
79
+ size = "icon-sm",
80
+ ...props
81
+ }: MessageActionProps) => {
82
+ const button = (
83
+ <Button size={size} type="button" variant={variant} {...props}>
84
+ {children}
85
+ <span className="sr-only">{label || tooltip}</span>
86
+ </Button>
87
+ );
88
+
89
+ if (tooltip) {
90
+ return (
91
+ <TooltipProvider>
92
+ <Tooltip>
93
+ <TooltipTrigger asChild>{button}</TooltipTrigger>
94
+ <TooltipContent>
95
+ <p>{tooltip}</p>
96
+ </TooltipContent>
97
+ </Tooltip>
98
+ </TooltipProvider>
99
+ );
100
+ }
101
+
102
+ return button;
103
+ };
104
+
105
+ interface MessageBranchContextType {
106
+ currentBranch: number;
107
+ totalBranches: number;
108
+ goToPrevious: () => void;
109
+ goToNext: () => void;
110
+ branches: ReactElement[];
111
+ setBranches: (branches: ReactElement[]) => void;
112
+ }
113
+
114
+ const MessageBranchContext = createContext<MessageBranchContextType | null>(
115
+ null
116
+ );
117
+
118
+ const useMessageBranch = () => {
119
+ const context = useContext(MessageBranchContext);
120
+
121
+ if (!context) {
122
+ throw new Error(
123
+ "MessageBranch components must be used within MessageBranch"
124
+ );
125
+ }
126
+
127
+ return context;
128
+ };
129
+
130
+ export type MessageBranchProps = HTMLAttributes<HTMLDivElement> & {
131
+ defaultBranch?: number;
132
+ onBranchChange?: (branchIndex: number) => void;
133
+ };
134
+
135
+ export const MessageBranch = ({
136
+ defaultBranch = 0,
137
+ onBranchChange,
138
+ className,
139
+ ...props
140
+ }: MessageBranchProps) => {
141
+ const [currentBranch, setCurrentBranch] = useState(defaultBranch);
142
+ const [branches, setBranches] = useState<ReactElement[]>([]);
143
+
144
+ const handleBranchChange = (newBranch: number) => {
145
+ setCurrentBranch(newBranch);
146
+ onBranchChange?.(newBranch);
147
+ };
148
+
149
+ const goToPrevious = () => {
150
+ const newBranch =
151
+ currentBranch > 0 ? currentBranch - 1 : branches.length - 1;
152
+ handleBranchChange(newBranch);
153
+ };
154
+
155
+ const goToNext = () => {
156
+ const newBranch =
157
+ currentBranch < branches.length - 1 ? currentBranch + 1 : 0;
158
+ handleBranchChange(newBranch);
159
+ };
160
+
161
+ const contextValue: MessageBranchContextType = {
162
+ currentBranch,
163
+ totalBranches: branches.length,
164
+ goToPrevious,
165
+ goToNext,
166
+ branches,
167
+ setBranches,
168
+ };
169
+
170
+ return (
171
+ <MessageBranchContext.Provider value={contextValue}>
172
+ <div
173
+ className={cn("grid w-full gap-2 [&>div]:pb-0", className)}
174
+ {...props}
175
+ />
176
+ </MessageBranchContext.Provider>
177
+ );
178
+ };
179
+
180
+ export type MessageBranchContentProps = HTMLAttributes<HTMLDivElement>;
181
+
182
+ export const MessageBranchContent = ({
183
+ children,
184
+ ...props
185
+ }: MessageBranchContentProps) => {
186
+ const { currentBranch, setBranches, branches } = useMessageBranch();
187
+ const childrenArray = Array.isArray(children) ? children : [children];
188
+
189
+ // Use useEffect to update branches when they change
190
+ useEffect(() => {
191
+ if (branches.length !== childrenArray.length) {
192
+ setBranches(childrenArray);
193
+ }
194
+ }, [childrenArray, branches, setBranches]);
195
+
196
+ return childrenArray.map((branch, index) => (
197
+ <div
198
+ className={cn(
199
+ "grid gap-2 overflow-hidden [&>div]:pb-0",
200
+ index === currentBranch ? "block" : "hidden"
201
+ )}
202
+ key={branch.key}
203
+ {...props}
204
+ >
205
+ {branch}
206
+ </div>
207
+ ));
208
+ };
209
+
210
+ export type MessageBranchSelectorProps = HTMLAttributes<HTMLDivElement> & {
211
+ from: UIMessage["role"];
212
+ };
213
+
214
+ export const MessageBranchSelector = ({
215
+ className,
216
+ from,
217
+ ...props
218
+ }: MessageBranchSelectorProps) => {
219
+ const { totalBranches } = useMessageBranch();
220
+
221
+ // Don't render if there's only one branch
222
+ if (totalBranches <= 1) {
223
+ return null;
224
+ }
225
+
226
+ return (
227
+ <ButtonGroup
228
+ className="[&>*:not(:first-child)]:rounded-l-md [&>*:not(:last-child)]:rounded-r-md"
229
+ orientation="horizontal"
230
+ {...props}
231
+ />
232
+ );
233
+ };
234
+
235
+ export type MessageBranchPreviousProps = ComponentProps<typeof Button>;
236
+
237
+ export const MessageBranchPrevious = ({
238
+ children,
239
+ ...props
240
+ }: MessageBranchPreviousProps) => {
241
+ const { goToPrevious, totalBranches } = useMessageBranch();
242
+
243
+ return (
244
+ <Button
245
+ aria-label="Previous branch"
246
+ disabled={totalBranches <= 1}
247
+ onClick={goToPrevious}
248
+ size="icon-sm"
249
+ type="button"
250
+ variant="ghost"
251
+ {...props}
252
+ >
253
+ {children ?? <ChevronLeftIcon size={14} />}
254
+ </Button>
255
+ );
256
+ };
257
+
258
+ export type MessageBranchNextProps = ComponentProps<typeof Button>;
259
+
260
+ export const MessageBranchNext = ({
261
+ children,
262
+ ...props
263
+ }: MessageBranchNextProps) => {
264
+ const { goToNext, totalBranches } = useMessageBranch();
265
+
266
+ return (
267
+ <Button
268
+ aria-label="Next branch"
269
+ disabled={totalBranches <= 1}
270
+ onClick={goToNext}
271
+ size="icon-sm"
272
+ type="button"
273
+ variant="ghost"
274
+ {...props}
275
+ >
276
+ {children ?? <ChevronRightIcon size={14} />}
277
+ </Button>
278
+ );
279
+ };
280
+
281
+ export type MessageBranchPageProps = HTMLAttributes<HTMLSpanElement>;
282
+
283
+ export const MessageBranchPage = ({
284
+ className,
285
+ ...props
286
+ }: MessageBranchPageProps) => {
287
+ const { currentBranch, totalBranches } = useMessageBranch();
288
+
289
+ return (
290
+ <ButtonGroupText
291
+ className={cn(
292
+ "border-none bg-transparent text-muted-foreground shadow-none",
293
+ className
294
+ )}
295
+ {...props}
296
+ >
297
+ {currentBranch + 1} of {totalBranches}
298
+ </ButtonGroupText>
299
+ );
300
+ };
301
+
302
+ export type MessageResponseProps = ComponentProps<typeof Streamdown>;
303
+
304
+ export const MessageResponse = memo(
305
+ ({ className, ...props }: MessageResponseProps) => (
306
+ <Streamdown
307
+ className={cn(
308
+ "size-full [&>*:first-child]:mt-0 [&>*:last-child]:mb-0",
309
+ className
310
+ )}
311
+ plugins={{ code, mermaid, math, cjk }}
312
+ {...props}
313
+ />
314
+ ),
315
+ (prevProps, nextProps) => prevProps.children === nextProps.children
316
+ );
317
+
318
+ MessageResponse.displayName = "MessageResponse";
319
+
320
+ export type MessageToolbarProps = ComponentProps<"div">;
321
+
322
+ export const MessageToolbar = ({
323
+ className,
324
+ children,
325
+ ...props
326
+ }: MessageToolbarProps) => (
327
+ <div
328
+ className={cn(
329
+ "mt-4 flex w-full items-center justify-between gap-4",
330
+ className
331
+ )}
332
+ {...props}
333
+ >
334
+ {children}
335
+ </div>
336
+ );