@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,251 +1,251 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { useState, type ReactNode } from "react";
|
|
4
|
-
import { motion, AnimatePresence } from "motion/react";
|
|
5
|
-
import { cn } from "../utils/cn";
|
|
6
|
-
import {
|
|
7
|
-
flatPanelSubtleClass,
|
|
8
|
-
flatPanelSubtleHoverClass,
|
|
9
|
-
} from "../ui/surface-classes";
|
|
10
|
-
import { Button } from "../ui/button";
|
|
11
|
-
import { Badge } from "../ui/badge";
|
|
12
|
-
import { ScrollArea } from "../ui/scroll-area";
|
|
13
|
-
import { Textarea } from "../ui/textarea";
|
|
14
|
-
import {
|
|
15
|
-
IconChevronDown,
|
|
16
|
-
IconEdit,
|
|
17
|
-
IconTrash,
|
|
18
|
-
IconCheck,
|
|
19
|
-
IconX,
|
|
20
|
-
IconStack2,
|
|
21
|
-
} from "@tabler/icons-react";
|
|
22
|
-
|
|
23
|
-
export interface QueueProps {
|
|
24
|
-
className?: string;
|
|
25
|
-
children?: ReactNode;
|
|
26
|
-
count: number;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export function Queue({ className, children, count }: QueueProps) {
|
|
30
|
-
return (
|
|
31
|
-
<AnimatePresence initial={false}>
|
|
32
|
-
{count > 0 ? (
|
|
33
|
-
<motion.div
|
|
34
|
-
key="queue"
|
|
35
|
-
initial={{ height: 0, opacity: 0 }}
|
|
36
|
-
animate={{ height: "auto", opacity: 1 }}
|
|
37
|
-
exit={{ height: 0, opacity: 0 }}
|
|
38
|
-
transition={{ type: "spring", bounce: 0, duration: 0.35 }}
|
|
39
|
-
className={cn("w-full overflow-hidden", className)}
|
|
40
|
-
>
|
|
41
|
-
{children}
|
|
42
|
-
</motion.div>
|
|
43
|
-
) : null}
|
|
44
|
-
</AnimatePresence>
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export interface QueueHeaderProps {
|
|
49
|
-
count: number;
|
|
50
|
-
isOpen: boolean;
|
|
51
|
-
onToggle: () => void;
|
|
52
|
-
className?: string;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export function QueueHeader({
|
|
56
|
-
count,
|
|
57
|
-
isOpen,
|
|
58
|
-
onToggle,
|
|
59
|
-
className,
|
|
60
|
-
}: QueueHeaderProps) {
|
|
61
|
-
return (
|
|
62
|
-
<button
|
|
63
|
-
type="button"
|
|
64
|
-
onClick={onToggle}
|
|
65
|
-
className={cn(
|
|
66
|
-
"flex items-center justify-between w-full",
|
|
67
|
-
"px-4 py-2.5",
|
|
68
|
-
flatPanelSubtleClass,
|
|
69
|
-
flatPanelSubtleHoverClass,
|
|
70
|
-
"rounded-t-2xl",
|
|
71
|
-
"group",
|
|
72
|
-
className,
|
|
73
|
-
)}
|
|
74
|
-
>
|
|
75
|
-
<div className="flex items-center gap-2">
|
|
76
|
-
<IconStack2 size={16} className="text-muted" />
|
|
77
|
-
<span className="text-sm font-medium text-foreground">
|
|
78
|
-
Queued Messages
|
|
79
|
-
</span>
|
|
80
|
-
<Badge variant="secondary" className="text-xs">
|
|
81
|
-
{count}
|
|
82
|
-
</Badge>
|
|
83
|
-
</div>
|
|
84
|
-
<IconChevronDown
|
|
85
|
-
size={16}
|
|
86
|
-
className={cn(
|
|
87
|
-
"text-muted transition-transform duration-200 ease-out",
|
|
88
|
-
isOpen && "rotate-180",
|
|
89
|
-
)}
|
|
90
|
-
/>
|
|
91
|
-
</button>
|
|
92
|
-
);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export interface QueueListProps {
|
|
96
|
-
className?: string;
|
|
97
|
-
children?: ReactNode;
|
|
98
|
-
open?: boolean;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
export function QueueList({
|
|
102
|
-
className,
|
|
103
|
-
children,
|
|
104
|
-
open = true,
|
|
105
|
-
}: QueueListProps) {
|
|
106
|
-
return (
|
|
107
|
-
<AnimatePresence initial={false}>
|
|
108
|
-
{open ? (
|
|
109
|
-
<motion.div
|
|
110
|
-
key="queue-list"
|
|
111
|
-
initial={{ height: 0, opacity: 0 }}
|
|
112
|
-
animate={{ height: "auto", opacity: 1 }}
|
|
113
|
-
exit={{ height: 0, opacity: 0 }}
|
|
114
|
-
transition={{ type: "spring", bounce: 0, duration: 0.35 }}
|
|
115
|
-
className="overflow-hidden"
|
|
116
|
-
>
|
|
117
|
-
<div className={cn(flatPanelSubtleClass, "rounded-b-2xl", className)}>
|
|
118
|
-
<ScrollArea className="max-h-[240px]">
|
|
119
|
-
<div className="p-2 space-y-1.5">{children}</div>
|
|
120
|
-
</ScrollArea>
|
|
121
|
-
</div>
|
|
122
|
-
</motion.div>
|
|
123
|
-
) : null}
|
|
124
|
-
</AnimatePresence>
|
|
125
|
-
);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
export interface QueueItemProps {
|
|
129
|
-
text: string;
|
|
130
|
-
model?: string;
|
|
131
|
-
personalityName?: string;
|
|
132
|
-
personaName?: string;
|
|
133
|
-
personaImage?: string;
|
|
134
|
-
onEdit: (newText: string) => void;
|
|
135
|
-
onRemove: () => void;
|
|
136
|
-
className?: string;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
export function QueueItem({
|
|
140
|
-
text,
|
|
141
|
-
model,
|
|
142
|
-
personalityName,
|
|
143
|
-
personaName,
|
|
144
|
-
personaImage,
|
|
145
|
-
onEdit,
|
|
146
|
-
onRemove,
|
|
147
|
-
className,
|
|
148
|
-
}: QueueItemProps) {
|
|
149
|
-
const [isEditing, setIsEditing] = useState(false);
|
|
150
|
-
const [editText, setEditText] = useState(text);
|
|
151
|
-
|
|
152
|
-
const handleSaveEdit = () => {
|
|
153
|
-
if (editText.trim()) {
|
|
154
|
-
onEdit(editText.trim());
|
|
155
|
-
setIsEditing(false);
|
|
156
|
-
}
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
const handleCancelEdit = () => {
|
|
160
|
-
setEditText(text);
|
|
161
|
-
setIsEditing(false);
|
|
162
|
-
};
|
|
163
|
-
|
|
164
|
-
return (
|
|
165
|
-
<div
|
|
166
|
-
className={cn(
|
|
167
|
-
"group relative",
|
|
168
|
-
"p-2.5 rounded-lg",
|
|
169
|
-
"transition-[background-color,transform] duration-150 ease-out active:scale-[0.99] hover:bg-surface-secondary",
|
|
170
|
-
className,
|
|
171
|
-
)}
|
|
172
|
-
>
|
|
173
|
-
{isEditing ? (
|
|
174
|
-
<div className="flex flex-col gap-2">
|
|
175
|
-
<Textarea
|
|
176
|
-
value={editText}
|
|
177
|
-
onChange={(e) => setEditText(e.currentTarget.value)}
|
|
178
|
-
className="min-h-[60px] text-sm"
|
|
179
|
-
autoFocus
|
|
180
|
-
/>
|
|
181
|
-
<div className="flex justify-end gap-2">
|
|
182
|
-
<Button
|
|
183
|
-
size="sm"
|
|
184
|
-
variant="ghost"
|
|
185
|
-
onClick={handleCancelEdit}
|
|
186
|
-
className="h-7 px-2"
|
|
187
|
-
>
|
|
188
|
-
<IconX size={14} />
|
|
189
|
-
<span className="ml-1 text-xs">Cancel</span>
|
|
190
|
-
</Button>
|
|
191
|
-
<Button
|
|
192
|
-
size="sm"
|
|
193
|
-
onClick={handleSaveEdit}
|
|
194
|
-
disabled={!editText.trim()}
|
|
195
|
-
className="h-7 px-2"
|
|
196
|
-
>
|
|
197
|
-
<IconCheck size={14} />
|
|
198
|
-
<span className="ml-1 text-xs">Save</span>
|
|
199
|
-
</Button>
|
|
200
|
-
</div>
|
|
201
|
-
</div>
|
|
202
|
-
) : (
|
|
203
|
-
<div className="flex items-center gap-2">
|
|
204
|
-
{personaImage && (
|
|
205
|
-
<img
|
|
206
|
-
src={personaImage}
|
|
207
|
-
alt={personaName || "Persona"}
|
|
208
|
-
className="size-5 rounded-full shrink-0"
|
|
209
|
-
/>
|
|
210
|
-
)}
|
|
211
|
-
<p className="text-sm text-foreground/90 flex-1 truncate">{text}</p>
|
|
212
|
-
<div className="flex items-center gap-1.5 shrink-0">
|
|
213
|
-
{model && (
|
|
214
|
-
<Badge variant="outline" className="text-xs h-5 px-1.5">
|
|
215
|
-
{model.split("/").pop()}
|
|
216
|
-
</Badge>
|
|
217
|
-
)}
|
|
218
|
-
{personalityName && (
|
|
219
|
-
<Badge variant="outline" className="text-xs h-5 px-1.5">
|
|
220
|
-
{personalityName}
|
|
221
|
-
</Badge>
|
|
222
|
-
)}
|
|
223
|
-
{personaName && !personaImage && (
|
|
224
|
-
<Badge variant="outline" className="text-xs h-5 px-1.5">
|
|
225
|
-
{personaName}
|
|
226
|
-
</Badge>
|
|
227
|
-
)}
|
|
228
|
-
</div>
|
|
229
|
-
<div className="flex items-center gap-1 shrink-0 opacity-0 group-hover:opacity-100 transition-opacity">
|
|
230
|
-
<Button
|
|
231
|
-
size="sm"
|
|
232
|
-
variant="ghost"
|
|
233
|
-
onClick={() => setIsEditing(true)}
|
|
234
|
-
className="h-6 w-6 p-0"
|
|
235
|
-
>
|
|
236
|
-
<IconEdit size={13} />
|
|
237
|
-
</Button>
|
|
238
|
-
<Button
|
|
239
|
-
size="sm"
|
|
240
|
-
variant="ghost"
|
|
241
|
-
onClick={onRemove}
|
|
242
|
-
className="h-6 w-6 p-0 text-danger hover:text-danger"
|
|
243
|
-
>
|
|
244
|
-
<IconTrash size={13} />
|
|
245
|
-
</Button>
|
|
246
|
-
</div>
|
|
247
|
-
</div>
|
|
248
|
-
)}
|
|
249
|
-
</div>
|
|
250
|
-
);
|
|
251
|
-
}
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useState, type ReactNode } from "react";
|
|
4
|
+
import { motion, AnimatePresence } from "motion/react";
|
|
5
|
+
import { cn } from "../utils/cn";
|
|
6
|
+
import {
|
|
7
|
+
flatPanelSubtleClass,
|
|
8
|
+
flatPanelSubtleHoverClass,
|
|
9
|
+
} from "../ui/surface-classes";
|
|
10
|
+
import { Button } from "../ui/button";
|
|
11
|
+
import { Badge } from "../ui/badge";
|
|
12
|
+
import { ScrollArea } from "../ui/scroll-area";
|
|
13
|
+
import { Textarea } from "../ui/textarea";
|
|
14
|
+
import {
|
|
15
|
+
IconChevronDown,
|
|
16
|
+
IconEdit,
|
|
17
|
+
IconTrash,
|
|
18
|
+
IconCheck,
|
|
19
|
+
IconX,
|
|
20
|
+
IconStack2,
|
|
21
|
+
} from "@tabler/icons-react";
|
|
22
|
+
|
|
23
|
+
export interface QueueProps {
|
|
24
|
+
className?: string;
|
|
25
|
+
children?: ReactNode;
|
|
26
|
+
count: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function Queue({ className, children, count }: QueueProps) {
|
|
30
|
+
return (
|
|
31
|
+
<AnimatePresence initial={false}>
|
|
32
|
+
{count > 0 ? (
|
|
33
|
+
<motion.div
|
|
34
|
+
key="queue"
|
|
35
|
+
initial={{ height: 0, opacity: 0 }}
|
|
36
|
+
animate={{ height: "auto", opacity: 1 }}
|
|
37
|
+
exit={{ height: 0, opacity: 0 }}
|
|
38
|
+
transition={{ type: "spring", bounce: 0, duration: 0.35 }}
|
|
39
|
+
className={cn("w-full overflow-hidden", className)}
|
|
40
|
+
>
|
|
41
|
+
{children}
|
|
42
|
+
</motion.div>
|
|
43
|
+
) : null}
|
|
44
|
+
</AnimatePresence>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface QueueHeaderProps {
|
|
49
|
+
count: number;
|
|
50
|
+
isOpen: boolean;
|
|
51
|
+
onToggle: () => void;
|
|
52
|
+
className?: string;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function QueueHeader({
|
|
56
|
+
count,
|
|
57
|
+
isOpen,
|
|
58
|
+
onToggle,
|
|
59
|
+
className,
|
|
60
|
+
}: QueueHeaderProps) {
|
|
61
|
+
return (
|
|
62
|
+
<button
|
|
63
|
+
type="button"
|
|
64
|
+
onClick={onToggle}
|
|
65
|
+
className={cn(
|
|
66
|
+
"flex items-center justify-between w-full",
|
|
67
|
+
"px-4 py-2.5",
|
|
68
|
+
flatPanelSubtleClass,
|
|
69
|
+
flatPanelSubtleHoverClass,
|
|
70
|
+
"rounded-t-2xl",
|
|
71
|
+
"group",
|
|
72
|
+
className,
|
|
73
|
+
)}
|
|
74
|
+
>
|
|
75
|
+
<div className="flex items-center gap-2">
|
|
76
|
+
<IconStack2 size={16} className="text-muted" />
|
|
77
|
+
<span className="text-sm font-medium text-foreground">
|
|
78
|
+
Queued Messages
|
|
79
|
+
</span>
|
|
80
|
+
<Badge variant="secondary" className="text-xs">
|
|
81
|
+
{count}
|
|
82
|
+
</Badge>
|
|
83
|
+
</div>
|
|
84
|
+
<IconChevronDown
|
|
85
|
+
size={16}
|
|
86
|
+
className={cn(
|
|
87
|
+
"text-muted transition-transform duration-200 ease-out",
|
|
88
|
+
isOpen && "rotate-180",
|
|
89
|
+
)}
|
|
90
|
+
/>
|
|
91
|
+
</button>
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export interface QueueListProps {
|
|
96
|
+
className?: string;
|
|
97
|
+
children?: ReactNode;
|
|
98
|
+
open?: boolean;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function QueueList({
|
|
102
|
+
className,
|
|
103
|
+
children,
|
|
104
|
+
open = true,
|
|
105
|
+
}: QueueListProps) {
|
|
106
|
+
return (
|
|
107
|
+
<AnimatePresence initial={false}>
|
|
108
|
+
{open ? (
|
|
109
|
+
<motion.div
|
|
110
|
+
key="queue-list"
|
|
111
|
+
initial={{ height: 0, opacity: 0 }}
|
|
112
|
+
animate={{ height: "auto", opacity: 1 }}
|
|
113
|
+
exit={{ height: 0, opacity: 0 }}
|
|
114
|
+
transition={{ type: "spring", bounce: 0, duration: 0.35 }}
|
|
115
|
+
className="overflow-hidden"
|
|
116
|
+
>
|
|
117
|
+
<div className={cn(flatPanelSubtleClass, "rounded-b-2xl", className)}>
|
|
118
|
+
<ScrollArea className="max-h-[240px]">
|
|
119
|
+
<div className="p-2 space-y-1.5">{children}</div>
|
|
120
|
+
</ScrollArea>
|
|
121
|
+
</div>
|
|
122
|
+
</motion.div>
|
|
123
|
+
) : null}
|
|
124
|
+
</AnimatePresence>
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export interface QueueItemProps {
|
|
129
|
+
text: string;
|
|
130
|
+
model?: string;
|
|
131
|
+
personalityName?: string;
|
|
132
|
+
personaName?: string;
|
|
133
|
+
personaImage?: string;
|
|
134
|
+
onEdit: (newText: string) => void;
|
|
135
|
+
onRemove: () => void;
|
|
136
|
+
className?: string;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export function QueueItem({
|
|
140
|
+
text,
|
|
141
|
+
model,
|
|
142
|
+
personalityName,
|
|
143
|
+
personaName,
|
|
144
|
+
personaImage,
|
|
145
|
+
onEdit,
|
|
146
|
+
onRemove,
|
|
147
|
+
className,
|
|
148
|
+
}: QueueItemProps) {
|
|
149
|
+
const [isEditing, setIsEditing] = useState(false);
|
|
150
|
+
const [editText, setEditText] = useState(text);
|
|
151
|
+
|
|
152
|
+
const handleSaveEdit = () => {
|
|
153
|
+
if (editText.trim()) {
|
|
154
|
+
onEdit(editText.trim());
|
|
155
|
+
setIsEditing(false);
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const handleCancelEdit = () => {
|
|
160
|
+
setEditText(text);
|
|
161
|
+
setIsEditing(false);
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
return (
|
|
165
|
+
<div
|
|
166
|
+
className={cn(
|
|
167
|
+
"group relative",
|
|
168
|
+
"p-2.5 rounded-lg",
|
|
169
|
+
"transition-[background-color,transform] duration-150 ease-out active:scale-[0.99] hover:bg-surface-secondary",
|
|
170
|
+
className,
|
|
171
|
+
)}
|
|
172
|
+
>
|
|
173
|
+
{isEditing ? (
|
|
174
|
+
<div className="flex flex-col gap-2">
|
|
175
|
+
<Textarea
|
|
176
|
+
value={editText}
|
|
177
|
+
onChange={(e) => setEditText(e.currentTarget.value)}
|
|
178
|
+
className="min-h-[60px] text-sm"
|
|
179
|
+
autoFocus
|
|
180
|
+
/>
|
|
181
|
+
<div className="flex justify-end gap-2">
|
|
182
|
+
<Button
|
|
183
|
+
size="sm"
|
|
184
|
+
variant="ghost"
|
|
185
|
+
onClick={handleCancelEdit}
|
|
186
|
+
className="h-7 px-2"
|
|
187
|
+
>
|
|
188
|
+
<IconX size={14} />
|
|
189
|
+
<span className="ml-1 text-xs">Cancel</span>
|
|
190
|
+
</Button>
|
|
191
|
+
<Button
|
|
192
|
+
size="sm"
|
|
193
|
+
onClick={handleSaveEdit}
|
|
194
|
+
disabled={!editText.trim()}
|
|
195
|
+
className="h-7 px-2"
|
|
196
|
+
>
|
|
197
|
+
<IconCheck size={14} />
|
|
198
|
+
<span className="ml-1 text-xs">Save</span>
|
|
199
|
+
</Button>
|
|
200
|
+
</div>
|
|
201
|
+
</div>
|
|
202
|
+
) : (
|
|
203
|
+
<div className="flex items-center gap-2">
|
|
204
|
+
{personaImage && (
|
|
205
|
+
<img
|
|
206
|
+
src={personaImage}
|
|
207
|
+
alt={personaName || "Persona"}
|
|
208
|
+
className="size-5 rounded-full shrink-0"
|
|
209
|
+
/>
|
|
210
|
+
)}
|
|
211
|
+
<p className="text-sm text-foreground/90 flex-1 truncate">{text}</p>
|
|
212
|
+
<div className="flex items-center gap-1.5 shrink-0">
|
|
213
|
+
{model && (
|
|
214
|
+
<Badge variant="outline" className="text-xs h-5 px-1.5">
|
|
215
|
+
{model.split("/").pop()}
|
|
216
|
+
</Badge>
|
|
217
|
+
)}
|
|
218
|
+
{personalityName && (
|
|
219
|
+
<Badge variant="outline" className="text-xs h-5 px-1.5">
|
|
220
|
+
{personalityName}
|
|
221
|
+
</Badge>
|
|
222
|
+
)}
|
|
223
|
+
{personaName && !personaImage && (
|
|
224
|
+
<Badge variant="outline" className="text-xs h-5 px-1.5">
|
|
225
|
+
{personaName}
|
|
226
|
+
</Badge>
|
|
227
|
+
)}
|
|
228
|
+
</div>
|
|
229
|
+
<div className="flex items-center gap-1 shrink-0 opacity-0 group-hover:opacity-100 transition-opacity">
|
|
230
|
+
<Button
|
|
231
|
+
size="sm"
|
|
232
|
+
variant="ghost"
|
|
233
|
+
onClick={() => setIsEditing(true)}
|
|
234
|
+
className="h-6 w-6 p-0"
|
|
235
|
+
>
|
|
236
|
+
<IconEdit size={13} />
|
|
237
|
+
</Button>
|
|
238
|
+
<Button
|
|
239
|
+
size="sm"
|
|
240
|
+
variant="ghost"
|
|
241
|
+
onClick={onRemove}
|
|
242
|
+
className="h-6 w-6 p-0 text-danger hover:text-danger"
|
|
243
|
+
>
|
|
244
|
+
<IconTrash size={13} />
|
|
245
|
+
</Button>
|
|
246
|
+
</div>
|
|
247
|
+
</div>
|
|
248
|
+
)}
|
|
249
|
+
</div>
|
|
250
|
+
);
|
|
251
|
+
}
|