@vvedantb/ui 0.1.1 → 0.1.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.
- package/LICENSE +21 -21
- package/package.json +3 -2
- package/src/ai-elements/artifact.tsx +99 -99
- package/src/ai-elements/attachments.tsx +366 -366
- package/src/ai-elements/chain-of-thought.tsx +212 -212
- package/src/ai-elements/code-block.tsx +478 -478
- package/src/ai-elements/conversation.tsx +157 -157
- package/src/ai-elements/image.tsx +25 -25
- package/src/ai-elements/inline-citation.tsx +252 -252
- package/src/ai-elements/message.tsx +269 -269
- package/src/ai-elements/model-selector.tsx +211 -211
- package/src/ai-elements/prompt-input.tsx +1018 -1018
- package/src/ai-elements/queue.tsx +251 -251
- package/src/ai-elements/reasoning.tsx +178 -178
- package/src/ai-elements/shimmer.tsx +56 -56
- package/src/ai-elements/sources.tsx +77 -77
- package/src/ai-elements/speech-input.tsx +290 -290
- package/src/ai-elements/suggestion.tsx +191 -191
- package/src/css.d.ts +2 -2
- package/src/index.ts +96 -96
- package/src/motion/presets.ts +35 -35
- package/src/motion/presets.vibot.ts +105 -105
- package/src/ui/_menu-classes.ts +30 -30
- package/src/ui/_menu-classes.vibot.ts +33 -33
- package/src/ui/accordion.tsx +81 -81
- package/src/ui/accordion.verve.tsx +56 -56
- package/src/ui/badge.tsx +34 -34
- package/src/ui/breadcrumb.tsx +83 -83
- package/src/ui/button-group.tsx +84 -84
- package/src/ui/button.tsx +57 -57
- package/src/ui/card.tsx +27 -27
- package/src/ui/carousel.tsx +211 -211
- package/src/ui/checkbox.tsx +27 -27
- package/src/ui/clear-input.tsx +170 -170
- package/src/ui/clear-input.vibot.tsx +76 -76
- package/src/ui/clearInputDissolve.ts +214 -214
- package/src/ui/collapsible.tsx +7 -7
- package/src/ui/command.tsx +148 -148
- package/src/ui/context-menu.tsx +173 -173
- package/src/ui/dialog.tsx +149 -149
- package/src/ui/dropdown-menu.tsx +178 -178
- package/src/ui/hover-card.tsx +31 -31
- package/src/ui/input-group.tsx +162 -162
- package/src/ui/input-group.verve.tsx +150 -150
- package/src/ui/input.tsx +21 -21
- package/src/ui/kbd.tsx +18 -18
- package/src/ui/label.tsx +20 -20
- package/src/ui/labeled-switch-row.tsx +53 -53
- package/src/ui/modalTransition.ts +85 -85
- package/src/ui/pagination.tsx +47 -47
- package/src/ui/popover.tsx +33 -33
- package/src/ui/progress.tsx +25 -25
- package/src/ui/radix-select.tsx +202 -202
- package/src/ui/scroll-area.tsx +55 -55
- package/src/ui/scroll-area.verve.tsx +43 -43
- package/src/ui/search-input.tsx +29 -29
- package/src/ui/select.tsx +163 -163
- package/src/ui/separator.tsx +28 -28
- package/src/ui/sheet.tsx +87 -87
- package/src/ui/skeleton.tsx +18 -18
- package/src/ui/sonner.css +16 -16
- package/src/ui/sonner.tsx +47 -47
- package/src/ui/spinner.tsx +27 -27
- package/src/ui/surface-classes.ts +55 -55
- package/src/ui/switch.tsx +26 -26
- package/src/ui/table.tsx +123 -123
- package/src/ui/tabs.tsx +140 -140
- package/src/ui/tabsSliding.ts +56 -56
- package/src/ui/textarea.tsx +21 -21
- package/src/ui/time-picker.tsx +170 -170
- package/src/ui/tooltip.tsx +25 -25
- package/src/ui/visually-hidden.tsx +3 -3
- package/src/utils/cn.ts +16 -16
- package/src/utils/ref.ts +10 -10
|
@@ -1,157 +1,157 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import type { ComponentProps } from "react";
|
|
3
|
-
import { AnimatePresence, motion } from "motion/react";
|
|
4
|
-
import { IconArrowDown, IconDownload } from "@tabler/icons-react";
|
|
5
|
-
import { Button } from "../ui/button";
|
|
6
|
-
import { cn } from "../utils/cn";
|
|
7
|
-
import { flatInteractiveClass } from "../ui/surface-classes";
|
|
8
|
-
import { StickToBottom, useStickToBottomContext } from "use-stick-to-bottom";
|
|
9
|
-
import { fastTransition } from "../motion/presets.vibot";
|
|
10
|
-
export type ConversationProps = ComponentProps<typeof StickToBottom>;
|
|
11
|
-
export const Conversation = ({ className, ...props }: ConversationProps) => (
|
|
12
|
-
<StickToBottom
|
|
13
|
-
className={cn("relative flex-1 overflow-y-hidden", className)}
|
|
14
|
-
initial="instant"
|
|
15
|
-
resize="smooth"
|
|
16
|
-
role="log"
|
|
17
|
-
{...props}
|
|
18
|
-
/>
|
|
19
|
-
);
|
|
20
|
-
export type ConversationContentProps = ComponentProps<
|
|
21
|
-
typeof StickToBottom.Content
|
|
22
|
-
>;
|
|
23
|
-
export const ConversationContent = ({
|
|
24
|
-
className,
|
|
25
|
-
...props
|
|
26
|
-
}: ConversationContentProps) => (
|
|
27
|
-
<StickToBottom.Content
|
|
28
|
-
className={cn("flex flex-col gap-4 px-3 py-3", className)}
|
|
29
|
-
{...props}
|
|
30
|
-
/>
|
|
31
|
-
);
|
|
32
|
-
export type ConversationEmptyStateProps = ComponentProps<"div"> & {
|
|
33
|
-
title?: string;
|
|
34
|
-
description?: string;
|
|
35
|
-
icon?: React.ReactNode;
|
|
36
|
-
};
|
|
37
|
-
export const ConversationEmptyState = ({
|
|
38
|
-
className,
|
|
39
|
-
title = "No messages yet",
|
|
40
|
-
description = "Start a conversation to see messages here",
|
|
41
|
-
icon,
|
|
42
|
-
children,
|
|
43
|
-
...props
|
|
44
|
-
}: ConversationEmptyStateProps) => (
|
|
45
|
-
<div
|
|
46
|
-
className={cn(
|
|
47
|
-
"flex size-full flex-col items-center justify-center gap-3 p-8 text-center",
|
|
48
|
-
className,
|
|
49
|
-
)}
|
|
50
|
-
{...props}
|
|
51
|
-
>
|
|
52
|
-
{children ?? (
|
|
53
|
-
<>
|
|
54
|
-
{icon && <div className="text-muted">{icon}</div>}
|
|
55
|
-
<div className="space-y-1">
|
|
56
|
-
<h3 className="font-medium text-sm">{title}</h3>
|
|
57
|
-
{description && <p className="text-muted text-sm">{description}</p>}
|
|
58
|
-
</div>
|
|
59
|
-
</>
|
|
60
|
-
)}
|
|
61
|
-
</div>
|
|
62
|
-
);
|
|
63
|
-
export type ConversationScrollButtonProps = ComponentProps<typeof Button>;
|
|
64
|
-
export const ConversationScrollButton = ({
|
|
65
|
-
className,
|
|
66
|
-
...props
|
|
67
|
-
}: ConversationScrollButtonProps) => {
|
|
68
|
-
const { isAtBottom, scrollToBottom } = useStickToBottomContext();
|
|
69
|
-
const handleScrollToBottom = () => {
|
|
70
|
-
scrollToBottom();
|
|
71
|
-
};
|
|
72
|
-
return (
|
|
73
|
-
<AnimatePresence initial={false}>
|
|
74
|
-
{isAtBottom ? null : (
|
|
75
|
-
<motion.div
|
|
76
|
-
key="scroll-to-bottom"
|
|
77
|
-
className="absolute bottom-4 left-0 right-0 z-10 flex justify-center"
|
|
78
|
-
initial={{ opacity: 0, y: 8 }}
|
|
79
|
-
animate={{ opacity: 1, y: 0 }}
|
|
80
|
-
exit={{ opacity: 0, y: 8 }}
|
|
81
|
-
transition={fastTransition}
|
|
82
|
-
>
|
|
83
|
-
<Button
|
|
84
|
-
className={cn(flatInteractiveClass, "rounded-full", className)}
|
|
85
|
-
onClick={handleScrollToBottom}
|
|
86
|
-
size="icon"
|
|
87
|
-
type="button"
|
|
88
|
-
variant="outline"
|
|
89
|
-
{...props}
|
|
90
|
-
>
|
|
91
|
-
<IconArrowDown className="size-4" />
|
|
92
|
-
</Button>
|
|
93
|
-
</motion.div>
|
|
94
|
-
)}
|
|
95
|
-
</AnimatePresence>
|
|
96
|
-
);
|
|
97
|
-
};
|
|
98
|
-
export interface ConversationMessage {
|
|
99
|
-
role: "user" | "assistant" | "system" | "data" | "tool";
|
|
100
|
-
content: string;
|
|
101
|
-
}
|
|
102
|
-
export type ConversationDownloadProps = Omit<
|
|
103
|
-
ComponentProps<typeof Button>,
|
|
104
|
-
"onClick"
|
|
105
|
-
> & {
|
|
106
|
-
messages: ConversationMessage[];
|
|
107
|
-
filename?: string;
|
|
108
|
-
formatMessage?: (message: ConversationMessage, index: number) => string;
|
|
109
|
-
};
|
|
110
|
-
const defaultFormatMessage = (message: ConversationMessage): string => {
|
|
111
|
-
const roleLabel =
|
|
112
|
-
message.role.charAt(0).toUpperCase() + message.role.slice(1);
|
|
113
|
-
return `**${roleLabel}:** ${message.content}`;
|
|
114
|
-
};
|
|
115
|
-
export const messagesToMarkdown = (
|
|
116
|
-
messages: ConversationMessage[],
|
|
117
|
-
formatMessage: (
|
|
118
|
-
message: ConversationMessage,
|
|
119
|
-
index: number,
|
|
120
|
-
) => string = defaultFormatMessage,
|
|
121
|
-
): string => messages.map((msg, i) => formatMessage(msg, i)).join("\n\n");
|
|
122
|
-
export const ConversationDownload = ({
|
|
123
|
-
messages,
|
|
124
|
-
filename = "conversation.md",
|
|
125
|
-
formatMessage = defaultFormatMessage,
|
|
126
|
-
className,
|
|
127
|
-
children,
|
|
128
|
-
...props
|
|
129
|
-
}: ConversationDownloadProps) => {
|
|
130
|
-
const handleDownload = () => {
|
|
131
|
-
const markdown = messagesToMarkdown(messages, formatMessage);
|
|
132
|
-
const blob = new Blob([markdown], { type: "text/markdown" });
|
|
133
|
-
const url = URL.createObjectURL(blob);
|
|
134
|
-
const link = document.createElement("a");
|
|
135
|
-
link.href = url;
|
|
136
|
-
link.download = filename;
|
|
137
|
-
document.body.append(link);
|
|
138
|
-
link.click();
|
|
139
|
-
link.remove();
|
|
140
|
-
URL.revokeObjectURL(url);
|
|
141
|
-
};
|
|
142
|
-
return (
|
|
143
|
-
<Button
|
|
144
|
-
className={cn(
|
|
145
|
-
cn(flatInteractiveClass, "absolute top-4 right-4 rounded-full"),
|
|
146
|
-
className,
|
|
147
|
-
)}
|
|
148
|
-
onClick={handleDownload}
|
|
149
|
-
size="icon"
|
|
150
|
-
type="button"
|
|
151
|
-
variant="outline"
|
|
152
|
-
{...props}
|
|
153
|
-
>
|
|
154
|
-
{children ?? <IconDownload className="size-4" />}
|
|
155
|
-
</Button>
|
|
156
|
-
);
|
|
157
|
-
};
|
|
1
|
+
"use client";
|
|
2
|
+
import type { ComponentProps } from "react";
|
|
3
|
+
import { AnimatePresence, motion } from "motion/react";
|
|
4
|
+
import { IconArrowDown, IconDownload } from "@tabler/icons-react";
|
|
5
|
+
import { Button } from "../ui/button";
|
|
6
|
+
import { cn } from "../utils/cn";
|
|
7
|
+
import { flatInteractiveClass } from "../ui/surface-classes";
|
|
8
|
+
import { StickToBottom, useStickToBottomContext } from "use-stick-to-bottom";
|
|
9
|
+
import { fastTransition } from "../motion/presets.vibot";
|
|
10
|
+
export type ConversationProps = ComponentProps<typeof StickToBottom>;
|
|
11
|
+
export const Conversation = ({ className, ...props }: ConversationProps) => (
|
|
12
|
+
<StickToBottom
|
|
13
|
+
className={cn("relative flex-1 overflow-y-hidden", className)}
|
|
14
|
+
initial="instant"
|
|
15
|
+
resize="smooth"
|
|
16
|
+
role="log"
|
|
17
|
+
{...props}
|
|
18
|
+
/>
|
|
19
|
+
);
|
|
20
|
+
export type ConversationContentProps = ComponentProps<
|
|
21
|
+
typeof StickToBottom.Content
|
|
22
|
+
>;
|
|
23
|
+
export const ConversationContent = ({
|
|
24
|
+
className,
|
|
25
|
+
...props
|
|
26
|
+
}: ConversationContentProps) => (
|
|
27
|
+
<StickToBottom.Content
|
|
28
|
+
className={cn("flex flex-col gap-4 px-3 py-3", className)}
|
|
29
|
+
{...props}
|
|
30
|
+
/>
|
|
31
|
+
);
|
|
32
|
+
export type ConversationEmptyStateProps = ComponentProps<"div"> & {
|
|
33
|
+
title?: string;
|
|
34
|
+
description?: string;
|
|
35
|
+
icon?: React.ReactNode;
|
|
36
|
+
};
|
|
37
|
+
export const ConversationEmptyState = ({
|
|
38
|
+
className,
|
|
39
|
+
title = "No messages yet",
|
|
40
|
+
description = "Start a conversation to see messages here",
|
|
41
|
+
icon,
|
|
42
|
+
children,
|
|
43
|
+
...props
|
|
44
|
+
}: ConversationEmptyStateProps) => (
|
|
45
|
+
<div
|
|
46
|
+
className={cn(
|
|
47
|
+
"flex size-full flex-col items-center justify-center gap-3 p-8 text-center",
|
|
48
|
+
className,
|
|
49
|
+
)}
|
|
50
|
+
{...props}
|
|
51
|
+
>
|
|
52
|
+
{children ?? (
|
|
53
|
+
<>
|
|
54
|
+
{icon && <div className="text-muted">{icon}</div>}
|
|
55
|
+
<div className="space-y-1">
|
|
56
|
+
<h3 className="font-medium text-sm">{title}</h3>
|
|
57
|
+
{description && <p className="text-muted text-sm">{description}</p>}
|
|
58
|
+
</div>
|
|
59
|
+
</>
|
|
60
|
+
)}
|
|
61
|
+
</div>
|
|
62
|
+
);
|
|
63
|
+
export type ConversationScrollButtonProps = ComponentProps<typeof Button>;
|
|
64
|
+
export const ConversationScrollButton = ({
|
|
65
|
+
className,
|
|
66
|
+
...props
|
|
67
|
+
}: ConversationScrollButtonProps) => {
|
|
68
|
+
const { isAtBottom, scrollToBottom } = useStickToBottomContext();
|
|
69
|
+
const handleScrollToBottom = () => {
|
|
70
|
+
scrollToBottom();
|
|
71
|
+
};
|
|
72
|
+
return (
|
|
73
|
+
<AnimatePresence initial={false}>
|
|
74
|
+
{isAtBottom ? null : (
|
|
75
|
+
<motion.div
|
|
76
|
+
key="scroll-to-bottom"
|
|
77
|
+
className="absolute bottom-4 left-0 right-0 z-10 flex justify-center"
|
|
78
|
+
initial={{ opacity: 0, y: 8 }}
|
|
79
|
+
animate={{ opacity: 1, y: 0 }}
|
|
80
|
+
exit={{ opacity: 0, y: 8 }}
|
|
81
|
+
transition={fastTransition}
|
|
82
|
+
>
|
|
83
|
+
<Button
|
|
84
|
+
className={cn(flatInteractiveClass, "rounded-full", className)}
|
|
85
|
+
onClick={handleScrollToBottom}
|
|
86
|
+
size="icon"
|
|
87
|
+
type="button"
|
|
88
|
+
variant="outline"
|
|
89
|
+
{...props}
|
|
90
|
+
>
|
|
91
|
+
<IconArrowDown className="size-4" />
|
|
92
|
+
</Button>
|
|
93
|
+
</motion.div>
|
|
94
|
+
)}
|
|
95
|
+
</AnimatePresence>
|
|
96
|
+
);
|
|
97
|
+
};
|
|
98
|
+
export interface ConversationMessage {
|
|
99
|
+
role: "user" | "assistant" | "system" | "data" | "tool";
|
|
100
|
+
content: string;
|
|
101
|
+
}
|
|
102
|
+
export type ConversationDownloadProps = Omit<
|
|
103
|
+
ComponentProps<typeof Button>,
|
|
104
|
+
"onClick"
|
|
105
|
+
> & {
|
|
106
|
+
messages: ConversationMessage[];
|
|
107
|
+
filename?: string;
|
|
108
|
+
formatMessage?: (message: ConversationMessage, index: number) => string;
|
|
109
|
+
};
|
|
110
|
+
const defaultFormatMessage = (message: ConversationMessage): string => {
|
|
111
|
+
const roleLabel =
|
|
112
|
+
message.role.charAt(0).toUpperCase() + message.role.slice(1);
|
|
113
|
+
return `**${roleLabel}:** ${message.content}`;
|
|
114
|
+
};
|
|
115
|
+
export const messagesToMarkdown = (
|
|
116
|
+
messages: ConversationMessage[],
|
|
117
|
+
formatMessage: (
|
|
118
|
+
message: ConversationMessage,
|
|
119
|
+
index: number,
|
|
120
|
+
) => string = defaultFormatMessage,
|
|
121
|
+
): string => messages.map((msg, i) => formatMessage(msg, i)).join("\n\n");
|
|
122
|
+
export const ConversationDownload = ({
|
|
123
|
+
messages,
|
|
124
|
+
filename = "conversation.md",
|
|
125
|
+
formatMessage = defaultFormatMessage,
|
|
126
|
+
className,
|
|
127
|
+
children,
|
|
128
|
+
...props
|
|
129
|
+
}: ConversationDownloadProps) => {
|
|
130
|
+
const handleDownload = () => {
|
|
131
|
+
const markdown = messagesToMarkdown(messages, formatMessage);
|
|
132
|
+
const blob = new Blob([markdown], { type: "text/markdown" });
|
|
133
|
+
const url = URL.createObjectURL(blob);
|
|
134
|
+
const link = document.createElement("a");
|
|
135
|
+
link.href = url;
|
|
136
|
+
link.download = filename;
|
|
137
|
+
document.body.append(link);
|
|
138
|
+
link.click();
|
|
139
|
+
link.remove();
|
|
140
|
+
URL.revokeObjectURL(url);
|
|
141
|
+
};
|
|
142
|
+
return (
|
|
143
|
+
<Button
|
|
144
|
+
className={cn(
|
|
145
|
+
cn(flatInteractiveClass, "absolute top-4 right-4 rounded-full"),
|
|
146
|
+
className,
|
|
147
|
+
)}
|
|
148
|
+
onClick={handleDownload}
|
|
149
|
+
size="icon"
|
|
150
|
+
type="button"
|
|
151
|
+
variant="outline"
|
|
152
|
+
{...props}
|
|
153
|
+
>
|
|
154
|
+
{children ?? <IconDownload className="size-4" />}
|
|
155
|
+
</Button>
|
|
156
|
+
);
|
|
157
|
+
};
|
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
import type { Experimental_GeneratedImage } from "ai";
|
|
2
|
-
|
|
3
|
-
import { cn } from "../utils/cn";
|
|
4
|
-
|
|
5
|
-
export type ImageProps = Experimental_GeneratedImage & {
|
|
6
|
-
className?: string;
|
|
7
|
-
alt?: string;
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
export const Image = ({
|
|
11
|
-
base64,
|
|
12
|
-
uint8Array: _uint8Array,
|
|
13
|
-
mediaType,
|
|
14
|
-
...props
|
|
15
|
-
}: ImageProps) => (
|
|
16
|
-
<img
|
|
17
|
-
{...props}
|
|
18
|
-
alt={props.alt}
|
|
19
|
-
className={cn(
|
|
20
|
-
"h-auto max-w-full overflow-hidden rounded-md",
|
|
21
|
-
props.className,
|
|
22
|
-
)}
|
|
23
|
-
src={`data:${mediaType};base64,${base64}`}
|
|
24
|
-
/>
|
|
25
|
-
);
|
|
1
|
+
import type { Experimental_GeneratedImage } from "ai";
|
|
2
|
+
|
|
3
|
+
import { cn } from "../utils/cn";
|
|
4
|
+
|
|
5
|
+
export type ImageProps = Experimental_GeneratedImage & {
|
|
6
|
+
className?: string;
|
|
7
|
+
alt?: string;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const Image = ({
|
|
11
|
+
base64,
|
|
12
|
+
uint8Array: _uint8Array,
|
|
13
|
+
mediaType,
|
|
14
|
+
...props
|
|
15
|
+
}: ImageProps) => (
|
|
16
|
+
<img
|
|
17
|
+
{...props}
|
|
18
|
+
alt={props.alt}
|
|
19
|
+
className={cn(
|
|
20
|
+
"h-auto max-w-full overflow-hidden rounded-md",
|
|
21
|
+
props.className,
|
|
22
|
+
)}
|
|
23
|
+
src={`data:${mediaType};base64,${base64}`}
|
|
24
|
+
/>
|
|
25
|
+
);
|