| import { useState } from "react"; |
| import { useTranslation } from "react-i18next"; |
| import { formatDate } from "../lib/utils"; |
| import { |
| BookOpen, |
| Bot, |
| Check, |
| ChevronDown, |
| CircleDot, |
| Command as CommandIcon, |
| DollarSign, |
| Hexagon, |
| History, |
| Inbox, |
| LayoutDashboard, |
| ListTodo, |
| Mail, |
| Plus, |
| Search, |
| Settings, |
| Target, |
| Trash2, |
| Upload, |
| User, |
| Zap, |
| } from "lucide-react"; |
| import { Button } from "@/components/ui/button"; |
| import { Badge } from "@/components/ui/badge"; |
| import { Input } from "@/components/ui/input"; |
| import { Textarea } from "@/components/ui/textarea"; |
| import { Checkbox } from "@/components/ui/checkbox"; |
| import { Label } from "@/components/ui/label"; |
| import { Separator } from "@/components/ui/separator"; |
| import { Skeleton } from "@/components/ui/skeleton"; |
| import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs"; |
| import { |
| Card, |
| CardHeader, |
| CardTitle, |
| CardDescription, |
| CardContent, |
| CardFooter, |
| } from "@/components/ui/card"; |
| import { |
| Dialog, |
| DialogTrigger, |
| DialogContent, |
| DialogHeader, |
| DialogTitle, |
| DialogDescription, |
| DialogFooter, |
| } from "@/components/ui/dialog"; |
| import { |
| Tooltip, |
| TooltipTrigger, |
| TooltipContent, |
| } from "@/components/ui/tooltip"; |
| import { |
| Select, |
| SelectTrigger, |
| SelectValue, |
| SelectContent, |
| SelectItem, |
| } from "@/components/ui/select"; |
| import { |
| DropdownMenu, |
| DropdownMenuTrigger, |
| DropdownMenuContent, |
| DropdownMenuItem, |
| DropdownMenuSeparator, |
| DropdownMenuCheckboxItem, |
| DropdownMenuShortcut, |
| } from "@/components/ui/dropdown-menu"; |
| import { |
| Popover, |
| PopoverTrigger, |
| PopoverContent, |
| } from "@/components/ui/popover"; |
| import { |
| Sheet, |
| SheetTrigger, |
| SheetContent, |
| SheetHeader, |
| SheetTitle, |
| SheetDescription, |
| SheetFooter, |
| } from "@/components/ui/sheet"; |
| import { |
| Collapsible, |
| CollapsibleTrigger, |
| CollapsibleContent, |
| } from "@/components/ui/collapsible"; |
| import { ScrollArea } from "@/components/ui/scroll-area"; |
| import { |
| Command, |
| CommandInput, |
| CommandList, |
| CommandGroup, |
| CommandItem, |
| CommandEmpty, |
| CommandSeparator, |
| } from "@/components/ui/command"; |
| import { |
| Breadcrumb, |
| BreadcrumbItem, |
| BreadcrumbLink, |
| BreadcrumbList, |
| BreadcrumbPage, |
| BreadcrumbSeparator, |
| } from "@/components/ui/breadcrumb"; |
| import { |
| Avatar, |
| AvatarFallback, |
| AvatarGroup, |
| AvatarGroupCount, |
| } from "@/components/ui/avatar"; |
| import { StatusBadge } from "@/components/StatusBadge"; |
| import { StatusIcon } from "@/components/StatusIcon"; |
| import { PriorityIcon } from "@/components/PriorityIcon"; |
| import { agentStatusDot, agentStatusDotDefault } from "@/lib/status-colors"; |
| import { EntityRow } from "@/components/EntityRow"; |
| import { EmptyState } from "@/components/EmptyState"; |
| import { MetricCard } from "@/components/MetricCard"; |
| import { FilterBar, type FilterValue } from "@/components/FilterBar"; |
| import { InlineEditor } from "@/components/InlineEditor"; |
| import { PageSkeleton } from "@/components/PageSkeleton"; |
| import { Identity } from "@/components/Identity"; |
| import { IssueReferencePill } from "@/components/IssueReferencePill"; |
|
|
| |
| |
| |
|
|
| function Section({ title, children }: { title: string; children: React.ReactNode }) { |
| return ( |
| <section className="space-y-4"> |
| <h3 className="text-sm font-semibold text-muted-foreground uppercase tracking-wide"> |
| {title} |
| </h3> |
| <Separator /> |
| {children} |
| </section> |
| ); |
| } |
|
|
| function SubSection({ title, children }: { title: string; children: React.ReactNode }) { |
| return ( |
| <div className="space-y-3"> |
| <h4 className="text-sm font-medium">{title}</h4> |
| {children} |
| </div> |
| ); |
| } |
|
|
| |
| |
| |
|
|
| function Swatch({ name, cssVar }: { name: string; cssVar: string }) { |
| return ( |
| <div className="flex items-center gap-3"> |
| <div |
| className="h-8 w-8 rounded-md border border-border shrink-0" |
| style={{ backgroundColor: `var(${cssVar})` }} |
| /> |
| <div> |
| <p className="text-xs font-mono">{cssVar}</p> |
| <p className="text-xs text-muted-foreground">{name}</p> |
| </div> |
| </div> |
| ); |
| } |
|
|
| |
| |
| |
|
|
| export function DesignGuide() { |
| const { t } = useTranslation(); |
| const td = (value: string, options?: Record<string, string | number>) => |
| t(value, { defaultValue: value, ...(options ?? {}) }); |
| const [status, setStatus] = useState("todo"); |
| const [priority, setPriority] = useState("medium"); |
| const [selectValue, setSelectValue] = useState("in_progress"); |
| const [menuChecked, setMenuChecked] = useState(true); |
| const [collapsibleOpen, setCollapsibleOpen] = useState(false); |
| const [inlineText, setInlineText] = useState(() => td("Click to edit this text")); |
| const [inlineTitle, setInlineTitle] = useState(() => td("Editable Title")); |
| const [inlineDesc, setInlineDesc] = useState( |
| () => |
| td( |
| "This is an editable description. Click to edit it so the textarea can auto-size without causing layout shift.", |
| ) |
| ); |
| const [filters, setFilters] = useState<FilterValue[]>([ |
| { key: "status", label: td("Status"), value: td("Active") }, |
| { key: "priority", label: td("Priority"), value: td("High") }, |
| ]); |
|
|
| return ( |
| <div className="space-y-10 max-w-4xl"> |
| {/* Page header */} |
| <div> |
| <h2 className="text-xl font-bold">{td("Design Guide")}</h2> |
| <p className="text-sm text-muted-foreground mt-1"> |
| {td("A reference for the components, styles, and interaction patterns used across Paperclip CN.")} |
| </p> |
| </div> |
| |
| {/* ============================================================ */} |
| {/* COVERAGE */} |
| {/* ============================================================ */} |
| <Section title={td("Component Coverage")}> |
| <p className="text-sm text-muted-foreground"> |
| {td("Update this page whenever new UI primitives or app-level patterns ship.")} |
| </p> |
| <div className="grid gap-6 md:grid-cols-2"> |
| <SubSection title={td("UI primitives")}> |
| <div className="flex flex-wrap gap-2" data-e2e-ignore-i18n="true"> |
| {[ |
| "avatar", "badge", "breadcrumb", "button", "card", "checkbox", "collapsible", |
| "command", "dialog", "dropdown-menu", "input", "label", "popover", "scroll-area", |
| "select", "separator", "sheet", "skeleton", "tabs", "textarea", "tooltip", |
| ].map((name) => ( |
| <Badge key={name} variant="outline" className="font-mono text-[10px]"> |
| {name} |
| </Badge> |
| ))} |
| </div> |
| </SubSection> |
| <SubSection title={td("App components")}> |
| <div className="flex flex-wrap gap-2" data-e2e-ignore-i18n="true"> |
| {[ |
| "StatusBadge", "StatusIcon", "PriorityIcon", "EntityRow", "EmptyState", "MetricCard", |
| "FilterBar", "InlineEditor", "PageSkeleton", "Identity", "CommentThread", "MarkdownEditor", |
| "PropertiesPanel", "Sidebar", "CommandPalette", |
| ].map((name) => ( |
| <Badge key={name} variant="ghost" className="font-mono text-[10px]"> |
| {name} |
| </Badge> |
| ))} |
| </div> |
| </SubSection> |
| </div> |
| </Section> |
| |
| {/* ============================================================ */} |
| {/* COLORS */} |
| {/* ============================================================ */} |
| <Section title={td("Colors")}> |
| <SubSection title={td("Core")}> |
| <div className="grid grid-cols-2 md:grid-cols-3 gap-4"> |
| <Swatch name={td("Background")} cssVar="--background" /> |
| <Swatch name={td("Foreground")} cssVar="--foreground" /> |
| <Swatch name={td("Card")} cssVar="--card" /> |
| <Swatch name={td("Primary")} cssVar="--primary" /> |
| <Swatch name={td("Primary foreground")} cssVar="--primary-foreground" /> |
| <Swatch name={td("Secondary")} cssVar="--secondary" /> |
| <Swatch name={td("Muted")} cssVar="--muted" /> |
| <Swatch name={td("Muted foreground")} cssVar="--muted-foreground" /> |
| <Swatch name={td("Accent")} cssVar="--accent" /> |
| <Swatch name={td("Destructive")} cssVar="--destructive" /> |
| <Swatch name={td("Border")} cssVar="--border" /> |
| <Swatch name={td("Ring")} cssVar="--ring" /> |
| </div> |
| </SubSection> |
| |
| <SubSection title={td("Sidebar")}> |
| <div className="grid grid-cols-2 md:grid-cols-3 gap-4"> |
| <Swatch name={td("Sidebar")} cssVar="--sidebar" /> |
| <Swatch name={td("Sidebar border")} cssVar="--sidebar-border" /> |
| </div> |
| </SubSection> |
| |
| <SubSection title={td("Chart")}> |
| <div className="grid grid-cols-2 md:grid-cols-3 gap-4"> |
| <Swatch name={td("Chart 1")} cssVar="--chart-1" /> |
| <Swatch name={td("Chart 2")} cssVar="--chart-2" /> |
| <Swatch name={td("Chart 3")} cssVar="--chart-3" /> |
| <Swatch name={td("Chart 4")} cssVar="--chart-4" /> |
| <Swatch name={td("Chart 5")} cssVar="--chart-5" /> |
| </div> |
| </SubSection> |
| </Section> |
| |
| {/* ============================================================ */} |
| {/* TYPOGRAPHY */} |
| {/* ============================================================ */} |
| <Section title={td("Typography")}> |
| <div className="space-y-3"> |
| <h2 className="text-xl font-bold">{td("Page title example")}</h2> |
| <h2 className="text-lg font-semibold">{td("Section title example")}</h2> |
| <h3 className="text-sm font-semibold text-muted-foreground uppercase tracking-wide"> |
| {td("Section heading example")} |
| </h3> |
| <p className="text-sm font-medium">{td("Card title example")}</p> |
| <p className="text-sm font-semibold">{td("Alternative card title example")}</p> |
| <p className="text-sm">{td("Body text example")}</p> |
| <p className="text-sm text-muted-foreground"> |
| {td("Muted description example")} |
| </p> |
| <p className="text-xs text-muted-foreground"> |
| {td("Tiny label example")} |
| </p> |
| <p className="text-sm font-mono text-muted-foreground"> |
| {td("Mono identifier example")} |
| </p> |
| <p className="text-2xl font-bold">{td("Large stat example")}</p> |
| <p className="font-mono text-xs">{td("Log/code text example")}</p> |
| </div> |
| </Section> |
| |
| {/* ============================================================ */} |
| {/* SPACING & RADIUS */} |
| {/* ============================================================ */} |
| <Section title={td("Radius")}> |
| <div className="flex items-end gap-4 flex-wrap"> |
| {[ |
| ["sm", "var(--radius-sm)"], |
| ["md", "var(--radius-md)"], |
| ["lg", "var(--radius-lg)"], |
| ["xl", "var(--radius-xl)"], |
| ["full", "9999px"], |
| ].map(([label, radius]) => ( |
| <div key={label} className="flex flex-col items-center gap-1"> |
| <div |
| className="h-12 w-12 bg-primary" |
| style={{ borderRadius: radius }} |
| /> |
| <span className="text-xs text-muted-foreground">{label}</span> |
| </div> |
| ))} |
| </div> |
| </Section> |
| |
| {/* ============================================================ */} |
| {/* BUTTONS */} |
| {/* ============================================================ */} |
| <Section title={td("Buttons")}> |
| <SubSection title={td("Variants")}> |
| <div className="flex items-center gap-2 flex-wrap"> |
| <Button variant="default">{td("Default")}</Button> |
| <Button variant="secondary">{td("Secondary")}</Button> |
| <Button variant="outline">{td("Outline")}</Button> |
| <Button variant="ghost">{td("Ghost")}</Button> |
| <Button variant="destructive">{td("Destructive")}</Button> |
| <Button variant="link">{td("Link")}</Button> |
| </div> |
| </SubSection> |
| |
| <SubSection title={td("Sizes")}> |
| <div className="flex items-center gap-2 flex-wrap"> |
| <Button size="xs">{td("Extra Small")}</Button> |
| <Button size="sm">{td("Small")}</Button> |
| <Button size="default">{td("Default")}</Button> |
| <Button size="lg">{td("Large")}</Button> |
| </div> |
| </SubSection> |
| |
| <SubSection title={td("Icon buttons")}> |
| <div className="flex items-center gap-2 flex-wrap"> |
| <Button variant="ghost" size="icon-xs"><Search /></Button> |
| <Button variant="ghost" size="icon-sm"><Search /></Button> |
| <Button variant="outline" size="icon"><Search /></Button> |
| <Button variant="outline" size="icon-lg"><Search /></Button> |
| </div> |
| </SubSection> |
| |
| <SubSection title={td("With icons")}> |
| <div className="flex items-center gap-2 flex-wrap"> |
| <Button><Plus /> {td("New Issue")}</Button> |
| <Button variant="outline"><Upload /> {td("Upload")}</Button> |
| <Button variant="destructive"><Trash2 /> {td("Delete")}</Button> |
| <Button size="sm"><Plus /> {td("Add")}</Button> |
| </div> |
| </SubSection> |
| |
| <SubSection title={td("States")}> |
| <div className="flex items-center gap-2 flex-wrap"> |
| <Button disabled>{td("Disabled")}</Button> |
| <Button variant="outline" disabled>{td("Disabled Outline")}</Button> |
| </div> |
| </SubSection> |
| </Section> |
| |
| {/* ============================================================ */} |
| {/* BADGES */} |
| {/* ============================================================ */} |
| <Section title={td("Badges")}> |
| <SubSection title={td("Variants")}> |
| <div className="flex items-center gap-2 flex-wrap"> |
| <Badge variant="default">{td("Default")}</Badge> |
| <Badge variant="secondary">{td("Secondary")}</Badge> |
| <Badge variant="outline">{td("Outline")}</Badge> |
| <Badge variant="destructive">{td("Destructive")}</Badge> |
| <Badge variant="ghost">{td("Ghost")}</Badge> |
| </div> |
| </SubSection> |
| </Section> |
| |
| {/* ============================================================ */} |
| {/* STATUS BADGES & ICONS */} |
| {/* ============================================================ */} |
| <Section title={td("Status System")}> |
| <SubSection title={td("StatusBadge (all statuses)")}> |
| <div className="flex items-center gap-2 flex-wrap"> |
| {[ |
| "active", "running", "paused", "idle", "archived", "planned", |
| "achieved", "completed", "failed", "timed_out", "succeeded", "error", |
| "pending_approval", "backlog", "todo", "in_progress", "in_review", "blocked", |
| "done", "terminated", "cancelled", "pending", "revision_requested", |
| "approved", "rejected", |
| ].map((s) => ( |
| <StatusBadge key={s} status={s} /> |
| ))} |
| </div> |
| </SubSection> |
| |
| <SubSection title={td("StatusIcon (interactive)")}> |
| <div className="flex items-center gap-3 flex-wrap"> |
| {["backlog", "todo", "in_progress", "in_review", "done", "cancelled", "blocked"].map( |
| (s) => ( |
| <div key={s} className="flex items-center gap-1.5"> |
| <StatusIcon status={s} /> |
| <span className="text-xs text-muted-foreground">{s}</span> |
| </div> |
| ) |
| )} |
| </div> |
| <div className="flex items-center gap-2 mt-2"> |
| <StatusIcon status={status} onChange={setStatus} /> |
| <span className="text-sm"> |
| {td("Click the icon to change status (current: {{status}})", { status })} |
| </span> |
| </div> |
| </SubSection> |
| |
| <SubSection title={td("PriorityIcon (interactive)")}> |
| <div className="flex items-center gap-3 flex-wrap"> |
| {["critical", "high", "medium", "low"].map((p) => ( |
| <div key={p} className="flex items-center gap-1.5"> |
| <PriorityIcon priority={p} /> |
| <span className="text-xs text-muted-foreground">{p}</span> |
| </div> |
| ))} |
| </div> |
| <div className="flex items-center gap-2 mt-2"> |
| <PriorityIcon priority={priority} onChange={setPriority} /> |
| <span className="text-sm"> |
| {td("Click the icon to change (current: {{priority}})", { priority })} |
| </span> |
| </div> |
| </SubSection> |
| |
| <SubSection title={td("Agent status dots")}> |
| <div className="flex items-center gap-4 flex-wrap" data-e2e-ignore-i18n="true"> |
| {(["running", "active", "paused", "error", "archived"] as const).map((label) => ( |
| <div key={label} className="flex items-center gap-2"> |
| <span className="relative flex h-2.5 w-2.5"> |
| <span className={`inline-flex h-full w-full rounded-full ${agentStatusDot[label] ?? agentStatusDotDefault}`} /> |
| </span> |
| <span className="text-xs text-muted-foreground">{label}</span> |
| </div> |
| ))} |
| </div> |
| </SubSection> |
| |
| <SubSection title={td("Run invocation badges")}> |
| <div className="flex items-center gap-2 flex-wrap" data-e2e-ignore-i18n="true"> |
| {[ |
| ["timer", "bg-blue-100 text-blue-700 dark:bg-blue-900/50 dark:text-blue-300"], |
| ["assignment", "bg-violet-100 text-violet-700 dark:bg-violet-900/50 dark:text-violet-300"], |
| ["on_demand", "bg-cyan-100 text-cyan-700 dark:bg-cyan-900/50 dark:text-cyan-300"], |
| ["automation", "bg-muted text-muted-foreground"], |
| ].map(([label, cls]) => ( |
| <span key={label} className={`rounded-full px-1.5 py-0.5 text-[10px] font-medium ${cls}`}> |
| {label} |
| </span> |
| ))} |
| </div> |
| </SubSection> |
| |
| <SubSection title="IssueReferencePill"> |
| <p className="text-xs text-muted-foreground"> |
| Used wherever a task is referenced — in markdown, the Related Work tab, and activity summaries. |
| Pass <code className="font-mono">status</code> to show the target issue's state at a glance. |
| Use <code className="font-mono">strikethrough</code> for "removed" contexts. |
| </p> |
| <div className="flex items-center gap-2 flex-wrap"> |
| <IssueReferencePill issue={{ id: "demo-1", identifier: "PAP-123", title: "Identifier only — no status yet" }} /> |
| <IssueReferencePill issue={{ id: "demo-2", identifier: "PAP-456", title: "With in_progress status", status: "in_progress" }} /> |
| <IssueReferencePill issue={{ id: "demo-3", identifier: "PAP-789", title: "Done status", status: "done" }} /> |
| <IssueReferencePill issue={{ id: "demo-4", identifier: "PAP-101", title: "Blocked status", status: "blocked" }} /> |
| <IssueReferencePill strikethrough issue={{ id: "demo-5", identifier: "PAP-202", title: "Removed (strikethrough)", status: "todo" }} /> |
| </div> |
| </SubSection> |
| </Section> |
| |
| {/* ============================================================ */} |
| {/* FORM ELEMENTS */} |
| {/* ============================================================ */} |
| <Section title={td("Form Elements")}> |
| <div className="grid gap-6 md:grid-cols-2"> |
| <SubSection title={td("Input")}> |
| <Input placeholder={td("Default input")} /> |
| <Input placeholder={td("Disabled input")} disabled className="mt-2" /> |
| </SubSection> |
| |
| <SubSection title={td("Textarea")}> |
| <Textarea placeholder={td("Write something...")} /> |
| </SubSection> |
| |
| <SubSection title={td("Checkbox & Label")}> |
| <div className="space-y-3"> |
| <div className="flex items-center gap-2"> |
| <Checkbox id="check1" defaultChecked /> |
| <Label htmlFor="check1">{td("Checked item")}</Label> |
| </div> |
| <div className="flex items-center gap-2"> |
| <Checkbox id="check2" /> |
| <Label htmlFor="check2">{td("Unchecked item")}</Label> |
| </div> |
| <div className="flex items-center gap-2"> |
| <Checkbox id="check3" disabled /> |
| <Label htmlFor="check3">{td("Disabled item")}</Label> |
| </div> |
| </div> |
| </SubSection> |
| |
| <SubSection title={td("Inline Editor")}> |
| <div className="space-y-4"> |
| <div> |
| <p className="text-xs text-muted-foreground mb-1">{td("Title (single-line)")}</p> |
| <InlineEditor |
| value={inlineTitle} |
| onSave={setInlineTitle} |
| as="h2" |
| className="text-xl font-bold" |
| /> |
| </div> |
| <div> |
| <p className="text-xs text-muted-foreground mb-1">{td("Body text (single-line)")}</p> |
| <InlineEditor |
| value={inlineText} |
| onSave={setInlineText} |
| as="p" |
| className="text-sm" |
| /> |
| </div> |
| <div> |
| <p className="text-xs text-muted-foreground mb-1">{td("Description (multiline, auto-sizing)")}</p> |
| <InlineEditor |
| value={inlineDesc} |
| onSave={setInlineDesc} |
| as="p" |
| className="text-sm text-muted-foreground" |
| placeholder={td("Add a description...")} |
| multiline |
| /> |
| </div> |
| </div> |
| </SubSection> |
| </div> |
| </Section> |
| |
| {/* ============================================================ */} |
| {/* SELECT */} |
| {/* ============================================================ */} |
| <Section title={td("Select")}> |
| <div className="grid gap-6 md:grid-cols-2"> |
| <SubSection title={td("Default size")}> |
| <Select value={selectValue} onValueChange={setSelectValue}> |
| <SelectTrigger className="w-full"> |
| <SelectValue placeholder={td("Select status")} /> |
| </SelectTrigger> |
| <SelectContent> |
| <SelectItem value="backlog">{td("Backlog")}</SelectItem> |
| <SelectItem value="todo">{td("Todo")}</SelectItem> |
| <SelectItem value="in_progress">{td("In Progress")}</SelectItem> |
| <SelectItem value="in_review">{td("In Review")}</SelectItem> |
| <SelectItem value="done">{td("Done")}</SelectItem> |
| </SelectContent> |
| </Select> |
| <p className="text-xs text-muted-foreground">{td("Current value: {{value}}", { value: selectValue })}</p> |
| </SubSection> |
| <SubSection title={td("Small trigger")}> |
| <Select defaultValue="high"> |
| <SelectTrigger size="sm" className="w-full"> |
| <SelectValue /> |
| </SelectTrigger> |
| <SelectContent> |
| <SelectItem value="critical">{td("Critical")}</SelectItem> |
| <SelectItem value="high">{td("High")}</SelectItem> |
| <SelectItem value="medium">{td("Medium")}</SelectItem> |
| <SelectItem value="low">{td("Low")}</SelectItem> |
| </SelectContent> |
| </Select> |
| </SubSection> |
| </div> |
| </Section> |
| |
| {/* ============================================================ */} |
| {/* DROPDOWN MENU */} |
| {/* ============================================================ */} |
| <Section title={td("Dropdown Menu")}> |
| <DropdownMenu> |
| <DropdownMenuTrigger asChild> |
| <Button variant="outline" size="sm"> |
| {td("Quick Actions")} |
| <ChevronDown className="h-4 w-4" /> |
| </Button> |
| </DropdownMenuTrigger> |
| <DropdownMenuContent align="start" className="w-56"> |
| <DropdownMenuItem> |
| <Check className="h-4 w-4" /> |
| {td("Mark as done")} |
| <DropdownMenuShortcut>⌘D</DropdownMenuShortcut> |
| </DropdownMenuItem> |
| <DropdownMenuItem> |
| <BookOpen className="h-4 w-4" /> |
| {td("Open docs")} |
| </DropdownMenuItem> |
| <DropdownMenuSeparator /> |
| <DropdownMenuCheckboxItem |
| checked={menuChecked} |
| onCheckedChange={(value) => setMenuChecked(value === true)} |
| > |
| {td("Watch issue")} |
| </DropdownMenuCheckboxItem> |
| <DropdownMenuItem variant="destructive"> |
| <Trash2 className="h-4 w-4" /> |
| {td("Delete issue")} |
| </DropdownMenuItem> |
| </DropdownMenuContent> |
| </DropdownMenu> |
| </Section> |
| |
| {/* ============================================================ */} |
| {/* POPOVER */} |
| {/* ============================================================ */} |
| <Section title={td("Popover")}> |
| <Popover> |
| <PopoverTrigger asChild> |
| <Button variant="outline" size="sm">{td("Open Popover")}</Button> |
| </PopoverTrigger> |
| <PopoverContent className="space-y-2"> |
| <p className="text-sm font-medium">{td("Agent heartbeat")}</p> |
| <p className="text-xs text-muted-foreground"> |
| {td("Last run succeeded 24s ago. Next timer run in 9m.")} |
| </p> |
| <Button size="xs">{td("Wake now")}</Button> |
| </PopoverContent> |
| </Popover> |
| </Section> |
| |
| {/* ============================================================ */} |
| {/* COLLAPSIBLE */} |
| {/* ============================================================ */} |
| <Section title={td("Collapsible")}> |
| <Collapsible open={collapsibleOpen} onOpenChange={setCollapsibleOpen} className="space-y-2"> |
| <CollapsibleTrigger asChild> |
| <Button variant="outline" size="sm"> |
| {collapsibleOpen ? td("Hide") : td("Show")} {td("advanced filters")} |
| </Button> |
| </CollapsibleTrigger> |
| <CollapsibleContent className="rounded-md border border-border p-3"> |
| <div className="space-y-2"> |
| <Label htmlFor="owner-filter">{td("Owner")}</Label> |
| <Input id="owner-filter" placeholder={td("Filter by agent name")} /> |
| </div> |
| </CollapsibleContent> |
| </Collapsible> |
| </Section> |
| |
| {/* ============================================================ */} |
| {/* SHEET */} |
| {/* ============================================================ */} |
| <Section title={td("Sheet")}> |
| <Sheet> |
| <SheetTrigger asChild> |
| <Button variant="outline" size="sm">{td("Open Side Panel")}</Button> |
| </SheetTrigger> |
| <SheetContent side="right"> |
| <SheetHeader> |
| <SheetTitle>{td("Issue Properties")}</SheetTitle> |
| <SheetDescription>{td("Edit metadata without leaving the current page.")}</SheetDescription> |
| </SheetHeader> |
| <div className="space-y-4 px-4"> |
| <div className="space-y-1"> |
| <Label htmlFor="sheet-title">{td("Title")}</Label> |
| <Input id="sheet-title" defaultValue={td("Improve onboarding docs")} /> |
| </div> |
| <div className="space-y-1"> |
| <Label htmlFor="sheet-description">{td("Description")}</Label> |
| <Textarea id="sheet-description" defaultValue={td("Capture setup pitfalls and screenshots.")} /> |
| </div> |
| </div> |
| <SheetFooter> |
| <Button variant="outline">{td("Cancel")}</Button> |
| <Button>{td("Save")}</Button> |
| </SheetFooter> |
| </SheetContent> |
| </Sheet> |
| </Section> |
| |
| {/* ============================================================ */} |
| {/* SCROLL AREA */} |
| {/* ============================================================ */} |
| <Section title={td("Scroll Area")}> |
| <ScrollArea className="h-36 rounded-md border border-border"> |
| <div className="space-y-2 p-3"> |
| {Array.from({ length: 12 }).map((_, i) => ( |
| <div key={i} className="rounded-md border border-border p-2 text-sm"> |
| {td("Heartbeat run #{{index}}: completed successfully", { index: i + 1 })} |
| </div> |
| ))} |
| </div> |
| </ScrollArea> |
| </Section> |
| |
| {/* ============================================================ */} |
| {/* COMMAND */} |
| {/* ============================================================ */} |
| <Section title={td("Command (CMDK)")}> |
| <div className="rounded-md border border-border"> |
| <Command> |
| <CommandInput placeholder={td("Type a command or search...")} /> |
| <CommandList> |
| <CommandEmpty>{td("No results found.")}</CommandEmpty> |
| <CommandGroup heading={td("Pages")}> |
| <CommandItem> |
| <LayoutDashboard className="h-4 w-4" /> |
| {td("Dashboard")} |
| </CommandItem> |
| <CommandItem> |
| <CircleDot className="h-4 w-4" /> |
| {td("Issues")} |
| </CommandItem> |
| </CommandGroup> |
| <CommandSeparator /> |
| <CommandGroup heading={td("Actions")}> |
| <CommandItem> |
| <CommandIcon className="h-4 w-4" /> |
| {td("Open command palette")} |
| </CommandItem> |
| <CommandItem> |
| <Plus className="h-4 w-4" /> |
| {td("Create new issue")} |
| </CommandItem> |
| </CommandGroup> |
| </CommandList> |
| </Command> |
| </div> |
| </Section> |
| |
| {/* ============================================================ */} |
| {/* BREADCRUMB */} |
| {/* ============================================================ */} |
| <Section title={td("Breadcrumb")}> |
| <Breadcrumb> |
| <BreadcrumbList> |
| <BreadcrumbItem> |
| <BreadcrumbLink href="#">{td("Projects")}</BreadcrumbLink> |
| </BreadcrumbItem> |
| <BreadcrumbSeparator /> |
| <BreadcrumbItem> |
| <BreadcrumbLink href="#">{td("Paperclip CN App")}</BreadcrumbLink> |
| </BreadcrumbItem> |
| <BreadcrumbSeparator /> |
| <BreadcrumbItem> |
| <BreadcrumbPage>{td("Issue List")}</BreadcrumbPage> |
| </BreadcrumbItem> |
| </BreadcrumbList> |
| </Breadcrumb> |
| </Section> |
| |
| {/* ============================================================ */} |
| {/* CARDS */} |
| {/* ============================================================ */} |
| <Section title={td("Cards")}> |
| <SubSection title={td("Standard Card")}> |
| <Card> |
| <CardHeader> |
| <CardTitle>{td("Card Title")}</CardTitle> |
| <CardDescription>{td("Card description with supporting text.")}</CardDescription> |
| </CardHeader> |
| <CardContent> |
| <p className="text-sm">{td("Card content goes here. This is the main body area.")}</p> |
| </CardContent> |
| <CardFooter className="gap-2"> |
| <Button size="sm">{td("Action")}</Button> |
| <Button variant="outline" size="sm">{td("Cancel")}</Button> |
| </CardFooter> |
| </Card> |
| </SubSection> |
| |
| <SubSection title={td("Metric Cards")}> |
| <div className="grid md:grid-cols-2 xl:grid-cols-4 gap-4"> |
| <MetricCard icon={Bot} value={12} label={td("Active Agents")} description={td("+3 this week")} /> |
| <MetricCard icon={CircleDot} value={48} label={td("Open Issues")} /> |
| <MetricCard icon={DollarSign} value="$1,234" label={td("Monthly Cost")} description={td("Under budget")} /> |
| <MetricCard icon={Zap} value="99.9%" label={td("Uptime")} /> |
| </div> |
| </SubSection> |
| </Section> |
| |
| {/* ============================================================ */} |
| {/* TABS */} |
| {/* ============================================================ */} |
| <Section title={td("Tabs")}> |
| <SubSection title={td("Default (pill) variant")}> |
| <Tabs defaultValue="overview"> |
| <TabsList> |
| <TabsTrigger value="overview">{td("Overview")}</TabsTrigger> |
| <TabsTrigger value="runs">{td("Runs")}</TabsTrigger> |
| <TabsTrigger value="config">{td("Config")}</TabsTrigger> |
| <TabsTrigger value="costs">{td("Costs")}</TabsTrigger> |
| </TabsList> |
| <TabsContent value="overview"> |
| <p className="text-sm text-muted-foreground py-4">{td("Overview tab content.")}</p> |
| </TabsContent> |
| <TabsContent value="runs"> |
| <p className="text-sm text-muted-foreground py-4">{td("Runs tab content.")}</p> |
| </TabsContent> |
| <TabsContent value="config"> |
| <p className="text-sm text-muted-foreground py-4">{td("Config tab content.")}</p> |
| </TabsContent> |
| <TabsContent value="costs"> |
| <p className="text-sm text-muted-foreground py-4">{td("Costs tab content.")}</p> |
| </TabsContent> |
| </Tabs> |
| </SubSection> |
| |
| <SubSection title={td("Line variant")}> |
| <Tabs defaultValue="summary"> |
| <TabsList variant="line"> |
| <TabsTrigger value="summary">{td("Summary")}</TabsTrigger> |
| <TabsTrigger value="details">{td("Details")}</TabsTrigger> |
| <TabsTrigger value="comments">{td("Comments")}</TabsTrigger> |
| </TabsList> |
| <TabsContent value="summary"> |
| <p className="text-sm text-muted-foreground py-4">{td("Summary content with underline tabs.")}</p> |
| </TabsContent> |
| <TabsContent value="details"> |
| <p className="text-sm text-muted-foreground py-4">{td("Details content.")}</p> |
| </TabsContent> |
| <TabsContent value="comments"> |
| <p className="text-sm text-muted-foreground py-4">{td("Comments content.")}</p> |
| </TabsContent> |
| </Tabs> |
| </SubSection> |
| </Section> |
| |
| {/* ============================================================ */} |
| {/* ENTITY ROWS */} |
| {/* ============================================================ */} |
| <Section title={td("Entity Rows")}> |
| <div className="border border-border rounded-md"> |
| <EntityRow |
| leading={ |
| <> |
| <StatusIcon status="in_progress" /> |
| <PriorityIcon priority="high" /> |
| </> |
| } |
| identifier="PAP-001" |
| title={td("Implement authentication flow")} |
| subtitle={td("Assigned to Agent Alpha")} |
| trailing={<StatusBadge status="in_progress" />} |
| onClick={() => {}} |
| /> |
| <EntityRow |
| leading={ |
| <> |
| <StatusIcon status="done" /> |
| <PriorityIcon priority="medium" /> |
| </> |
| } |
| identifier="PAP-002" |
| title={td("Set up CI/CD pipeline")} |
| subtitle={td("Completed 2 days ago")} |
| trailing={<StatusBadge status="done" />} |
| onClick={() => {}} |
| /> |
| <EntityRow |
| leading={ |
| <> |
| <StatusIcon status="todo" /> |
| <PriorityIcon priority="low" /> |
| </> |
| } |
| identifier="PAP-003" |
| title={td("Write API documentation")} |
| trailing={<StatusBadge status="todo" />} |
| onClick={() => {}} |
| /> |
| <EntityRow |
| leading={ |
| <> |
| <StatusIcon status="blocked" /> |
| <PriorityIcon priority="critical" /> |
| </> |
| } |
| identifier="PAP-004" |
| title={td("Deploy to production")} |
| subtitle={td("Blocked by PAP-001")} |
| trailing={<StatusBadge status="blocked" />} |
| selected |
| /> |
| </div> |
| </Section> |
|
|
| {} |
| {} |
| {} |
| <Section title={td("Filter Bar")}> |
| <FilterBar |
| filters={filters} |
| onRemove={(key) => setFilters((f) => f.filter((x) => x.key !== key))} |
| onClear={() => setFilters([])} |
| /> |
| {filters.length === 0 && ( |
| <Button |
| variant="outline" |
| size="sm" |
| onClick={() => |
| setFilters([ |
| { key: "status", label: "Status", value: "Active" }, |
| { key: "priority", label: td("Priority"), value: td("High") }, |
| ]) |
| } |
| > |
| {td("Reset filters")} |
| </Button> |
| )} |
| </Section> |
|
|
| {} |
| {} |
| {} |
| <Section title={td("Avatars")}> |
| <SubSection title={td("Sizes")}> |
| <div className="flex items-center gap-3"> |
| <Avatar size="sm"><AvatarFallback>SM</AvatarFallback></Avatar> |
| <Avatar><AvatarFallback>DF</AvatarFallback></Avatar> |
| <Avatar size="lg"><AvatarFallback>LG</AvatarFallback></Avatar> |
| </div> |
| </SubSection> |
|
|
| <SubSection title={td("Group")}> |
| <AvatarGroup> |
| <Avatar><AvatarFallback>A1</AvatarFallback></Avatar> |
| <Avatar><AvatarFallback>A2</AvatarFallback></Avatar> |
| <Avatar><AvatarFallback>A3</AvatarFallback></Avatar> |
| <AvatarGroupCount>+5</AvatarGroupCount> |
| </AvatarGroup> |
| </SubSection> |
| </Section> |
|
|
| {} |
| {} |
| {} |
| <Section title={td("Identity")}> |
| <SubSection title={td("Sizes")}> |
| <div className="flex items-center gap-6" data-e2e-ignore-i18n="true"> |
| <Identity name="Agent Alpha" size="sm" /> |
| <Identity name="Agent Alpha" /> |
| <Identity name="Agent Alpha" size="lg" /> |
| </div> |
| </SubSection> |
|
|
| <SubSection title={td("Initials derivation")}> |
| <div className="flex flex-col gap-2" data-e2e-ignore-i18n="true"> |
| <Identity name="CEO Agent" size="sm" /> |
| <Identity name="Alpha" size="sm" /> |
| <Identity name="Quality Assurance Lead" size="sm" /> |
| </div> |
| </SubSection> |
|
|
| <SubSection title={td("Custom initials")}> |
| <div data-e2e-ignore-i18n="true"> |
| <Identity name="Backend Service" initials="BS" size="sm" /> |
| </div> |
| </SubSection> |
| </Section> |
|
|
| {} |
| {} |
| {} |
| <Section title={td("Tooltips")}> |
| <div className="flex items-center gap-4"> |
| <Tooltip> |
| <TooltipTrigger asChild> |
| <Button variant="outline" size="sm">{td("Hover me")}</Button> |
| </TooltipTrigger> |
| <TooltipContent>{td("This is a tooltip")}</TooltipContent> |
| </Tooltip> |
| <Tooltip> |
| <TooltipTrigger asChild> |
| <Button variant="ghost" size="icon-sm"><Settings /></Button> |
| </TooltipTrigger> |
| <TooltipContent>{td("Settings")}</TooltipContent> |
| </Tooltip> |
| </div> |
| </Section> |
|
|
| {} |
| {} |
| {} |
| <Section title={td("Dialog")}> |
| <Dialog> |
| <DialogTrigger asChild> |
| <Button variant="outline">{td("Open Dialog")}</Button> |
| </DialogTrigger> |
| <DialogContent> |
| <DialogHeader> |
| <DialogTitle>{td("Dialog Title")}</DialogTitle> |
| <DialogDescription> |
| {td("This is a sample dialog showing the standard layout with header, content, and footer.")} |
| </DialogDescription> |
| </DialogHeader> |
| <div className="space-y-3"> |
| <div> |
| <Label>{td("Name")}</Label> |
| <Input placeholder={td("Enter a name")} className="mt-1.5" /> |
| </div> |
| <div> |
| <Label>{td("Description")}</Label> |
| <Textarea placeholder={td("Describe...")} className="mt-1.5" /> |
| </div> |
| </div> |
| <DialogFooter> |
| <Button variant="outline">{td("Cancel")}</Button> |
| <Button>{td("Save")}</Button> |
| </DialogFooter> |
| </DialogContent> |
| </Dialog> |
| </Section> |
|
|
| {} |
| {} |
| {} |
| <Section title={td("Empty State")}> |
| <div className="border border-border rounded-md"> |
| <EmptyState |
| icon={Inbox} |
| message={td("No items to show. Create your first one to get started.")} |
| action={td("Create Item")} |
| onAction={() => {}} |
| /> |
| </div> |
| </Section> |
|
|
| {} |
| {} |
| {} |
| <Section title={td("Progress Bars (Budget)")}> |
| <div className="space-y-3"> |
| {[ |
| { label: td("Under budget (40%)"), pct: 40, color: "bg-green-400" }, |
| { label: td("Warning (75%)"), pct: 75, color: "bg-yellow-400" }, |
| { label: td("Over budget (95%)"), pct: 95, color: "bg-red-400" }, |
| ].map(({ label, pct, color }) => ( |
| <div key={label} className="space-y-1"> |
| <div className="flex items-center justify-between"> |
| <span className="text-xs text-muted-foreground">{label}</span> |
| <span className="text-xs font-mono">{pct}%</span> |
| </div> |
| <div className="w-full h-2 bg-muted rounded-full overflow-hidden"> |
| <div |
| className={`h-full rounded-full transition-[width,background-color] duration-150 ${color}`} |
| style={{ width: `${pct}%` }} |
| /> |
| </div> |
| </div> |
| ))} |
| </div> |
| </Section> |
|
|
| {} |
| {} |
| {} |
| <Section title={td("Log Viewer")}> |
| <div className="bg-neutral-950 rounded-lg p-3 font-mono text-xs max-h-80 overflow-y-auto" data-e2e-ignore-i18n="true"> |
| <div className="text-foreground">[12:00:01] INFO Agent started successfully</div> |
| <div className="text-foreground">[12:00:02] INFO Processing task PAP-001</div> |
| <div className="text-yellow-400">[12:00:05] WARN Rate limit approaching (80%)</div> |
| <div className="text-foreground">[12:00:08] INFO Task PAP-001 completed</div> |
| <div className="text-red-400">[12:00:12] ERROR Connection timeout to upstream service</div> |
| <div className="text-blue-300">[12:00:12] SYS Retrying connection in 5s...</div> |
| <div className="text-foreground">[12:00:17] INFO Reconnected successfully</div> |
| <div className="flex items-center gap-1.5"> |
| <span className="relative flex h-1.5 w-1.5"> |
| <span className="absolute inline-flex h-full w-full rounded-full bg-cyan-400 animate-pulse" /> |
| <span className="inline-flex h-full w-full rounded-full bg-cyan-400" /> |
| </span> |
| <span className="text-cyan-400">Live</span> |
| </div> |
| </div> |
| </Section> |
|
|
| {} |
| {} |
| {} |
| <Section title={td("Property Row Pattern")}> |
| <div className="border border-border rounded-md p-4 space-y-1 max-w-sm"> |
| <div className="flex items-center justify-between py-1.5"> |
| <span className="text-xs text-muted-foreground">{td("Status")}</span> |
| <StatusBadge status="active" /> |
| </div> |
| <div className="flex items-center justify-between py-1.5"> |
| <span className="text-xs text-muted-foreground">{td("Priority")}</span> |
| <PriorityIcon priority="high" /> |
| </div> |
| <div className="flex items-center justify-between py-1.5"> |
| <span className="text-xs text-muted-foreground">{td("Assignee")}</span> |
| <div className="flex items-center gap-1.5"> |
| <Avatar size="sm"><AvatarFallback>A</AvatarFallback></Avatar> |
| <span className="text-xs">Agent Alpha</span> |
| </div> |
| </div> |
| <div className="flex items-center justify-between py-1.5"> |
| <span className="text-xs text-muted-foreground">{td("Created")}</span> |
| <span className="text-xs">{formatDate("2025-01-15")}</span> |
| </div> |
| </div> |
| </Section> |
|
|
| {} |
| {} |
| {} |
| <Section title={td("Navigation Patterns")}> |
| <SubSection title={td("Sidebar nav items")}> |
| <div className="w-60 border border-border rounded-md p-3 space-y-0.5 bg-card"> |
| <div className="flex items-center gap-2 px-3 py-1.5 rounded-md text-sm font-medium bg-accent text-accent-foreground"> |
| <LayoutDashboard className="h-4 w-4" /> |
| {td("Dashboard")} |
| </div> |
| <div className="flex items-center gap-2 px-3 py-1.5 rounded-md text-sm font-medium text-muted-foreground hover:bg-accent/50 hover:text-accent-foreground cursor-pointer"> |
| <CircleDot className="h-4 w-4" /> |
| {td("Issues")} |
| <span className="ml-auto text-xs bg-primary text-primary-foreground rounded-full px-1.5 py-0.5"> |
| 12 |
| </span> |
| </div> |
| <div className="flex items-center gap-2 px-3 py-1.5 rounded-md text-sm font-medium text-muted-foreground hover:bg-accent/50 hover:text-accent-foreground cursor-pointer"> |
| <Bot className="h-4 w-4" /> |
| {td("Agents")} |
| </div> |
| <div className="flex items-center gap-2 px-3 py-1.5 rounded-md text-sm font-medium text-muted-foreground hover:bg-accent/50 hover:text-accent-foreground cursor-pointer"> |
| <Hexagon className="h-4 w-4" /> |
| {td("Projects")} |
| </div> |
| </div> |
| </SubSection> |
|
|
| <SubSection title={td("View toggle")}> |
| <div className="flex items-center border border-border rounded-md w-fit"> |
| <button className="px-3 py-1.5 text-xs font-medium bg-accent text-foreground rounded-l-md"> |
| <ListTodo className="h-3.5 w-3.5 inline mr-1" /> |
| {td("List")} |
| </button> |
| <button className="px-3 py-1.5 text-xs font-medium text-muted-foreground hover:bg-accent/50 rounded-r-md"> |
| <Target className="h-3.5 w-3.5 inline mr-1" /> |
| {td("Org")} |
| </button> |
| </div> |
| </SubSection> |
| </Section> |
|
|
| {} |
| {} |
| {} |
| <Section title={td("Grouped List (Issues pattern)")}> |
| <div> |
| <div className="flex items-center gap-2 px-4 py-2 bg-muted/50 rounded-t-md"> |
| <StatusIcon status="in_progress" /> |
| <span className="text-sm font-medium">{td("In Progress")}</span> |
| <span className="text-xs text-muted-foreground ml-1">2</span> |
| </div> |
| <div className="border border-border rounded-b-md"> |
| <EntityRow |
| leading={<PriorityIcon priority="high" />} |
| identifier="PAP-101" |
| title={td("Build agent heartbeat system")} |
| onClick={() => {}} |
| /> |
| <EntityRow |
| leading={<PriorityIcon priority="medium" />} |
| identifier="PAP-102" |
| title={td("Add cost tracking dashboard")} |
| onClick={() => {}} |
| /> |
| </div> |
| </div> |
| </Section> |
|
|
| {} |
| {} |
| {} |
| <Section title={td("Comment Thread Pattern")}> |
| <div className="space-y-3 max-w-2xl"> |
| <h3 className="text-sm font-semibold">{td("Comments (2)")}</h3> |
| <div className="space-y-3"> |
| <div className="rounded-md border border-border p-3"> |
| <div className="flex items-center justify-between mb-1"> |
| <span className="text-xs font-medium text-muted-foreground">{td("Agent")}</span> |
| <span className="text-xs text-muted-foreground">{formatDate("2025-01-15")}</span> |
| </div> |
| <p className="text-sm">{td("Started working on the authentication module. Will need API keys configured.")}</p> |
| </div> |
| <div className="rounded-md border border-border p-3"> |
| <div className="flex items-center justify-between mb-1"> |
| <span className="text-xs font-medium text-muted-foreground">{td("Human")}</span> |
| <span className="text-xs text-muted-foreground">{formatDate("2025-01-16")}</span> |
| </div> |
| <p className="text-sm">{td("API keys have been added to the vault. Please proceed.")}</p> |
| </div> |
| </div> |
| <div className="space-y-2"> |
| <Textarea placeholder={td("Leave a comment...")} rows={3} /> |
| <Button size="sm">{td("Comment")}</Button> |
| </div> |
| </div> |
| </Section> |
|
|
| {} |
| {} |
| {} |
| <Section title={td("Cost Table Pattern")}> |
| <div className="border border-border rounded-lg overflow-hidden"> |
| <table className="w-full text-xs"> |
| <thead className="border-b border-border bg-accent/20"> |
| <tr> |
| <th className="text-left px-3 py-2 font-medium text-muted-foreground">{td("Model")}</th> |
| <th className="text-left px-3 py-2 font-medium text-muted-foreground">{td("Tokens")}</th> |
| <th className="text-left px-3 py-2 font-medium text-muted-foreground">{td("Cost")}</th> |
| </tr> |
| </thead> |
| <tbody> |
| <tr className="border-b border-border"> |
| <td className="px-3 py-2">claude-sonnet-4-20250514</td> |
| <td className="px-3 py-2 font-mono">1.2M</td> |
| <td className="px-3 py-2 font-mono">$18.00</td> |
| </tr> |
| <tr className="border-b border-border"> |
| <td className="px-3 py-2">claude-haiku-4-20250506</td> |
| <td className="px-3 py-2 font-mono">500k</td> |
| <td className="px-3 py-2 font-mono">$1.25</td> |
| </tr> |
| <tr> |
| <td className="px-3 py-2 font-medium">{td("Total")}</td> |
| <td className="px-3 py-2 font-mono">1.7M</td> |
| <td className="px-3 py-2 font-mono font-medium">$19.25</td> |
| </tr> |
| </tbody> |
| </table> |
| </div> |
| </Section> |
|
|
| {} |
| {} |
| {} |
| <Section title={td("Skeletons")}> |
| <SubSection title={td("Individual")}> |
| <div className="space-y-2"> |
| <Skeleton className="h-4 w-48" /> |
| <Skeleton className="h-8 w-full max-w-sm" /> |
| <Skeleton className="h-20 w-full" /> |
| </div> |
| </SubSection> |
|
|
| <SubSection title={td("Page Skeleton (list)")}> |
| <div className="border border-border rounded-md p-4"> |
| <PageSkeleton variant="list" /> |
| </div> |
| </SubSection> |
|
|
| <SubSection title={td("Page Skeleton (detail)")}> |
| <div className="border border-border rounded-md p-4"> |
| <PageSkeleton variant="detail" /> |
| </div> |
| </SubSection> |
| </Section> |
|
|
| {} |
| {} |
| {} |
| <Section title={td("Separator")}> |
| <div className="space-y-4"> |
| <p className="text-sm text-muted-foreground">{td("Horizontal")}</p> |
| <Separator /> |
| <div className="flex items-center gap-4 h-8"> |
| <span className="text-sm">{td("Left")}</span> |
| <Separator orientation="vertical" /> |
| <span className="text-sm">{td("Right")}</span> |
| </div> |
| </div> |
| </Section> |
|
|
| {} |
| {} |
| {} |
| <Section title={td("Common Icons (Lucide)")}> |
| <div className="grid grid-cols-4 md:grid-cols-6 gap-4" data-e2e-ignore-i18n="true"> |
| {[ |
| ["Inbox", Inbox], |
| ["ListTodo", ListTodo], |
| ["CircleDot", CircleDot], |
| ["Hexagon", Hexagon], |
| ["Target", Target], |
| ["LayoutDashboard", LayoutDashboard], |
| ["Bot", Bot], |
| ["DollarSign", DollarSign], |
| ["History", History], |
| ["Search", Search], |
| ["Plus", Plus], |
| ["Trash2", Trash2], |
| ["Settings", Settings], |
| ["User", User], |
| ["Mail", Mail], |
| ["Upload", Upload], |
| ["Zap", Zap], |
| ].map(([name, Icon]) => { |
| const LucideIcon = Icon as React.FC<{ className?: string }>; |
| return ( |
| <div key={name as string} className="flex flex-col items-center gap-1.5 p-2"> |
| <LucideIcon className="h-4 w-4 text-muted-foreground" /> |
| <span className="text-[10px] text-muted-foreground font-mono">{name as string}</span> |
| </div> |
| ); |
| })} |
| </div> |
| </Section> |
|
|
| {} |
| {} |
| {} |
| <Section title={td("Keyboard Shortcuts")}> |
| <div className="border border-border rounded-md divide-y divide-border text-sm"> |
| {[ |
| ["Cmd+K / Ctrl+K", td("Open Command Palette")], |
| ["C", td("New Issue (outside inputs)")], |
| ["[", td("Toggle Sidebar")], |
| ["]", td("Toggle Properties Panel")], |
| |
| ["Cmd+Enter / Ctrl+Enter", td("Submit markdown comment")], |
| ].map(([key, desc]) => ( |
| <div key={key} className="flex items-center justify-between px-4 py-2"> |
| <span className="text-muted-foreground">{desc}</span> |
| <kbd className="px-2 py-0.5 text-xs font-mono bg-muted rounded border border-border"> |
| {key} |
| </kbd> |
| </div> |
| ))} |
| </div> |
| </Section> |
| </div> |
| ); |
| } |
|
|