content/components/ai/ai-02.tsx
'use client';
import {
IconAlertTriangle,
IconArrowUp,
IconCloud,
IconFileSpark,
IconGauge,
IconPhotoScan,
} from '@tabler/icons-react';
import { useRef, useState } from 'react';
import { Button } from '@/components/ui/button';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
import { Textarea } from '@/components/ui/textarea';
const PROMPTS = [
{
icon: IconFileSpark,
text: 'Write documentation',
prompt:
'Write comprehensive documentation for this codebase, including setup instructions, API references, and usage examples.',
},
{
icon: IconGauge,
text: 'Optimize performance',
prompt:
'Analyze the codebase for performance bottlenecks and suggest optimizations to improve loading times and runtime efficiency.',
},
{
icon: IconAlertTriangle,
text: 'Find and fix 3 bugs',
prompt:
'Scan through the codebase to identify and fix 3 critical bugs, providing detailed explanations for each fix.',
},
];
const MODELS = [
{
value: 'gpt-5',
name: 'GPT-5',
description: 'Most advanced model',
max: true,
},
{
value: 'gpt-4o',
name: 'GPT-4o',
description: 'Fast and capable',
},
{
value: 'claude-3.5',
name: 'Claude 3.5 Sonnet',
description: 'Great for coding tasks',
},
];
export default function Ai02() {
const [inputValue, setInputValue] = useState('');
const [modelValue, setModelValue] = useState(MODELS[0].value);
const selectedModel = MODELS.find((m) => m.value === modelValue) ?? MODELS[0];
const inputRef = useRef<HTMLTextAreaElement>(null);
const handlePromptClick = (prompt: string) => {
if (inputRef.current) {
inputRef.current.value = prompt;
setInputValue(prompt);
inputRef.current.focus();
}
};
const maxBadge = (
<span className="inline-flex h-[15px] items-center rounded-[4px] border border-border/80 bg-muted/40 px-[5px]">
<span
className="font-bold text-[9px] uppercase leading-none tracking-[0.06em]"
style={{
background:
'linear-gradient(to right, rgb(129, 161, 193), rgb(125, 124, 155))',
WebkitBackgroundClip: 'text',
WebkitTextFillColor: 'transparent',
}}
>
MAX
</span>
</span>
);
return (
<div className="flex w-[calc(42rem-5rem)] flex-col gap-4">
<div className="flex min-h-[120px] cursor-text flex-col rounded-2xl border border-border bg-card shadow-lg">
<div className="relative max-h-[258px] flex-1 overflow-y-auto">
<Textarea
className="min-h-[48.4px] w-full resize-none whitespace-pre-wrap break-words border-0 bg-transparent! p-3 text-[16px] text-foreground shadow-none outline-none transition-[padding] duration-200 ease-in-out focus-visible:ring-0 focus-visible:ring-offset-0"
onChange={(e) => setInputValue(e.target.value)}
placeholder="Ask anything"
ref={inputRef}
value={inputValue}
/>
</div>
<div className="flex min-h-[40px] items-center gap-2 p-2 pb-1">
<div className="flex size-7 items-center justify-center rounded-full bg-muted">
<IconCloud className="size-4 text-muted-foreground" />
</div>
<Select
onValueChange={(value) => value && setModelValue(value)}
value={modelValue}
>
<SelectTrigger
className="h-7 rounded-full border-transparent bg-transparent px-2 font-medium text-foreground text-sm shadow-none transition-[background-color,color] hover:bg-muted aria-expanded:bg-muted dark:bg-transparent dark:hover:bg-muted"
size="sm"
>
<SelectValue>
<span className="flex items-center gap-1.5">
<span>{selectedModel.name}</span>
{selectedModel.max && maxBadge}
</span>
</SelectValue>
</SelectTrigger>
<SelectContent
align="start"
alignItemWithTrigger={false}
className="w-64 p-1"
sideOffset={6}
>
{MODELS.map((model) => (
<SelectItem
className="rounded-md py-1.5"
key={model.value}
value={model.value}
>
<span className="flex flex-col gap-0.5">
<span className="flex items-center gap-1.5 font-medium">
{model.name}
{model.max && maxBadge}
</span>
<span className="text-muted-foreground text-xs">
{model.description}
</span>
</span>
</SelectItem>
))}
</SelectContent>
</Select>
<div className="ml-auto flex items-center gap-1.5">
<Button
aria-label="Attach images"
className="rounded-full text-muted-foreground transition-[background-color,color] hover:text-foreground"
size="icon-sm"
title="Attach images"
variant="ghost"
>
<IconPhotoScan className="size-4.5" />
</Button>
<Button
aria-label="Send message"
className="rounded-full transition-[background-color,scale,opacity] active:not-disabled:scale-[0.96]"
disabled={!inputValue}
size="icon-sm"
>
<IconArrowUp className="size-4" />
</Button>
</div>
</div>
</div>
<div className="flex flex-wrap justify-center gap-2">
{PROMPTS.map((button) => {
const IconComponent = button.icon;
return (
<Button
className="h-8 gap-1.5 rounded-full border-border bg-background pr-3.5 pl-2.5 font-normal text-foreground text-sm shadow-[0_1px_2px_oklch(0_0_0/0.05)] transition-[background-color,color,scale,box-shadow] duration-150 ease-out hover:bg-muted active:scale-[0.96] dark:bg-muted/40 dark:hover:bg-muted"
key={button.text}
onClick={() => handlePromptClick(button.prompt)}
variant="outline"
>
<IconComponent
className="size-4 text-muted-foreground transition-colors duration-150 group-hover/button:text-foreground"
strokeWidth={1.75}
/>
{button.text}
</Button>
);
})}
</div>
</div>
);
}