Introduce a footer-triggered Version History drawer with categorized release notes, hardcoded release data, and semver tracking at v0.3.0. Also ships blog blockquote/table styling, site-wide typography, home page image updates, header refinements, and related bug fixes. Co-authored-by: Cursor <cursoragent@cursor.com>
191 lines
6.2 KiB
TypeScript
191 lines
6.2 KiB
TypeScript
"use client";
|
||
|
||
import { FileText, Mail, Plus, X } from "lucide-react";
|
||
import { motion } from "motion/react";
|
||
import { Button } from "@/components/ui/button";
|
||
import {
|
||
AGENT_AUDIENCE_TAGS,
|
||
AGENT_PREVIEW_TAGS,
|
||
AGENT_WORKSPACE_TITLE,
|
||
} from "./playground-content";
|
||
import {
|
||
agentFadeUp,
|
||
agentLayoutTransition,
|
||
agentSlideInEnd,
|
||
agentStaggerContainer,
|
||
} from "./agent-motion";
|
||
import { cn } from "@/lib/utils";
|
||
import { type } from "@/lib/typography";
|
||
|
||
interface AgentPreviewPanelProps {
|
||
className?: string;
|
||
onClose?: () => void;
|
||
}
|
||
|
||
function TagChip({ label }: { label: string }) {
|
||
return (
|
||
<motion.span
|
||
className={cn(
|
||
"inline-flex items-center gap-1 rounded-md border bg-muted/60 px-2 py-1",
|
||
type.caption
|
||
)}
|
||
variants={agentFadeUp}
|
||
custom={index}
|
||
>
|
||
{label}
|
||
<X className="size-3 opacity-50" aria-hidden />
|
||
</motion.span>
|
||
);
|
||
}
|
||
|
||
function AudienceSection({
|
||
title,
|
||
tags,
|
||
}: {
|
||
title: string;
|
||
tags: readonly string[];
|
||
}) {
|
||
return (
|
||
<motion.div className="space-y-2" variants={agentFadeUp}>
|
||
<p className={cn(type.caption, "font-medium uppercase tracking-wide text-muted-foreground")}>
|
||
{title}
|
||
</p>
|
||
<motion.div
|
||
className="flex flex-wrap gap-1.5"
|
||
variants={agentStaggerContainer}
|
||
initial="hidden"
|
||
animate="visible"
|
||
>
|
||
{tags.map((tag, index) => (
|
||
<TagChip key={tag} label={tag} />
|
||
))}
|
||
<motion.button
|
||
type="button"
|
||
variants={agentFadeUp}
|
||
className="inline-flex size-7 items-center justify-center rounded-md border border-dashed text-muted-foreground hover:bg-muted/50"
|
||
aria-label={`افزودن ${title}`}
|
||
whileHover={{ scale: 1.06 }}
|
||
whileTap={{ scale: 0.94 }}
|
||
>
|
||
<Plus className="size-3.5" aria-hidden />
|
||
</motion.button>
|
||
</motion.div>
|
||
</motion.div>
|
||
);
|
||
}
|
||
|
||
export function AgentPreviewPanel({ className, onClose }: AgentPreviewPanelProps) {
|
||
return (
|
||
<motion.aside
|
||
layout
|
||
className={cn(
|
||
"flex w-full shrink-0 flex-col overflow-hidden border-s bg-card lg:w-72 xl:w-80",
|
||
className
|
||
)}
|
||
variants={agentSlideInEnd}
|
||
initial="hidden"
|
||
animate="visible"
|
||
exit="exit"
|
||
transition={agentLayoutTransition}
|
||
>
|
||
<div className="flex items-center justify-between border-b px-4 py-3">
|
||
<h2 className={type["card-title"]}>پیشنمایش سناریو</h2>
|
||
{onClose && (
|
||
<Button
|
||
type="button"
|
||
variant="ghost"
|
||
size="icon-sm"
|
||
onClick={onClose}
|
||
aria-label="بستن پیشنمایش"
|
||
>
|
||
<X className="size-4" aria-hidden />
|
||
</Button>
|
||
)}
|
||
</div>
|
||
|
||
<motion.div
|
||
className="flex-1 space-y-5 overflow-y-auto p-4"
|
||
variants={agentStaggerContainer}
|
||
initial="hidden"
|
||
animate="visible"
|
||
>
|
||
<motion.div className="space-y-2" variants={agentFadeUp}>
|
||
<p className={cn(type.caption, "font-medium uppercase tracking-wide text-muted-foreground")}>
|
||
سناریو
|
||
</p>
|
||
<h3 className={type.h4}>{AGENT_WORKSPACE_TITLE}</h3>
|
||
<p className={cn(type["body-sm"], "text-muted-foreground")}>
|
||
سناریوی آزمایشی برای طراحی workflow پشتیبانی و پاسخگویی خودکار به
|
||
مشتریان.
|
||
</p>
|
||
<div className="flex flex-wrap gap-1.5 pt-1">
|
||
{AGENT_PREVIEW_TAGS.map((tag, index) => (
|
||
<motion.span
|
||
key={tag}
|
||
className={cn(
|
||
"rounded-md bg-primary/10 px-2 py-0.5 text-primary",
|
||
type.caption
|
||
)}
|
||
initial={{ opacity: 0, scale: 0.9 }}
|
||
animate={{ opacity: 1, scale: 1 }}
|
||
transition={{ delay: 0.15 + index * 0.06 }}
|
||
>
|
||
{tag}
|
||
</motion.span>
|
||
))}
|
||
</div>
|
||
</motion.div>
|
||
|
||
<motion.div className="space-y-2" variants={agentFadeUp}>
|
||
<p className={cn(type.caption, "font-medium uppercase tracking-wide text-muted-foreground")}>
|
||
قالب
|
||
</p>
|
||
<div className="flex items-center justify-between rounded-xl border bg-muted/30 p-3">
|
||
<div className="flex items-center gap-2">
|
||
<FileText className="size-4 text-muted-foreground" aria-hidden />
|
||
<span className={type["body-sm"]}>پاسخگویی خودکار</span>
|
||
</div>
|
||
<Button type="button" variant="ghost" size="sm" className="h-7 px-2 text-xs">
|
||
تغییر
|
||
</Button>
|
||
</div>
|
||
</motion.div>
|
||
|
||
<div className="space-y-4">
|
||
<p className={cn(type.caption, "font-medium uppercase tracking-wide text-muted-foreground")}>
|
||
نمای کلی مخاطب
|
||
</p>
|
||
<AudienceSection title="نوع شرکت" tags={AGENT_AUDIENCE_TAGS.companyType} />
|
||
<AudienceSection title="مرحله" tags={AGENT_AUDIENCE_TAGS.stage} />
|
||
<AudienceSection title="اندازه شرکت" tags={AGENT_AUDIENCE_TAGS.companySize} />
|
||
<AudienceSection title="منطقه" tags={AGENT_AUDIENCE_TAGS.region} />
|
||
</div>
|
||
|
||
<motion.div className="space-y-2" variants={agentFadeUp}>
|
||
<p className={cn(type.caption, "font-medium uppercase tracking-wide text-muted-foreground")}>
|
||
ساختار سناریو
|
||
</p>
|
||
<div className="flex items-center gap-2 rounded-xl border bg-muted/30 px-3 py-2.5">
|
||
<Mail className="size-4 text-muted-foreground" aria-hidden />
|
||
<span className={type["body-sm"]}>پیام ۱</span>
|
||
</div>
|
||
</motion.div>
|
||
</motion.div>
|
||
|
||
<motion.div
|
||
className="flex gap-2 border-t p-4"
|
||
initial={{ opacity: 0, y: 12 }}
|
||
animate={{ opacity: 1, y: 0 }}
|
||
transition={{ delay: 0.2 }}
|
||
>
|
||
<Button type="button" variant="outline" className="flex-1">
|
||
زمانبندی
|
||
</Button>
|
||
<Button type="button" className="flex-1">
|
||
اجرای سناریو
|
||
</Button>
|
||
</motion.div>
|
||
</motion.aside>
|
||
);
|
||
}
|