Build & Push Docker Image / build (push) Has been cancelled
Refresh the homepage around new hero/services/roadmap sections, shared design tokens, and updated brand assets. Co-authored-by: Cursor <cursoragent@cursor.com>
157 lines
4.8 KiB
TypeScript
157 lines
4.8 KiB
TypeScript
"use client";
|
|
|
|
import {
|
|
AtSign,
|
|
CheckSquare,
|
|
Mic,
|
|
Paperclip,
|
|
Play,
|
|
Shield,
|
|
} from "lucide-react";
|
|
import { motion, useReducedMotion } from "motion/react";
|
|
import { Button } from "@/components/ui/button";
|
|
import {
|
|
Select,
|
|
SelectContent,
|
|
SelectItem,
|
|
SelectTrigger,
|
|
SelectValue,
|
|
} from "@/components/ui/select";
|
|
import { Textarea } from "@/components/ui/textarea";
|
|
import { agentEase, agentPanelTransition } from "./agent-motion";
|
|
import { cn } from "@/lib/utils";
|
|
import { type } from "@/lib/typography";
|
|
|
|
interface ChatComposerProps {
|
|
value: string;
|
|
onChange: (value: string) => void;
|
|
onSubmit: () => void;
|
|
disabled?: boolean;
|
|
isSending?: boolean;
|
|
className?: string;
|
|
}
|
|
|
|
export function ChatComposer({
|
|
value,
|
|
onChange,
|
|
onSubmit,
|
|
disabled = false,
|
|
isSending = false,
|
|
className,
|
|
}: ChatComposerProps) {
|
|
const canSubmit = value.trim().length > 0 && !disabled && !isSending;
|
|
const reduceMotion = useReducedMotion();
|
|
|
|
function handleKeyDown(event: React.KeyboardEvent<HTMLTextAreaElement>) {
|
|
if (event.key === "Enter" && !event.shiftKey) {
|
|
event.preventDefault();
|
|
if (canSubmit) onSubmit();
|
|
}
|
|
}
|
|
|
|
return (
|
|
<motion.div
|
|
className={cn("shrink-0 p-3 sm:p-4", className)}
|
|
initial={{ opacity: 0, y: 12 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={agentPanelTransition}
|
|
>
|
|
<motion.div
|
|
className="rounded-2xl border bg-card p-3 shadow-md sm:p-4"
|
|
layout
|
|
transition={agentPanelTransition}
|
|
whileHover={
|
|
reduceMotion ? undefined : { boxShadow: "0 8px 30px -8px rgba(1, 121, 111, 0.12)" }
|
|
}
|
|
>
|
|
<Textarea
|
|
value={value}
|
|
onChange={(event) => onChange(event.target.value)}
|
|
onKeyDown={handleKeyDown}
|
|
placeholder="امتحان کنید: «یک سناریو پاسخگویی خودکار برای مشتریان طراحی کن»..."
|
|
disabled={disabled || isSending}
|
|
rows={2}
|
|
className={cn(
|
|
"min-h-[4.5rem] resize-none border-0 bg-transparent px-1 py-1 text-start shadow-none transition-[min-height] duration-300 ease-out focus-visible:ring-0",
|
|
type["body-sm"]
|
|
)}
|
|
aria-label="دستور عامل"
|
|
/>
|
|
|
|
<div className="mt-2 flex items-center justify-between gap-2 border-t pt-2">
|
|
<div className="flex items-center gap-0.5 sm:gap-1">
|
|
<Button
|
|
type="button"
|
|
variant="ghost"
|
|
size="icon-sm"
|
|
className="text-muted-foreground"
|
|
aria-label="پیوست"
|
|
>
|
|
<Paperclip className="size-4" aria-hidden />
|
|
</Button>
|
|
<Button
|
|
type="button"
|
|
variant="ghost"
|
|
size="icon-sm"
|
|
className="text-muted-foreground"
|
|
aria-label="اشاره"
|
|
>
|
|
<AtSign className="size-4" aria-hidden />
|
|
</Button>
|
|
<Button
|
|
type="button"
|
|
variant="ghost"
|
|
size="icon-sm"
|
|
className="text-muted-foreground"
|
|
aria-label="وظایف"
|
|
>
|
|
<CheckSquare className="size-4" aria-hidden />
|
|
</Button>
|
|
<Select defaultValue="overall">
|
|
<SelectTrigger
|
|
size="sm"
|
|
className="h-8 gap-1 border-0 bg-transparent px-2 shadow-none"
|
|
>
|
|
<Shield className="size-3.5 text-muted-foreground" aria-hidden />
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="overall">کلی</SelectItem>
|
|
<SelectItem value="focused">متمرکز</SelectItem>
|
|
<SelectItem value="creative">خلاقانه</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
|
|
<div className="flex items-center gap-1.5">
|
|
<Button
|
|
type="button"
|
|
variant="ghost"
|
|
size="icon-sm"
|
|
className="text-muted-foreground"
|
|
aria-label="ضبط صدا"
|
|
>
|
|
<Mic className="size-4" aria-hidden />
|
|
</Button>
|
|
<motion.div
|
|
whileHover={reduceMotion ? undefined : { scale: 1.04 }}
|
|
whileTap={reduceMotion ? undefined : { scale: 0.96 }}
|
|
transition={{ duration: 0.18, ease: agentEase }}
|
|
>
|
|
<Button
|
|
type="button"
|
|
onClick={onSubmit}
|
|
disabled={!canSubmit}
|
|
size="sm"
|
|
>
|
|
<Play className="fill-current" aria-hidden />
|
|
اجرا
|
|
</Button>
|
|
</motion.div>
|
|
</div>
|
|
</div>
|
|
</motion.div>
|
|
</motion.div>
|
|
);
|
|
}
|