2024-10-30 16:01:24 +05:30
|
|
|
'use client';
|
|
|
|
|
|
2024-11-01 15:31:54 +05:30
|
|
|
import { AnimatePresence, motion } from 'framer-motion';
|
2024-10-30 16:01:24 +05:30
|
|
|
import { useState } from 'react';
|
2024-11-01 15:31:54 +05:30
|
|
|
import { useWindowSize } from 'usehooks-ts';
|
2024-10-30 16:01:24 +05:30
|
|
|
|
2024-11-15 12:18:17 -05:00
|
|
|
import type { UISuggestion } from '@/lib/editor/suggestions';
|
2024-10-30 16:01:24 +05:30
|
|
|
|
|
|
|
|
import { CrossIcon, MessageIcon } from './icons';
|
2024-11-15 10:14:25 -05:00
|
|
|
import { Button } from './ui/button';
|
2024-12-16 18:14:40 +05:30
|
|
|
import { cn } from '@/lib/utils';
|
2025-08-28 14:15:36 +01:00
|
|
|
import type { ArtifactKind } from './artifact';
|
2024-10-30 16:01:24 +05:30
|
|
|
|
|
|
|
|
export const Suggestion = ({
|
|
|
|
|
suggestion,
|
|
|
|
|
onApply,
|
2025-02-13 08:25:57 -08:00
|
|
|
artifactKind,
|
2024-10-30 16:01:24 +05:30
|
|
|
}: {
|
|
|
|
|
suggestion: UISuggestion;
|
|
|
|
|
onApply: () => void;
|
2025-02-13 08:25:57 -08:00
|
|
|
artifactKind: ArtifactKind;
|
2024-10-30 16:01:24 +05:30
|
|
|
}) => {
|
|
|
|
|
const [isExpanded, setIsExpanded] = useState(false);
|
2024-10-30 20:36:45 +05:30
|
|
|
const { width: windowWidth } = useWindowSize();
|
2024-10-30 16:01:24 +05:30
|
|
|
|
2024-11-01 15:31:54 +05:30
|
|
|
return (
|
|
|
|
|
<AnimatePresence>
|
|
|
|
|
{!isExpanded ? (
|
|
|
|
|
<motion.div
|
2025-09-09 15:44:07 -04:00
|
|
|
className={cn('cursor-pointer p-1 text-muted-foreground', {
|
|
|
|
|
'-right-8 absolute': artifactKind === 'text',
|
2025-02-13 08:25:57 -08:00
|
|
|
'sticky top-0 right-4': artifactKind === 'code',
|
2024-12-16 18:14:40 +05:30
|
|
|
})}
|
2024-10-30 16:01:24 +05:30
|
|
|
onClick={() => {
|
2024-11-01 15:31:54 +05:30
|
|
|
setIsExpanded(true);
|
2024-10-30 16:01:24 +05:30
|
|
|
}}
|
2024-11-01 15:31:54 +05:30
|
|
|
whileHover={{ scale: 1.1 }}
|
2024-10-30 16:01:24 +05:30
|
|
|
>
|
2024-11-01 15:31:54 +05:30
|
|
|
<MessageIcon size={windowWidth && windowWidth < 768 ? 16 : 14} />
|
|
|
|
|
</motion.div>
|
|
|
|
|
) : (
|
|
|
|
|
<motion.div
|
|
|
|
|
key={suggestion.id}
|
2025-09-09 15:44:07 -04:00
|
|
|
className="-right-12 md:-right-16 absolute z-50 flex w-56 flex-col gap-3 rounded-2xl border bg-background p-3 font-sans text-sm shadow-xl"
|
2024-11-01 15:31:54 +05:30
|
|
|
transition={{ type: 'spring', stiffness: 500, damping: 30 }}
|
|
|
|
|
initial={{ opacity: 0, y: -10 }}
|
|
|
|
|
animate={{ opacity: 1, y: -20 }}
|
|
|
|
|
exit={{ opacity: 0, y: -10 }}
|
|
|
|
|
whileHover={{ scale: 1.05 }}
|
|
|
|
|
>
|
2025-09-09 15:44:07 -04:00
|
|
|
<div className="flex flex-row items-center justify-between">
|
|
|
|
|
<div className="flex flex-row items-center gap-2">
|
|
|
|
|
<div className="size-4 rounded-full bg-muted-foreground/25" />
|
2024-11-01 15:31:54 +05:30
|
|
|
<div className="font-medium">Assistant</div>
|
|
|
|
|
</div>
|
2024-11-15 13:00:15 -05:00
|
|
|
<button
|
|
|
|
|
type="button"
|
2025-09-09 15:44:07 -04:00
|
|
|
className="cursor-pointer text-gray-500 text-xs"
|
2024-11-01 15:31:54 +05:30
|
|
|
onClick={() => {
|
|
|
|
|
setIsExpanded(false);
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<CrossIcon size={12} />
|
2024-11-15 13:00:15 -05:00
|
|
|
</button>
|
2024-11-01 15:31:54 +05:30
|
|
|
</div>
|
|
|
|
|
<div>{suggestion.description}</div>
|
|
|
|
|
<Button
|
|
|
|
|
variant="outline"
|
2025-09-09 15:44:07 -04:00
|
|
|
className="w-fit rounded-full px-3 py-1.5"
|
2024-11-01 15:31:54 +05:30
|
|
|
onClick={onApply}
|
|
|
|
|
>
|
|
|
|
|
Apply
|
|
|
|
|
</Button>
|
|
|
|
|
</motion.div>
|
|
|
|
|
)}
|
|
|
|
|
</AnimatePresence>
|
2024-10-30 16:01:24 +05:30
|
|
|
);
|
|
|
|
|
};
|