@chat-js/cli 0.6.1 → 0.6.2

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 (153) hide show
  1. package/dist/index.js +16938 -16786
  2. package/package.json +1 -1
  3. package/templates/chat-app/app/(auth)/login/page.tsx +3 -3
  4. package/templates/chat-app/app/(chat)/api/chat/route.ts +4 -60
  5. package/templates/chat-app/app/not-found.tsx +2 -2
  6. package/templates/chat-app/chat.config.ts +3 -0
  7. package/templates/chat-app/components/ai-elements/actions.tsx +44 -44
  8. package/templates/chat-app/components/ai-elements/artifact.tsx +92 -92
  9. package/templates/chat-app/components/ai-elements/code-block.tsx +143 -143
  10. package/templates/chat-app/components/ai-elements/context.tsx +313 -313
  11. package/templates/chat-app/components/ai-elements/conversation.tsx +65 -65
  12. package/templates/chat-app/components/ai-elements/extra/conversation-content-scroll-area.tsx +29 -29
  13. package/templates/chat-app/components/ai-elements/extra/mcp-tool-header.tsx +27 -27
  14. package/templates/chat-app/components/ai-elements/message.tsx +341 -344
  15. package/templates/chat-app/components/ai-elements/parseIncompleteMarkdown.tsx +122 -122
  16. package/templates/chat-app/components/ai-elements/prompt-input.tsx +1059 -1059
  17. package/templates/chat-app/components/ai-elements/reasoning.tsx +131 -131
  18. package/templates/chat-app/components/ai-elements/response.tsx +15 -12
  19. package/templates/chat-app/components/ai-elements/sandbox.tsx +84 -84
  20. package/templates/chat-app/components/ai-elements/shimmer.tsx +47 -47
  21. package/templates/chat-app/components/ai-elements/suggestion.tsx +33 -33
  22. package/templates/chat-app/components/ai-elements/tool.tsx +118 -118
  23. package/templates/chat-app/components/app-sidebar-history-conditional.tsx +3 -3
  24. package/templates/chat-app/components/app-sidebar.tsx +3 -3
  25. package/templates/chat-app/components/connectors-dropdown.tsx +6 -3
  26. package/templates/chat-app/components/deep-research-progress.tsx +1 -1
  27. package/templates/chat-app/components/header-breadcrumb.tsx +14 -11
  28. package/templates/chat-app/components/internal-link.tsx +73 -0
  29. package/templates/chat-app/components/login-form.tsx +5 -5
  30. package/templates/chat-app/components/message-parts.tsx +1 -71
  31. package/templates/chat-app/components/model-selector.tsx +3 -3
  32. package/templates/chat-app/components/new-chat-button.tsx +4 -4
  33. package/templates/chat-app/components/part/document-common.tsx +3 -3
  34. package/templates/chat-app/components/part/document-tool.tsx +3 -3
  35. package/templates/chat-app/components/part/message-annotations.tsx +2 -2
  36. package/templates/chat-app/components/part/tool-part.tsx +92 -0
  37. package/templates/chat-app/components/project-chat-item.tsx +2 -2
  38. package/templates/chat-app/components/research-progress.tsx +2 -2
  39. package/templates/chat-app/components/research-task.tsx +1 -1
  40. package/templates/chat-app/components/research-tasks.tsx +1 -1
  41. package/templates/chat-app/components/settings/connectors-settings.tsx +4 -4
  42. package/templates/chat-app/components/settings/mcp-details-page.tsx +5 -5
  43. package/templates/chat-app/components/settings/settings-nav.tsx +3 -3
  44. package/templates/chat-app/components/sidebar-chat-item.tsx +4 -12
  45. package/templates/chat-app/components/sidebar-project-item.tsx +4 -11
  46. package/templates/chat-app/components/sidebar-top-row.tsx +7 -7
  47. package/templates/chat-app/components/sidebar-user-nav.tsx +3 -3
  48. package/templates/chat-app/components/signup-form.tsx +8 -5
  49. package/templates/chat-app/components/source-badge.tsx +3 -9
  50. package/templates/chat-app/components/sources.tsx +1 -1
  51. package/templates/chat-app/components/ui/accordion.tsx +32 -32
  52. package/templates/chat-app/components/ui/alert-dialog.tsx +103 -103
  53. package/templates/chat-app/components/ui/alert.tsx +36 -36
  54. package/templates/chat-app/components/ui/avatar.tsx +28 -28
  55. package/templates/chat-app/components/ui/badge.tsx +22 -22
  56. package/templates/chat-app/components/ui/breadcrumb.tsx +72 -72
  57. package/templates/chat-app/components/ui/button-group.tsx +58 -58
  58. package/templates/chat-app/components/ui/button.tsx +45 -45
  59. package/templates/chat-app/components/ui/card.tsx +65 -65
  60. package/templates/chat-app/components/ui/checkbox.tsx +16 -16
  61. package/templates/chat-app/components/ui/collapsible.tsx +1 -1
  62. package/templates/chat-app/components/ui/command.tsx +137 -137
  63. package/templates/chat-app/components/ui/dialog.tsx +94 -94
  64. package/templates/chat-app/components/ui/drawer.tsx +68 -68
  65. package/templates/chat-app/components/ui/dropdown-menu.tsx +184 -184
  66. package/templates/chat-app/components/ui/empty.tsx +76 -76
  67. package/templates/chat-app/components/ui/extra/action-container.tsx +3 -3
  68. package/templates/chat-app/components/ui/extra/scroll-area-viewport-ref.tsx +24 -24
  69. package/templates/chat-app/components/ui/form.tsx +112 -112
  70. package/templates/chat-app/components/ui/hover-card.tsx +25 -25
  71. package/templates/chat-app/components/ui/input-group.tsx +126 -126
  72. package/templates/chat-app/components/ui/input.tsx +13 -13
  73. package/templates/chat-app/components/ui/label.tsx +12 -12
  74. package/templates/chat-app/components/ui/popover.tsx +25 -25
  75. package/templates/chat-app/components/ui/progress.tsx +19 -19
  76. package/templates/chat-app/components/ui/resizable.tsx +27 -27
  77. package/templates/chat-app/components/ui/scroll-area.tsx +30 -30
  78. package/templates/chat-app/components/ui/select.tsx +108 -108
  79. package/templates/chat-app/components/ui/separator.tsx +16 -16
  80. package/templates/chat-app/components/ui/sheet.tsx +91 -91
  81. package/templates/chat-app/components/ui/sidebar.tsx +615 -615
  82. package/templates/chat-app/components/ui/skeleton.tsx +7 -7
  83. package/templates/chat-app/components/ui/slider.tsx +50 -50
  84. package/templates/chat-app/components/ui/spinner.tsx +8 -8
  85. package/templates/chat-app/components/ui/switch.tsx +16 -16
  86. package/templates/chat-app/components/ui/table.tsx +71 -71
  87. package/templates/chat-app/components/ui/tabs.tsx +31 -31
  88. package/templates/chat-app/components/ui/textarea.tsx +10 -10
  89. package/templates/chat-app/components/ui/toggle.tsx +31 -31
  90. package/templates/chat-app/components/ui/tooltip.tsx +48 -48
  91. package/templates/chat-app/components/upgrade-cta/limit-display.tsx +7 -7
  92. package/templates/chat-app/components/upgrade-cta/login-cta-banner.tsx +3 -3
  93. package/templates/chat-app/components/upgrade-cta/login-prompt.tsx +3 -3
  94. package/templates/chat-app/hooks/use-mobile.ts +13 -13
  95. package/templates/chat-app/lib/ai/core-chat-agent.ts +25 -14
  96. package/templates/chat-app/lib/ai/eval-agent.ts +4 -5
  97. package/templates/chat-app/lib/ai/installed-tools.ts +12 -0
  98. package/templates/chat-app/lib/ai/mcp/mcp-client.ts +2 -2
  99. package/templates/chat-app/lib/ai/models.generated.ts +4236 -4585
  100. package/templates/chat-app/lib/ai/tool-renderer-registry.ts +31 -0
  101. package/templates/chat-app/lib/ai/types.ts +15 -20
  102. package/templates/chat-app/lib/config-requirements.ts +11 -6
  103. package/templates/chat-app/lib/config-schema.ts +13 -0
  104. package/templates/chat-app/lib/stores/hooks-message-parts.ts +1 -1
  105. package/templates/chat-app/lib/utils.ts +157 -157
  106. package/templates/chat-app/package.json +1 -1
  107. package/templates/chat-app/scripts/check-env.ts +229 -2
  108. package/templates/chat-app/tools/chatjs/_shared/lib/tool-part.ts +5 -0
  109. package/templates/chat-app/{components/part/weather.tsx → tools/chatjs/get-weather/renderer.tsx} +24 -38
  110. package/templates/chat-app/{components/part/retrieve-url.tsx → tools/chatjs/retrieve-url/renderer.tsx} +20 -15
  111. package/templates/chat-app/{lib/ai/tools/retrieve-url.ts → tools/chatjs/retrieve-url/tool.ts} +46 -7
  112. package/templates/chat-app/tools/chatjs/tools.ts +16 -0
  113. package/templates/chat-app/tools/chatjs/ui.ts +17 -0
  114. package/templates/chat-app/tools/chatjs/word-count/renderer.tsx +50 -0
  115. package/templates/chat-app/tools/chatjs/word-count/tool.ts +30 -0
  116. package/templates/chat-app/{lib/ai/tools → tools/platform}/code-execution.ts +3 -5
  117. package/templates/chat-app/{lib/ai/tools → tools/platform}/deep-research/deep-research.ts +2 -3
  118. package/templates/chat-app/{lib/ai/tools → tools/platform}/deep-research/pipeline.ts +1 -1
  119. package/templates/chat-app/{lib/ai/tools → tools/platform}/deep-research/types.ts +1 -1
  120. package/templates/chat-app/{lib/ai/tools → tools/platform}/deep-research/utils.ts +7 -7
  121. package/templates/chat-app/{lib/ai/tools → tools/platform}/documents/types.ts +1 -1
  122. package/templates/chat-app/{lib/ai/tools → tools/platform}/generate-video.ts +4 -6
  123. package/templates/chat-app/{lib/ai/tools → tools/platform}/read-document.ts +2 -2
  124. package/templates/chat-app/{lib/ai/tools → tools/platform}/steps/multi-query-web-search.ts +1 -1
  125. package/templates/chat-app/{lib/ai/tools → tools/platform}/steps/web-search.ts +1 -1
  126. package/templates/chat-app/{lib/ai/tools → tools/platform}/tools.ts +20 -20
  127. package/templates/chat-app/{lib/ai/tools → tools/platform}/web-search.ts +7 -5
  128. package/templates/electron/CHANGELOG.md +7 -1
  129. package/templates/electron/package.json +1 -1
  130. package/templates/chat-app/lib/ai/tools/tools-definitions.ts +0 -83
  131. /package/templates/chat-app/{lib/ai/tools/get-weather.ts → tools/chatjs/get-weather/tool.ts} +0 -0
  132. /package/templates/chat-app/{lib/ai/tools → tools/platform}/code-execution.javascript.ts +0 -0
  133. /package/templates/chat-app/{lib/ai/tools → tools/platform}/code-execution.python.ts +0 -0
  134. /package/templates/chat-app/{lib/ai/tools → tools/platform}/code-execution.shared.test.ts +0 -0
  135. /package/templates/chat-app/{lib/ai/tools → tools/platform}/code-execution.shared.ts +0 -0
  136. /package/templates/chat-app/{lib/ai/tools → tools/platform}/code-execution.types.ts +0 -0
  137. /package/templates/chat-app/{lib/ai/tools → tools/platform}/deep-research/configuration.ts +0 -0
  138. /package/templates/chat-app/{lib/ai/tools → tools/platform}/deep-research/prompts.ts +0 -0
  139. /package/templates/chat-app/{lib/ai/tools → tools/platform}/deep-research/researcher-agent.ts +0 -0
  140. /package/templates/chat-app/{lib/ai/tools → tools/platform}/deep-research/supervisor-agent.ts +0 -0
  141. /package/templates/chat-app/{lib/ai/tools → tools/platform}/documents/code-guidelines.ts +0 -0
  142. /package/templates/chat-app/{lib/ai/tools → tools/platform}/documents/create-code-document.ts +0 -0
  143. /package/templates/chat-app/{lib/ai/tools → tools/platform}/documents/create-sheet-document.ts +0 -0
  144. /package/templates/chat-app/{lib/ai/tools → tools/platform}/documents/create-text-document.ts +0 -0
  145. /package/templates/chat-app/{lib/ai/tools → tools/platform}/documents/edit-code-document.ts +0 -0
  146. /package/templates/chat-app/{lib/ai/tools → tools/platform}/documents/edit-sheet-document.ts +0 -0
  147. /package/templates/chat-app/{lib/ai/tools → tools/platform}/documents/edit-text-document.ts +0 -0
  148. /package/templates/chat-app/{lib/ai/tools → tools/platform}/documents/sheet-guidelines.ts +0 -0
  149. /package/templates/chat-app/{lib/ai/tools → tools/platform}/documents/text-guidelines.ts +0 -0
  150. /package/templates/chat-app/{lib/ai/tools → tools/platform}/generate-image.ts +0 -0
  151. /package/templates/chat-app/{lib/ai/tools → tools/platform}/research-updates-schema.ts +0 -0
  152. /package/templates/chat-app/{lib/ai/tools → tools/platform}/steps/search-utils.ts +0 -0
  153. /package/templates/chat-app/{lib/ai/tools → tools/platform}/types.ts +0 -0
@@ -1,455 +1,452 @@
1
1
  "use client";
2
2
 
3
- import { Button } from "@/components/ui/button";
4
- import {
5
- ButtonGroup,
6
- ButtonGroupText,
7
- } from "@/components/ui/button-group";
8
- import {
9
- Tooltip,
10
- TooltipContent,
11
- TooltipProvider,
12
- TooltipTrigger,
13
- } from "@/components/ui/tooltip";
14
- import { cn } from "@/lib/utils";
3
+ import { code } from "@streamdown/code";
4
+ import { math } from "@streamdown/math";
5
+ import { mermaid } from "@streamdown/mermaid";
15
6
  import type { FileUIPart, UIMessage } from "ai";
16
7
  import {
17
- ChevronLeftIcon,
18
- ChevronRightIcon,
19
- PaperclipIcon,
20
- XIcon,
8
+ ChevronLeftIcon,
9
+ ChevronRightIcon,
10
+ PaperclipIcon,
11
+ XIcon,
21
12
  } from "lucide-react";
22
13
  import type { ComponentProps, HTMLAttributes, ReactElement } from "react";
23
14
  import { createContext, memo, useContext, useEffect, useState } from "react";
24
- import { code } from "@streamdown/code";
25
- import { math } from "@streamdown/math";
26
- import { mermaid } from "@streamdown/mermaid";
27
15
  import { Streamdown } from "streamdown";
16
+ import { Button } from "@/components/ui/button";
17
+ import { ButtonGroup, ButtonGroupText } from "@/components/ui/button-group";
18
+ import {
19
+ Tooltip,
20
+ TooltipContent,
21
+ TooltipProvider,
22
+ TooltipTrigger,
23
+ } from "@/components/ui/tooltip";
24
+ import { cn } from "@/lib/utils";
28
25
  import "streamdown/styles.css";
29
26
 
30
27
  const plugins = { code, mermaid, math };
31
28
 
32
29
  export type MessageProps = HTMLAttributes<HTMLDivElement> & {
33
- from: UIMessage["role"];
30
+ from: UIMessage["role"];
34
31
  };
35
32
 
36
33
  export const Message = ({ className, from, ...props }: MessageProps) => (
37
- <div
38
- className={cn(
39
- "group flex w-full max-w-[80%] gap-2",
40
- from === "user" ? "is-user ml-auto justify-end" : "is-assistant",
41
- className
42
- )}
43
- {...props}
44
- />
34
+ <div
35
+ className={cn(
36
+ "group flex w-full max-w-[80%] gap-2",
37
+ from === "user" ? "is-user ml-auto justify-end" : "is-assistant",
38
+ className,
39
+ )}
40
+ {...props}
41
+ />
45
42
  );
46
43
 
47
44
  export type MessageContentProps = HTMLAttributes<HTMLDivElement>;
48
45
 
49
46
  export const MessageContent = ({
50
- children,
51
- className,
52
- ...props
47
+ children,
48
+ className,
49
+ ...props
53
50
  }: MessageContentProps) => (
54
- <div
55
- className={cn(
56
- "is-user:dark flex w-fit flex-col gap-2 overflow-hidden text-sm",
57
- "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",
58
- "group-[.is-assistant]:text-foreground",
59
- className
60
- )}
61
- {...props}
62
- >
63
- {children}
64
- </div>
51
+ <div
52
+ className={cn(
53
+ "is-user:dark flex w-fit flex-col gap-2 overflow-hidden text-sm",
54
+ "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",
55
+ "group-[.is-assistant]:text-foreground",
56
+ className,
57
+ )}
58
+ {...props}
59
+ >
60
+ {children}
61
+ </div>
65
62
  );
66
63
 
67
64
  export type MessageActionsProps = ComponentProps<"div">;
68
65
 
69
66
  export const MessageActions = ({
70
- className,
71
- children,
72
- ...props
67
+ className,
68
+ children,
69
+ ...props
73
70
  }: MessageActionsProps) => (
74
- <div className={cn("flex items-center gap-1", className)} {...props}>
75
- {children}
76
- </div>
71
+ <div className={cn("flex items-center gap-1", className)} {...props}>
72
+ {children}
73
+ </div>
77
74
  );
78
75
 
79
76
  export type MessageActionProps = ComponentProps<typeof Button> & {
80
- tooltip?: string;
81
- label?: string;
77
+ tooltip?: string;
78
+ label?: string;
82
79
  };
83
80
 
84
81
  export const MessageAction = ({
85
- tooltip,
86
- children,
87
- label,
88
- variant = "ghost",
89
- size = "icon-sm",
90
- ...props
82
+ tooltip,
83
+ children,
84
+ label,
85
+ variant = "ghost",
86
+ size = "icon-sm",
87
+ ...props
91
88
  }: MessageActionProps) => {
92
- const button = (
93
- <Button size={size} type="button" variant={variant} {...props}>
94
- {children}
95
- <span className="sr-only">{label || tooltip}</span>
96
- </Button>
97
- );
98
-
99
- if (tooltip) {
100
- return (
101
- <TooltipProvider>
102
- <Tooltip>
103
- <TooltipTrigger asChild>{button}</TooltipTrigger>
104
- <TooltipContent>
105
- <p>{tooltip}</p>
106
- </TooltipContent>
107
- </Tooltip>
108
- </TooltipProvider>
109
- );
110
- }
111
-
112
- return button;
89
+ const button = (
90
+ <Button size={size} type="button" variant={variant} {...props}>
91
+ {children}
92
+ <span className="sr-only">{label || tooltip}</span>
93
+ </Button>
94
+ );
95
+
96
+ if (tooltip) {
97
+ return (
98
+ <TooltipProvider>
99
+ <Tooltip>
100
+ <TooltipTrigger asChild>{button}</TooltipTrigger>
101
+ <TooltipContent>
102
+ <p>{tooltip}</p>
103
+ </TooltipContent>
104
+ </Tooltip>
105
+ </TooltipProvider>
106
+ );
107
+ }
108
+
109
+ return button;
113
110
  };
114
111
 
115
112
  type MessageBranchContextType = {
116
- currentBranch: number;
117
- totalBranches: number;
118
- goToPrevious: () => void;
119
- goToNext: () => void;
120
- branches: ReactElement[];
121
- setBranches: (branches: ReactElement[]) => void;
113
+ currentBranch: number;
114
+ totalBranches: number;
115
+ goToPrevious: () => void;
116
+ goToNext: () => void;
117
+ branches: ReactElement[];
118
+ setBranches: (branches: ReactElement[]) => void;
122
119
  };
123
120
 
124
121
  const MessageBranchContext = createContext<MessageBranchContextType | null>(
125
- null
122
+ null,
126
123
  );
127
124
 
128
125
  const useMessageBranch = () => {
129
- const context = useContext(MessageBranchContext);
126
+ const context = useContext(MessageBranchContext);
130
127
 
131
- if (!context) {
132
- throw new Error(
133
- "MessageBranch components must be used within MessageBranch"
134
- );
135
- }
128
+ if (!context) {
129
+ throw new Error(
130
+ "MessageBranch components must be used within MessageBranch",
131
+ );
132
+ }
136
133
 
137
- return context;
134
+ return context;
138
135
  };
139
136
 
140
137
  export type MessageBranchProps = HTMLAttributes<HTMLDivElement> & {
141
- defaultBranch?: number;
142
- onBranchChange?: (branchIndex: number) => void;
138
+ defaultBranch?: number;
139
+ onBranchChange?: (branchIndex: number) => void;
143
140
  };
144
141
 
145
142
  export const MessageBranch = ({
146
- defaultBranch = 0,
147
- onBranchChange,
148
- className,
149
- ...props
143
+ defaultBranch = 0,
144
+ onBranchChange,
145
+ className,
146
+ ...props
150
147
  }: MessageBranchProps) => {
151
- const [currentBranch, setCurrentBranch] = useState(defaultBranch);
152
- const [branches, setBranches] = useState<ReactElement[]>([]);
153
-
154
- const handleBranchChange = (newBranch: number) => {
155
- setCurrentBranch(newBranch);
156
- onBranchChange?.(newBranch);
157
- };
158
-
159
- const goToPrevious = () => {
160
- const newBranch =
161
- currentBranch > 0 ? currentBranch - 1 : branches.length - 1;
162
- handleBranchChange(newBranch);
163
- };
164
-
165
- const goToNext = () => {
166
- const newBranch =
167
- currentBranch < branches.length - 1 ? currentBranch + 1 : 0;
168
- handleBranchChange(newBranch);
169
- };
170
-
171
- const contextValue: MessageBranchContextType = {
172
- currentBranch,
173
- totalBranches: branches.length,
174
- goToPrevious,
175
- goToNext,
176
- branches,
177
- setBranches,
178
- };
179
-
180
- return (
181
- <MessageBranchContext.Provider value={contextValue}>
182
- <div
183
- className={cn("grid w-full gap-2 [&>div]:pb-0", className)}
184
- {...props}
185
- />
186
- </MessageBranchContext.Provider>
187
- );
148
+ const [currentBranch, setCurrentBranch] = useState(defaultBranch);
149
+ const [branches, setBranches] = useState<ReactElement[]>([]);
150
+
151
+ const handleBranchChange = (newBranch: number) => {
152
+ setCurrentBranch(newBranch);
153
+ onBranchChange?.(newBranch);
154
+ };
155
+
156
+ const goToPrevious = () => {
157
+ const newBranch =
158
+ currentBranch > 0 ? currentBranch - 1 : branches.length - 1;
159
+ handleBranchChange(newBranch);
160
+ };
161
+
162
+ const goToNext = () => {
163
+ const newBranch =
164
+ currentBranch < branches.length - 1 ? currentBranch + 1 : 0;
165
+ handleBranchChange(newBranch);
166
+ };
167
+
168
+ const contextValue: MessageBranchContextType = {
169
+ currentBranch,
170
+ totalBranches: branches.length,
171
+ goToPrevious,
172
+ goToNext,
173
+ branches,
174
+ setBranches,
175
+ };
176
+
177
+ return (
178
+ <MessageBranchContext.Provider value={contextValue}>
179
+ <div
180
+ className={cn("grid w-full gap-2 [&>div]:pb-0", className)}
181
+ {...props}
182
+ />
183
+ </MessageBranchContext.Provider>
184
+ );
188
185
  };
189
186
 
190
187
  export type MessageBranchContentProps = HTMLAttributes<HTMLDivElement>;
191
188
 
192
189
  export const MessageBranchContent = ({
193
- children,
194
- ...props
190
+ children,
191
+ ...props
195
192
  }: MessageBranchContentProps) => {
196
- const { currentBranch, setBranches, branches } = useMessageBranch();
197
- const childrenArray = Array.isArray(children) ? children : [children];
198
-
199
- // Use useEffect to update branches when they change
200
- useEffect(() => {
201
- if (branches.length !== childrenArray.length) {
202
- setBranches(childrenArray);
203
- }
204
- }, [childrenArray, branches, setBranches]);
205
-
206
- return childrenArray.map((branch, index) => (
207
- <div
208
- className={cn(
209
- "grid gap-2 overflow-hidden [&>div]:pb-0",
210
- index === currentBranch ? "block" : "hidden"
211
- )}
212
- key={branch.key}
213
- {...props}
214
- >
215
- {branch}
216
- </div>
217
- ));
193
+ const { currentBranch, setBranches, branches } = useMessageBranch();
194
+ const childrenArray = Array.isArray(children) ? children : [children];
195
+
196
+ // Use useEffect to update branches when they change
197
+ useEffect(() => {
198
+ if (branches.length !== childrenArray.length) {
199
+ setBranches(childrenArray);
200
+ }
201
+ }, [childrenArray, branches, setBranches]);
202
+
203
+ return childrenArray.map((branch, index) => (
204
+ <div
205
+ className={cn(
206
+ "grid gap-2 overflow-hidden [&>div]:pb-0",
207
+ index === currentBranch ? "block" : "hidden",
208
+ )}
209
+ key={branch.key}
210
+ {...props}
211
+ >
212
+ {branch}
213
+ </div>
214
+ ));
218
215
  };
219
216
 
220
217
  export type MessageBranchSelectorProps = HTMLAttributes<HTMLDivElement> & {
221
- from: UIMessage["role"];
218
+ from: UIMessage["role"];
222
219
  };
223
220
 
224
221
  export const MessageBranchSelector = ({
225
- className,
226
- from,
227
- ...props
222
+ className,
223
+ from,
224
+ ...props
228
225
  }: MessageBranchSelectorProps) => {
229
- const { totalBranches } = useMessageBranch();
230
-
231
- // Don't render if there's only one branch
232
- if (totalBranches <= 1) {
233
- return null;
234
- }
235
-
236
- return (
237
- <ButtonGroup
238
- className="[&>*:not(:first-child)]:rounded-l-md [&>*:not(:last-child)]:rounded-r-md"
239
- orientation="horizontal"
240
- {...props}
241
- />
242
- );
226
+ const { totalBranches } = useMessageBranch();
227
+
228
+ // Don't render if there's only one branch
229
+ if (totalBranches <= 1) {
230
+ return null;
231
+ }
232
+
233
+ return (
234
+ <ButtonGroup
235
+ className="[&>*:not(:first-child)]:rounded-l-md [&>*:not(:last-child)]:rounded-r-md"
236
+ orientation="horizontal"
237
+ {...props}
238
+ />
239
+ );
243
240
  };
244
241
 
245
242
  export type MessageBranchPreviousProps = ComponentProps<typeof Button>;
246
243
 
247
244
  export const MessageBranchPrevious = ({
248
- children,
249
- ...props
245
+ children,
246
+ ...props
250
247
  }: MessageBranchPreviousProps) => {
251
- const { goToPrevious, totalBranches } = useMessageBranch();
252
-
253
- return (
254
- <Button
255
- aria-label="Previous branch"
256
- disabled={totalBranches <= 1}
257
- onClick={goToPrevious}
258
- size="icon-sm"
259
- type="button"
260
- variant="ghost"
261
- {...props}
262
- >
263
- {children ?? <ChevronLeftIcon size={14} />}
264
- </Button>
265
- );
248
+ const { goToPrevious, totalBranches } = useMessageBranch();
249
+
250
+ return (
251
+ <Button
252
+ aria-label="Previous branch"
253
+ disabled={totalBranches <= 1}
254
+ onClick={goToPrevious}
255
+ size="icon-sm"
256
+ type="button"
257
+ variant="ghost"
258
+ {...props}
259
+ >
260
+ {children ?? <ChevronLeftIcon size={14} />}
261
+ </Button>
262
+ );
266
263
  };
267
264
 
268
265
  export type MessageBranchNextProps = ComponentProps<typeof Button>;
269
266
 
270
267
  export const MessageBranchNext = ({
271
- children,
272
- className,
273
- ...props
268
+ children,
269
+ className,
270
+ ...props
274
271
  }: MessageBranchNextProps) => {
275
- const { goToNext, totalBranches } = useMessageBranch();
276
-
277
- return (
278
- <Button
279
- aria-label="Next branch"
280
- disabled={totalBranches <= 1}
281
- onClick={goToNext}
282
- size="icon-sm"
283
- type="button"
284
- variant="ghost"
285
- {...props}
286
- >
287
- {children ?? <ChevronRightIcon size={14} />}
288
- </Button>
289
- );
272
+ const { goToNext, totalBranches } = useMessageBranch();
273
+
274
+ return (
275
+ <Button
276
+ aria-label="Next branch"
277
+ disabled={totalBranches <= 1}
278
+ onClick={goToNext}
279
+ size="icon-sm"
280
+ type="button"
281
+ variant="ghost"
282
+ {...props}
283
+ >
284
+ {children ?? <ChevronRightIcon size={14} />}
285
+ </Button>
286
+ );
290
287
  };
291
288
 
292
289
  export type MessageBranchPageProps = HTMLAttributes<HTMLSpanElement>;
293
290
 
294
291
  export const MessageBranchPage = ({
295
- className,
296
- ...props
292
+ className,
293
+ ...props
297
294
  }: MessageBranchPageProps) => {
298
- const { currentBranch, totalBranches } = useMessageBranch();
299
-
300
- return (
301
- <ButtonGroupText
302
- className={cn(
303
- "border-none bg-transparent text-muted-foreground shadow-none",
304
- className
305
- )}
306
- {...props}
307
- >
308
- {currentBranch + 1} of {totalBranches}
309
- </ButtonGroupText>
310
- );
295
+ const { currentBranch, totalBranches } = useMessageBranch();
296
+
297
+ return (
298
+ <ButtonGroupText
299
+ className={cn(
300
+ "border-none bg-transparent text-muted-foreground shadow-none",
301
+ className,
302
+ )}
303
+ {...props}
304
+ >
305
+ {currentBranch + 1} of {totalBranches}
306
+ </ButtonGroupText>
307
+ );
311
308
  };
312
309
 
313
310
  export type MessageResponseProps = ComponentProps<typeof Streamdown>;
314
311
 
315
312
  export const MessageResponse = memo(
316
- ({ className, ...props }: MessageResponseProps) => (
317
- <Streamdown
318
- className={cn(
319
- "size-full [&>*:first-child]:mt-0 [&>*:last-child]:mb-0",
320
- className
321
- )}
322
- plugins={plugins}
323
- {...props}
324
- />
325
- ),
326
- (prevProps, nextProps) => prevProps.children === nextProps.children
313
+ ({ className, ...props }: MessageResponseProps) => (
314
+ <Streamdown
315
+ className={cn(
316
+ "size-full [&>*:first-child]:mt-0 [&>*:last-child]:mb-0",
317
+ className,
318
+ )}
319
+ plugins={plugins}
320
+ {...props}
321
+ />
322
+ ),
323
+ (prevProps, nextProps) => prevProps.children === nextProps.children,
327
324
  );
328
325
 
329
326
  MessageResponse.displayName = "MessageResponse";
330
327
 
331
328
  export type MessageAttachmentProps = HTMLAttributes<HTMLDivElement> & {
332
- data: FileUIPart;
333
- className?: string;
334
- onRemove?: () => void;
329
+ data: FileUIPart;
330
+ className?: string;
331
+ onRemove?: () => void;
335
332
  };
336
333
 
337
334
  export function MessageAttachment({
338
- data,
339
- className,
340
- onRemove,
341
- ...props
335
+ data,
336
+ className,
337
+ onRemove,
338
+ ...props
342
339
  }: MessageAttachmentProps) {
343
- const filename = data.filename || "";
344
- const mediaType =
345
- data.mediaType?.startsWith("image/") && data.url ? "image" : "file";
346
- const isImage = mediaType === "image";
347
- const attachmentLabel = filename || (isImage ? "Image" : "Attachment");
348
-
349
- return (
350
- <div
351
- className={cn(
352
- "group relative size-24 overflow-hidden rounded-lg",
353
- className
354
- )}
355
- {...props}
356
- >
357
- {isImage ? (
358
- <>
359
- <img
360
- alt={filename || "attachment"}
361
- className="size-full object-cover"
362
- height={100}
363
- src={data.url}
364
- width={100}
365
- />
366
- {onRemove && (
367
- <Button
368
- aria-label="Remove attachment"
369
- className="absolute top-2 right-2 size-6 rounded-full bg-background/80 p-0 opacity-0 backdrop-blur-sm transition-opacity hover:bg-background group-hover:opacity-100 [&>svg]:size-3"
370
- onClick={(e) => {
371
- e.stopPropagation();
372
- onRemove();
373
- }}
374
- type="button"
375
- variant="ghost"
376
- >
377
- <XIcon />
378
- <span className="sr-only">Remove</span>
379
- </Button>
380
- )}
381
- </>
382
- ) : (
383
- <>
384
- <Tooltip>
385
- <TooltipTrigger asChild>
386
- <div className="flex size-full shrink-0 items-center justify-center rounded-lg bg-muted text-muted-foreground">
387
- <PaperclipIcon className="size-4" />
388
- </div>
389
- </TooltipTrigger>
390
- <TooltipContent>
391
- <p>{attachmentLabel}</p>
392
- </TooltipContent>
393
- </Tooltip>
394
- {onRemove && (
395
- <Button
396
- aria-label="Remove attachment"
397
- className="size-6 shrink-0 rounded-full p-0 opacity-0 transition-opacity hover:bg-accent group-hover:opacity-100 [&>svg]:size-3"
398
- onClick={(e) => {
399
- e.stopPropagation();
400
- onRemove();
401
- }}
402
- type="button"
403
- variant="ghost"
404
- >
405
- <XIcon />
406
- <span className="sr-only">Remove</span>
407
- </Button>
408
- )}
409
- </>
410
- )}
411
- </div>
412
- );
340
+ const filename = data.filename || "";
341
+ const mediaType =
342
+ data.mediaType?.startsWith("image/") && data.url ? "image" : "file";
343
+ const isImage = mediaType === "image";
344
+ const attachmentLabel = filename || (isImage ? "Image" : "Attachment");
345
+
346
+ return (
347
+ <div
348
+ className={cn(
349
+ "group relative size-24 overflow-hidden rounded-lg",
350
+ className,
351
+ )}
352
+ {...props}
353
+ >
354
+ {isImage ? (
355
+ <>
356
+ <img
357
+ alt={filename || "attachment"}
358
+ className="size-full object-cover"
359
+ height={100}
360
+ src={data.url}
361
+ width={100}
362
+ />
363
+ {onRemove && (
364
+ <Button
365
+ aria-label="Remove attachment"
366
+ className="absolute top-2 right-2 size-6 rounded-full bg-background/80 p-0 opacity-0 backdrop-blur-sm transition-opacity hover:bg-background group-hover:opacity-100 [&>svg]:size-3"
367
+ onClick={(e) => {
368
+ e.stopPropagation();
369
+ onRemove();
370
+ }}
371
+ type="button"
372
+ variant="ghost"
373
+ >
374
+ <XIcon />
375
+ <span className="sr-only">Remove</span>
376
+ </Button>
377
+ )}
378
+ </>
379
+ ) : (
380
+ <>
381
+ <Tooltip>
382
+ <TooltipTrigger asChild>
383
+ <div className="flex size-full shrink-0 items-center justify-center rounded-lg bg-muted text-muted-foreground">
384
+ <PaperclipIcon className="size-4" />
385
+ </div>
386
+ </TooltipTrigger>
387
+ <TooltipContent>
388
+ <p>{attachmentLabel}</p>
389
+ </TooltipContent>
390
+ </Tooltip>
391
+ {onRemove && (
392
+ <Button
393
+ aria-label="Remove attachment"
394
+ className="size-6 shrink-0 rounded-full p-0 opacity-0 transition-opacity hover:bg-accent group-hover:opacity-100 [&>svg]:size-3"
395
+ onClick={(e) => {
396
+ e.stopPropagation();
397
+ onRemove();
398
+ }}
399
+ type="button"
400
+ variant="ghost"
401
+ >
402
+ <XIcon />
403
+ <span className="sr-only">Remove</span>
404
+ </Button>
405
+ )}
406
+ </>
407
+ )}
408
+ </div>
409
+ );
413
410
  }
414
411
 
415
412
  export type MessageAttachmentsProps = ComponentProps<"div">;
416
413
 
417
414
  export function MessageAttachments({
418
- children,
419
- className,
420
- ...props
415
+ children,
416
+ className,
417
+ ...props
421
418
  }: MessageAttachmentsProps) {
422
- if (!children) {
423
- return null;
424
- }
425
-
426
- return (
427
- <div
428
- className={cn(
429
- "ml-auto flex w-fit flex-wrap items-start gap-2",
430
- className
431
- )}
432
- {...props}
433
- >
434
- {children}
435
- </div>
436
- );
419
+ if (!children) {
420
+ return null;
421
+ }
422
+
423
+ return (
424
+ <div
425
+ className={cn(
426
+ "ml-auto flex w-fit flex-wrap items-start gap-2",
427
+ className,
428
+ )}
429
+ {...props}
430
+ >
431
+ {children}
432
+ </div>
433
+ );
437
434
  }
438
435
 
439
436
  export type MessageToolbarProps = ComponentProps<"div">;
440
437
 
441
438
  export const MessageToolbar = ({
442
- className,
443
- children,
444
- ...props
439
+ className,
440
+ children,
441
+ ...props
445
442
  }: MessageToolbarProps) => (
446
- <div
447
- className={cn(
448
- "mt-4 flex w-full items-center justify-between gap-4",
449
- className
450
- )}
451
- {...props}
452
- >
453
- {children}
454
- </div>
443
+ <div
444
+ className={cn(
445
+ "mt-4 flex w-full items-center justify-between gap-4",
446
+ className,
447
+ )}
448
+ {...props}
449
+ >
450
+ {children}
451
+ </div>
455
452
  );