@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.
- package/README.md +135 -0
- package/components.json +21 -0
- package/dist/index.css +4241 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.mts +59 -0
- package/dist/index.d.ts +59 -0
- package/dist/index.js +2169 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2182 -0
- package/dist/index.mjs.map +1 -0
- package/example/.env.sample +2 -0
- package/example/App.tsx +368 -0
- package/example/app.css +1 -0
- package/example/index.html +12 -0
- package/example/main.tsx +9 -0
- package/example/vite.config.ts +34 -0
- package/package.json +75 -0
- package/postcss.config.cjs +3 -0
- package/src/components/ai-elements/agent.tsx +140 -0
- package/src/components/ai-elements/artifact.tsx +147 -0
- package/src/components/ai-elements/attachments.tsx +421 -0
- package/src/components/ai-elements/audio-player.tsx +228 -0
- package/src/components/ai-elements/canvas.tsx +22 -0
- package/src/components/ai-elements/chain-of-thought.tsx +228 -0
- package/src/components/ai-elements/checkpoint.tsx +71 -0
- package/src/components/ai-elements/code-block.tsx +532 -0
- package/src/components/ai-elements/commit.tsx +448 -0
- package/src/components/ai-elements/confirmation.tsx +176 -0
- package/src/components/ai-elements/connection.tsx +28 -0
- package/src/components/ai-elements/context.tsx +408 -0
- package/src/components/ai-elements/controls.tsx +18 -0
- package/src/components/ai-elements/conversation.tsx +100 -0
- package/src/components/ai-elements/edge.tsx +140 -0
- package/src/components/ai-elements/environment-variables.tsx +295 -0
- package/src/components/ai-elements/file-tree.tsx +258 -0
- package/src/components/ai-elements/image.tsx +24 -0
- package/src/components/ai-elements/inline-citation.tsx +287 -0
- package/src/components/ai-elements/message.tsx +336 -0
- package/src/components/ai-elements/mic-selector.tsx +370 -0
- package/src/components/ai-elements/model-selector.tsx +211 -0
- package/src/components/ai-elements/node.tsx +71 -0
- package/src/components/ai-elements/open-in-chat.tsx +365 -0
- package/src/components/ai-elements/package-info.tsx +233 -0
- package/src/components/ai-elements/panel.tsx +15 -0
- package/src/components/ai-elements/persona.tsx +270 -0
- package/src/components/ai-elements/plan.tsx +142 -0
- package/src/components/ai-elements/prompt-input.tsx +1263 -0
- package/src/components/ai-elements/queue.tsx +274 -0
- package/src/components/ai-elements/reasoning.tsx +193 -0
- package/src/components/ai-elements/sandbox.tsx +126 -0
- package/src/components/ai-elements/schema-display.tsx +458 -0
- package/src/components/ai-elements/shimmer.tsx +64 -0
- package/src/components/ai-elements/snippet.tsx +139 -0
- package/src/components/ai-elements/sources.tsx +77 -0
- package/src/components/ai-elements/speech-input.tsx +301 -0
- package/src/components/ai-elements/stack-trace.tsx +482 -0
- package/src/components/ai-elements/suggestion.tsx +53 -0
- package/src/components/ai-elements/task.tsx +87 -0
- package/src/components/ai-elements/terminal.tsx +261 -0
- package/src/components/ai-elements/test-results.tsx +485 -0
- package/src/components/ai-elements/tool.tsx +174 -0
- package/src/components/ai-elements/toolbar.tsx +16 -0
- package/src/components/ai-elements/transcription.tsx +124 -0
- package/src/components/ai-elements/voice-selector.tsx +479 -0
- package/src/components/ai-elements/web-preview.tsx +263 -0
- package/src/components/chat/Chat.tsx +178 -0
- package/src/components/chat/Input.tsx +98 -0
- package/src/components/chat/Message.tsx +276 -0
- package/src/components/chat/index.ts +2 -0
- package/src/components/index.ts +1 -0
- package/src/components/ui/accordion.tsx +64 -0
- package/src/components/ui/alert.tsx +66 -0
- package/src/components/ui/avatar.tsx +107 -0
- package/src/components/ui/badge.tsx +48 -0
- package/src/components/ui/button-group.tsx +83 -0
- package/src/components/ui/button.tsx +64 -0
- package/src/components/ui/card.tsx +92 -0
- package/src/components/ui/carousel.tsx +239 -0
- package/src/components/ui/collapsible.tsx +31 -0
- package/src/components/ui/command.tsx +184 -0
- package/src/components/ui/dialog.tsx +158 -0
- package/src/components/ui/dropdown-menu.tsx +257 -0
- package/src/components/ui/hover-card.tsx +42 -0
- package/src/components/ui/input-group.tsx +168 -0
- package/src/components/ui/input.tsx +21 -0
- package/src/components/ui/popover.tsx +87 -0
- package/src/components/ui/progress.tsx +31 -0
- package/src/components/ui/scroll-area.tsx +56 -0
- package/src/components/ui/select.tsx +190 -0
- package/src/components/ui/separator.tsx +28 -0
- package/src/components/ui/spinner.tsx +16 -0
- package/src/components/ui/switch.tsx +33 -0
- package/src/components/ui/tabs.tsx +91 -0
- package/src/components/ui/textarea.tsx +18 -0
- package/src/components/ui/tooltip.tsx +61 -0
- package/src/css/global.css +123 -0
- package/src/css/index.css +1 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/use-copy-to-clipboard.ts +31 -0
- package/src/index.ts +4 -0
- package/src/lib/utils.ts +6 -0
- package/src/locales/context.ts +8 -0
- package/src/locales/hooks.ts +20 -0
- package/src/locales/index.ts +3 -0
- package/src/locales/langs/en.ts +17 -0
- package/src/locales/langs/index.ts +12 -0
- package/src/locales/langs/zh-cn.ts +18 -0
- package/tsconfig.json +21 -0
- package/tsup.config.ts +21 -0
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useControllableState } from "@radix-ui/react-use-controllable-state";
|
|
4
|
+
import { Button } from "@/components/ui/button";
|
|
5
|
+
import {
|
|
6
|
+
Command,
|
|
7
|
+
CommandEmpty,
|
|
8
|
+
CommandInput,
|
|
9
|
+
CommandItem,
|
|
10
|
+
CommandList,
|
|
11
|
+
} from "@/components/ui/command";
|
|
12
|
+
import {
|
|
13
|
+
Popover,
|
|
14
|
+
PopoverContent,
|
|
15
|
+
PopoverTrigger,
|
|
16
|
+
} from "@/components/ui/popover";
|
|
17
|
+
import { cn } from "@/lib/utils";
|
|
18
|
+
import { ChevronsUpDownIcon } from "lucide-react";
|
|
19
|
+
import {
|
|
20
|
+
type ComponentProps,
|
|
21
|
+
createContext,
|
|
22
|
+
type ReactNode,
|
|
23
|
+
useCallback,
|
|
24
|
+
useContext,
|
|
25
|
+
useEffect,
|
|
26
|
+
useRef,
|
|
27
|
+
useState,
|
|
28
|
+
} from "react";
|
|
29
|
+
|
|
30
|
+
const deviceIdRegex = /\(([\da-fA-F]{4}:[\da-fA-F]{4})\)$/;
|
|
31
|
+
|
|
32
|
+
interface MicSelectorContextType {
|
|
33
|
+
data: MediaDeviceInfo[];
|
|
34
|
+
value: string | undefined;
|
|
35
|
+
onValueChange?: (value: string) => void;
|
|
36
|
+
open: boolean;
|
|
37
|
+
onOpenChange?: (open: boolean) => void;
|
|
38
|
+
width: number;
|
|
39
|
+
setWidth?: (width: number) => void;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const MicSelectorContext = createContext<MicSelectorContextType>({
|
|
43
|
+
data: [],
|
|
44
|
+
value: undefined,
|
|
45
|
+
onValueChange: undefined,
|
|
46
|
+
open: false,
|
|
47
|
+
onOpenChange: undefined,
|
|
48
|
+
width: 200,
|
|
49
|
+
setWidth: undefined,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
export type MicSelectorProps = ComponentProps<typeof Popover> & {
|
|
53
|
+
defaultValue?: string;
|
|
54
|
+
value?: string | undefined;
|
|
55
|
+
onValueChange?: (value: string | undefined) => void;
|
|
56
|
+
open?: boolean;
|
|
57
|
+
onOpenChange?: (open: boolean) => void;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export const MicSelector = ({
|
|
61
|
+
defaultValue,
|
|
62
|
+
value: controlledValue,
|
|
63
|
+
onValueChange: controlledOnValueChange,
|
|
64
|
+
defaultOpen = false,
|
|
65
|
+
open: controlledOpen,
|
|
66
|
+
onOpenChange: controlledOnOpenChange,
|
|
67
|
+
...props
|
|
68
|
+
}: MicSelectorProps) => {
|
|
69
|
+
const [value, onValueChange] = useControllableState<string | undefined>({
|
|
70
|
+
defaultProp: defaultValue,
|
|
71
|
+
prop: controlledValue,
|
|
72
|
+
onChange: controlledOnValueChange,
|
|
73
|
+
});
|
|
74
|
+
const [open, onOpenChange] = useControllableState({
|
|
75
|
+
defaultProp: defaultOpen,
|
|
76
|
+
prop: controlledOpen,
|
|
77
|
+
onChange: controlledOnOpenChange,
|
|
78
|
+
});
|
|
79
|
+
const [width, setWidth] = useState(200);
|
|
80
|
+
const { devices, loading, hasPermission, loadDevices } = useAudioDevices();
|
|
81
|
+
|
|
82
|
+
useEffect(() => {
|
|
83
|
+
if (open && !hasPermission && !loading) {
|
|
84
|
+
loadDevices();
|
|
85
|
+
}
|
|
86
|
+
}, [open, hasPermission, loading, loadDevices]);
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<MicSelectorContext.Provider
|
|
90
|
+
value={{
|
|
91
|
+
data: devices,
|
|
92
|
+
value,
|
|
93
|
+
onValueChange,
|
|
94
|
+
open,
|
|
95
|
+
onOpenChange,
|
|
96
|
+
width,
|
|
97
|
+
setWidth,
|
|
98
|
+
}}
|
|
99
|
+
>
|
|
100
|
+
<Popover {...props} onOpenChange={onOpenChange} open={open} />
|
|
101
|
+
</MicSelectorContext.Provider>
|
|
102
|
+
);
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
export type MicSelectorTriggerProps = ComponentProps<typeof Button>;
|
|
106
|
+
|
|
107
|
+
export const MicSelectorTrigger = ({
|
|
108
|
+
children,
|
|
109
|
+
...props
|
|
110
|
+
}: MicSelectorTriggerProps) => {
|
|
111
|
+
const { setWidth } = useContext(MicSelectorContext);
|
|
112
|
+
const ref = useRef<HTMLButtonElement>(null);
|
|
113
|
+
|
|
114
|
+
useEffect(() => {
|
|
115
|
+
// Create a ResizeObserver to detect width changes
|
|
116
|
+
const resizeObserver = new ResizeObserver((entries) => {
|
|
117
|
+
for (const entry of entries) {
|
|
118
|
+
const newWidth = (entry.target as HTMLElement).offsetWidth;
|
|
119
|
+
if (newWidth) {
|
|
120
|
+
setWidth?.(newWidth);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
if (ref.current) {
|
|
126
|
+
resizeObserver.observe(ref.current);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Clean up the observer when component unmounts
|
|
130
|
+
return () => {
|
|
131
|
+
resizeObserver.disconnect();
|
|
132
|
+
};
|
|
133
|
+
}, [setWidth]);
|
|
134
|
+
|
|
135
|
+
return (
|
|
136
|
+
<PopoverTrigger asChild>
|
|
137
|
+
<Button variant="outline" {...props} ref={ref}>
|
|
138
|
+
{children}
|
|
139
|
+
<ChevronsUpDownIcon
|
|
140
|
+
className="shrink-0 text-muted-foreground"
|
|
141
|
+
size={16}
|
|
142
|
+
/>
|
|
143
|
+
</Button>
|
|
144
|
+
</PopoverTrigger>
|
|
145
|
+
);
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
export type MicSelectorContentProps = ComponentProps<typeof Command> & {
|
|
149
|
+
popoverOptions?: ComponentProps<typeof PopoverContent>;
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
export const MicSelectorContent = ({
|
|
153
|
+
className,
|
|
154
|
+
popoverOptions,
|
|
155
|
+
...props
|
|
156
|
+
}: MicSelectorContentProps) => {
|
|
157
|
+
const { width, onValueChange, value } = useContext(MicSelectorContext);
|
|
158
|
+
|
|
159
|
+
return (
|
|
160
|
+
<PopoverContent
|
|
161
|
+
className={cn("p-0", className)}
|
|
162
|
+
style={{ width }}
|
|
163
|
+
{...popoverOptions}
|
|
164
|
+
>
|
|
165
|
+
<Command onValueChange={onValueChange} value={value} {...props} />
|
|
166
|
+
</PopoverContent>
|
|
167
|
+
);
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
export type MicSelectorInputProps = ComponentProps<typeof CommandInput> & {
|
|
171
|
+
value?: string;
|
|
172
|
+
defaultValue?: string;
|
|
173
|
+
onValueChange?: (value: string) => void;
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
export const MicSelectorInput = ({ ...props }: MicSelectorInputProps) => (
|
|
177
|
+
<CommandInput placeholder="Search microphones..." {...props} />
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
export type MicSelectorListProps = Omit<
|
|
181
|
+
ComponentProps<typeof CommandList>,
|
|
182
|
+
"children"
|
|
183
|
+
> & {
|
|
184
|
+
children: (devices: MediaDeviceInfo[]) => ReactNode;
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
export const MicSelectorList = ({
|
|
188
|
+
children,
|
|
189
|
+
...props
|
|
190
|
+
}: MicSelectorListProps) => {
|
|
191
|
+
const { data } = useContext(MicSelectorContext);
|
|
192
|
+
|
|
193
|
+
return <CommandList {...props}>{children(data)}</CommandList>;
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
export type MicSelectorEmptyProps = ComponentProps<typeof CommandEmpty>;
|
|
197
|
+
|
|
198
|
+
export const MicSelectorEmpty = ({
|
|
199
|
+
children = "No microphone found.",
|
|
200
|
+
...props
|
|
201
|
+
}: MicSelectorEmptyProps) => <CommandEmpty {...props}>{children}</CommandEmpty>;
|
|
202
|
+
|
|
203
|
+
export type MicSelectorItemProps = ComponentProps<typeof CommandItem>;
|
|
204
|
+
|
|
205
|
+
export const MicSelectorItem = (props: MicSelectorItemProps) => {
|
|
206
|
+
const { onValueChange, onOpenChange } = useContext(MicSelectorContext);
|
|
207
|
+
|
|
208
|
+
return (
|
|
209
|
+
<CommandItem
|
|
210
|
+
onSelect={(currentValue) => {
|
|
211
|
+
onValueChange?.(currentValue);
|
|
212
|
+
onOpenChange?.(false);
|
|
213
|
+
}}
|
|
214
|
+
{...props}
|
|
215
|
+
/>
|
|
216
|
+
);
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
export type MicSelectorLabelProps = ComponentProps<"span"> & {
|
|
220
|
+
device: MediaDeviceInfo;
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
export const MicSelectorLabel = ({
|
|
224
|
+
device,
|
|
225
|
+
className,
|
|
226
|
+
...props
|
|
227
|
+
}: MicSelectorLabelProps) => {
|
|
228
|
+
const matches = device.label.match(deviceIdRegex);
|
|
229
|
+
|
|
230
|
+
console.log(matches, device.label);
|
|
231
|
+
|
|
232
|
+
if (!matches) {
|
|
233
|
+
return (
|
|
234
|
+
<span className={className} {...props}>
|
|
235
|
+
{device.label}
|
|
236
|
+
</span>
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const [, deviceId] = matches;
|
|
241
|
+
const name = device.label.replace(deviceIdRegex, "");
|
|
242
|
+
|
|
243
|
+
return (
|
|
244
|
+
<span className={className} {...props}>
|
|
245
|
+
<span>{name}</span>
|
|
246
|
+
<span className="text-muted-foreground"> ({deviceId})</span>
|
|
247
|
+
</span>
|
|
248
|
+
);
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
export type MicSelectorValueProps = ComponentProps<"span">;
|
|
252
|
+
|
|
253
|
+
export const MicSelectorValue = ({
|
|
254
|
+
className,
|
|
255
|
+
...props
|
|
256
|
+
}: MicSelectorValueProps) => {
|
|
257
|
+
const { data, value } = useContext(MicSelectorContext);
|
|
258
|
+
const currentDevice = data.find((d) => d.deviceId === value);
|
|
259
|
+
|
|
260
|
+
if (!currentDevice) {
|
|
261
|
+
return (
|
|
262
|
+
<span className={cn("flex-1 text-left", className)} {...props}>
|
|
263
|
+
Select microphone...
|
|
264
|
+
</span>
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return (
|
|
269
|
+
<MicSelectorLabel
|
|
270
|
+
className={cn("flex-1 text-left", className)}
|
|
271
|
+
device={currentDevice}
|
|
272
|
+
{...props}
|
|
273
|
+
/>
|
|
274
|
+
);
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
export const useAudioDevices = () => {
|
|
278
|
+
const [devices, setDevices] = useState<MediaDeviceInfo[]>([]);
|
|
279
|
+
const [loading, setLoading] = useState(true);
|
|
280
|
+
const [error, setError] = useState<string | null>(null);
|
|
281
|
+
const [hasPermission, setHasPermission] = useState(false);
|
|
282
|
+
|
|
283
|
+
const loadDevicesWithoutPermission = useCallback(async () => {
|
|
284
|
+
try {
|
|
285
|
+
setLoading(true);
|
|
286
|
+
setError(null);
|
|
287
|
+
|
|
288
|
+
const deviceList = await navigator.mediaDevices.enumerateDevices();
|
|
289
|
+
const audioInputs = deviceList.filter(
|
|
290
|
+
(device) => device.kind === "audioinput"
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
setDevices(audioInputs);
|
|
294
|
+
} catch (err) {
|
|
295
|
+
const message =
|
|
296
|
+
err instanceof Error ? err.message : "Failed to get audio devices";
|
|
297
|
+
|
|
298
|
+
setError(message);
|
|
299
|
+
console.error("Error getting audio devices:", message);
|
|
300
|
+
} finally {
|
|
301
|
+
setLoading(false);
|
|
302
|
+
}
|
|
303
|
+
}, []);
|
|
304
|
+
|
|
305
|
+
const loadDevicesWithPermission = useCallback(async () => {
|
|
306
|
+
if (loading) {
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
try {
|
|
311
|
+
setLoading(true);
|
|
312
|
+
setError(null);
|
|
313
|
+
|
|
314
|
+
const tempStream = await navigator.mediaDevices.getUserMedia({
|
|
315
|
+
audio: true,
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
for (const track of tempStream.getTracks()) {
|
|
319
|
+
track.stop();
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const deviceList = await navigator.mediaDevices.enumerateDevices();
|
|
323
|
+
const audioInputs = deviceList.filter(
|
|
324
|
+
(device) => device.kind === "audioinput"
|
|
325
|
+
);
|
|
326
|
+
|
|
327
|
+
setDevices(audioInputs);
|
|
328
|
+
setHasPermission(true);
|
|
329
|
+
} catch (err) {
|
|
330
|
+
const message =
|
|
331
|
+
err instanceof Error ? err.message : "Failed to get audio devices";
|
|
332
|
+
|
|
333
|
+
setError(message);
|
|
334
|
+
console.error("Error getting audio devices:", message);
|
|
335
|
+
} finally {
|
|
336
|
+
setLoading(false);
|
|
337
|
+
}
|
|
338
|
+
}, [loading]);
|
|
339
|
+
|
|
340
|
+
useEffect(() => {
|
|
341
|
+
loadDevicesWithoutPermission();
|
|
342
|
+
}, [loadDevicesWithoutPermission]);
|
|
343
|
+
|
|
344
|
+
useEffect(() => {
|
|
345
|
+
const handleDeviceChange = () => {
|
|
346
|
+
if (hasPermission) {
|
|
347
|
+
loadDevicesWithPermission();
|
|
348
|
+
} else {
|
|
349
|
+
loadDevicesWithoutPermission();
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
navigator.mediaDevices.addEventListener("devicechange", handleDeviceChange);
|
|
354
|
+
|
|
355
|
+
return () => {
|
|
356
|
+
navigator.mediaDevices.removeEventListener(
|
|
357
|
+
"devicechange",
|
|
358
|
+
handleDeviceChange
|
|
359
|
+
);
|
|
360
|
+
};
|
|
361
|
+
}, [hasPermission, loadDevicesWithPermission, loadDevicesWithoutPermission]);
|
|
362
|
+
|
|
363
|
+
return {
|
|
364
|
+
devices,
|
|
365
|
+
loading,
|
|
366
|
+
error,
|
|
367
|
+
hasPermission,
|
|
368
|
+
loadDevices: loadDevicesWithPermission,
|
|
369
|
+
};
|
|
370
|
+
};
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Command,
|
|
3
|
+
CommandDialog,
|
|
4
|
+
CommandEmpty,
|
|
5
|
+
CommandGroup,
|
|
6
|
+
CommandInput,
|
|
7
|
+
CommandItem,
|
|
8
|
+
CommandList,
|
|
9
|
+
CommandSeparator,
|
|
10
|
+
CommandShortcut,
|
|
11
|
+
} from "@/components/ui/command";
|
|
12
|
+
import {
|
|
13
|
+
Dialog,
|
|
14
|
+
DialogContent,
|
|
15
|
+
DialogTitle,
|
|
16
|
+
DialogTrigger,
|
|
17
|
+
} from "@/components/ui/dialog";
|
|
18
|
+
import { cn } from "@/lib/utils";
|
|
19
|
+
import type { ComponentProps, ReactNode } from "react";
|
|
20
|
+
|
|
21
|
+
export type ModelSelectorProps = ComponentProps<typeof Dialog>;
|
|
22
|
+
|
|
23
|
+
export const ModelSelector = (props: ModelSelectorProps) => (
|
|
24
|
+
<Dialog {...props} />
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
export type ModelSelectorTriggerProps = ComponentProps<typeof DialogTrigger>;
|
|
28
|
+
|
|
29
|
+
export const ModelSelectorTrigger = (props: ModelSelectorTriggerProps) => (
|
|
30
|
+
<DialogTrigger {...props} />
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
export type ModelSelectorContentProps = ComponentProps<typeof DialogContent> & {
|
|
34
|
+
title?: ReactNode;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export const ModelSelectorContent = ({
|
|
38
|
+
className,
|
|
39
|
+
children,
|
|
40
|
+
title = "Model Selector",
|
|
41
|
+
...props
|
|
42
|
+
}: ModelSelectorContentProps) => (
|
|
43
|
+
<DialogContent
|
|
44
|
+
className={cn(
|
|
45
|
+
"outline! border-none! p-0 outline-border! outline-solid!",
|
|
46
|
+
className
|
|
47
|
+
)}
|
|
48
|
+
{...props}
|
|
49
|
+
>
|
|
50
|
+
<DialogTitle className="sr-only">{title}</DialogTitle>
|
|
51
|
+
<Command className="**:data-[slot=command-input-wrapper]:h-auto">
|
|
52
|
+
{children}
|
|
53
|
+
</Command>
|
|
54
|
+
</DialogContent>
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
export type ModelSelectorDialogProps = ComponentProps<typeof CommandDialog>;
|
|
58
|
+
|
|
59
|
+
export const ModelSelectorDialog = (props: ModelSelectorDialogProps) => (
|
|
60
|
+
<CommandDialog {...props} />
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
export type ModelSelectorInputProps = ComponentProps<typeof CommandInput>;
|
|
64
|
+
|
|
65
|
+
export const ModelSelectorInput = ({
|
|
66
|
+
className,
|
|
67
|
+
...props
|
|
68
|
+
}: ModelSelectorInputProps) => (
|
|
69
|
+
<CommandInput className={cn("h-auto py-3.5", className)} {...props} />
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
export type ModelSelectorListProps = ComponentProps<typeof CommandList>;
|
|
73
|
+
|
|
74
|
+
export const ModelSelectorList = (props: ModelSelectorListProps) => (
|
|
75
|
+
<CommandList {...props} />
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
export type ModelSelectorEmptyProps = ComponentProps<typeof CommandEmpty>;
|
|
79
|
+
|
|
80
|
+
export const ModelSelectorEmpty = (props: ModelSelectorEmptyProps) => (
|
|
81
|
+
<CommandEmpty {...props} />
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
export type ModelSelectorGroupProps = ComponentProps<typeof CommandGroup>;
|
|
85
|
+
|
|
86
|
+
export const ModelSelectorGroup = (props: ModelSelectorGroupProps) => (
|
|
87
|
+
<CommandGroup {...props} />
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
export type ModelSelectorItemProps = ComponentProps<typeof CommandItem>;
|
|
91
|
+
|
|
92
|
+
export const ModelSelectorItem = (props: ModelSelectorItemProps) => (
|
|
93
|
+
<CommandItem {...props} />
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
export type ModelSelectorShortcutProps = ComponentProps<typeof CommandShortcut>;
|
|
97
|
+
|
|
98
|
+
export const ModelSelectorShortcut = (props: ModelSelectorShortcutProps) => (
|
|
99
|
+
<CommandShortcut {...props} />
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
export type ModelSelectorSeparatorProps = ComponentProps<
|
|
103
|
+
typeof CommandSeparator
|
|
104
|
+
>;
|
|
105
|
+
|
|
106
|
+
export const ModelSelectorSeparator = (props: ModelSelectorSeparatorProps) => (
|
|
107
|
+
<CommandSeparator {...props} />
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
export type ModelSelectorLogoProps = Omit<
|
|
111
|
+
ComponentProps<"img">,
|
|
112
|
+
"src" | "alt"
|
|
113
|
+
> & {
|
|
114
|
+
provider:
|
|
115
|
+
| "moonshotai-cn"
|
|
116
|
+
| "lucidquery"
|
|
117
|
+
| "moonshotai"
|
|
118
|
+
| "zai-coding-plan"
|
|
119
|
+
| "alibaba"
|
|
120
|
+
| "xai"
|
|
121
|
+
| "vultr"
|
|
122
|
+
| "nvidia"
|
|
123
|
+
| "upstage"
|
|
124
|
+
| "groq"
|
|
125
|
+
| "github-copilot"
|
|
126
|
+
| "mistral"
|
|
127
|
+
| "vercel"
|
|
128
|
+
| "nebius"
|
|
129
|
+
| "deepseek"
|
|
130
|
+
| "alibaba-cn"
|
|
131
|
+
| "google-vertex-anthropic"
|
|
132
|
+
| "venice"
|
|
133
|
+
| "chutes"
|
|
134
|
+
| "cortecs"
|
|
135
|
+
| "github-models"
|
|
136
|
+
| "togetherai"
|
|
137
|
+
| "azure"
|
|
138
|
+
| "baseten"
|
|
139
|
+
| "huggingface"
|
|
140
|
+
| "opencode"
|
|
141
|
+
| "fastrouter"
|
|
142
|
+
| "google"
|
|
143
|
+
| "google-vertex"
|
|
144
|
+
| "cloudflare-workers-ai"
|
|
145
|
+
| "inception"
|
|
146
|
+
| "wandb"
|
|
147
|
+
| "openai"
|
|
148
|
+
| "zhipuai-coding-plan"
|
|
149
|
+
| "perplexity"
|
|
150
|
+
| "openrouter"
|
|
151
|
+
| "zenmux"
|
|
152
|
+
| "v0"
|
|
153
|
+
| "iflowcn"
|
|
154
|
+
| "synthetic"
|
|
155
|
+
| "deepinfra"
|
|
156
|
+
| "zhipuai"
|
|
157
|
+
| "submodel"
|
|
158
|
+
| "zai"
|
|
159
|
+
| "inference"
|
|
160
|
+
| "requesty"
|
|
161
|
+
| "morph"
|
|
162
|
+
| "lmstudio"
|
|
163
|
+
| "anthropic"
|
|
164
|
+
| "aihubmix"
|
|
165
|
+
| "fireworks-ai"
|
|
166
|
+
| "modelscope"
|
|
167
|
+
| "llama"
|
|
168
|
+
| "scaleway"
|
|
169
|
+
| "amazon-bedrock"
|
|
170
|
+
| "cerebras"
|
|
171
|
+
| (string & {});
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
export const ModelSelectorLogo = ({
|
|
175
|
+
provider,
|
|
176
|
+
className,
|
|
177
|
+
...props
|
|
178
|
+
}: ModelSelectorLogoProps) => (
|
|
179
|
+
<img
|
|
180
|
+
{...props}
|
|
181
|
+
alt={`${provider} logo`}
|
|
182
|
+
className={cn("size-3 dark:invert", className)}
|
|
183
|
+
height={12}
|
|
184
|
+
src={`https://models.dev/logos/${provider}.svg`}
|
|
185
|
+
width={12}
|
|
186
|
+
/>
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
export type ModelSelectorLogoGroupProps = ComponentProps<"div">;
|
|
190
|
+
|
|
191
|
+
export const ModelSelectorLogoGroup = ({
|
|
192
|
+
className,
|
|
193
|
+
...props
|
|
194
|
+
}: ModelSelectorLogoGroupProps) => (
|
|
195
|
+
<div
|
|
196
|
+
className={cn(
|
|
197
|
+
"flex shrink-0 items-center -space-x-1 [&>img]:rounded-full [&>img]:bg-background [&>img]:p-px [&>img]:ring-1 dark:[&>img]:bg-foreground",
|
|
198
|
+
className
|
|
199
|
+
)}
|
|
200
|
+
{...props}
|
|
201
|
+
/>
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
export type ModelSelectorNameProps = ComponentProps<"span">;
|
|
205
|
+
|
|
206
|
+
export const ModelSelectorName = ({
|
|
207
|
+
className,
|
|
208
|
+
...props
|
|
209
|
+
}: ModelSelectorNameProps) => (
|
|
210
|
+
<span className={cn("flex-1 truncate text-left", className)} {...props} />
|
|
211
|
+
);
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Card,
|
|
3
|
+
CardAction,
|
|
4
|
+
CardContent,
|
|
5
|
+
CardDescription,
|
|
6
|
+
CardFooter,
|
|
7
|
+
CardHeader,
|
|
8
|
+
CardTitle,
|
|
9
|
+
} from "@/components/ui/card";
|
|
10
|
+
import { cn } from "@/lib/utils";
|
|
11
|
+
import { Handle, Position } from "@xyflow/react";
|
|
12
|
+
import type { ComponentProps } from "react";
|
|
13
|
+
|
|
14
|
+
export type NodeProps = ComponentProps<typeof Card> & {
|
|
15
|
+
handles: {
|
|
16
|
+
target: boolean;
|
|
17
|
+
source: boolean;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const Node = ({ handles, className, ...props }: NodeProps) => (
|
|
22
|
+
<Card
|
|
23
|
+
className={cn(
|
|
24
|
+
"node-container relative size-full h-auto w-sm gap-0 rounded-md p-0",
|
|
25
|
+
className
|
|
26
|
+
)}
|
|
27
|
+
{...props}
|
|
28
|
+
>
|
|
29
|
+
{handles.target && <Handle position={Position.Left} type="target" />}
|
|
30
|
+
{handles.source && <Handle position={Position.Right} type="source" />}
|
|
31
|
+
{props.children}
|
|
32
|
+
</Card>
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
export type NodeHeaderProps = ComponentProps<typeof CardHeader>;
|
|
36
|
+
|
|
37
|
+
export const NodeHeader = ({ className, ...props }: NodeHeaderProps) => (
|
|
38
|
+
<CardHeader
|
|
39
|
+
className={cn("gap-0.5 rounded-t-md border-b bg-secondary p-3!", className)}
|
|
40
|
+
{...props}
|
|
41
|
+
/>
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
export type NodeTitleProps = ComponentProps<typeof CardTitle>;
|
|
45
|
+
|
|
46
|
+
export const NodeTitle = (props: NodeTitleProps) => <CardTitle {...props} />;
|
|
47
|
+
|
|
48
|
+
export type NodeDescriptionProps = ComponentProps<typeof CardDescription>;
|
|
49
|
+
|
|
50
|
+
export const NodeDescription = (props: NodeDescriptionProps) => (
|
|
51
|
+
<CardDescription {...props} />
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
export type NodeActionProps = ComponentProps<typeof CardAction>;
|
|
55
|
+
|
|
56
|
+
export const NodeAction = (props: NodeActionProps) => <CardAction {...props} />;
|
|
57
|
+
|
|
58
|
+
export type NodeContentProps = ComponentProps<typeof CardContent>;
|
|
59
|
+
|
|
60
|
+
export const NodeContent = ({ className, ...props }: NodeContentProps) => (
|
|
61
|
+
<CardContent className={cn("p-3", className)} {...props} />
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
export type NodeFooterProps = ComponentProps<typeof CardFooter>;
|
|
65
|
+
|
|
66
|
+
export const NodeFooter = ({ className, ...props }: NodeFooterProps) => (
|
|
67
|
+
<CardFooter
|
|
68
|
+
className={cn("rounded-b-md border-t bg-secondary p-3!", className)}
|
|
69
|
+
{...props}
|
|
70
|
+
/>
|
|
71
|
+
);
|