import { useState } from "react"; 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, AgentStatusBadge, AgentStatusCapsule } from "@/components/StatusBadge"; import { StatusIcon } from "@/components/StatusIcon"; import { PriorityIcon } from "@/components/PriorityIcon"; 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"; import { MembershipAction } from "@/components/MembershipAction"; import { IssueOutputSection } from "@/components/issue-output/IssueOutputSection"; import { EnvInputsList, ExternalSourcesList, RequiredSkillsList, StepSkillPlan, StepSourcePolicy, TeamCard, TeamHierarchyPreview, TeamRow, } from "@/pages/TeamCatalog"; import { currentInstalledState, onboardingTeams, optionalTeam, outOfDateInstalledState, sampleSkillPreparations, sampleTeam, warnTeam, } from "@/pages/TeamCatalog.fixtures"; import type { IssueWorkProduct } from "@paperclipai/shared"; /* ------------------------------------------------------------------ */ /* Sample data for the Issue Output surface showcase */ /* ------------------------------------------------------------------ */ function sampleOutput( id: string, attachmentId: string, contentType: string, filename: string, opts: { byteSize: number; isPrimary?: boolean; createdAt: string }, ): IssueWorkProduct { const contentPath = `/api/attachments/${attachmentId}/content`; return { id, companyId: "demo-company", projectId: null, issueId: "demo-issue", executionWorkspaceId: null, runtimeServiceId: null, type: "artifact", provider: "paperclip", externalId: null, title: filename, url: null, status: "active", reviewState: "none", isPrimary: Boolean(opts.isPrimary), healthStatus: "unknown", summary: null, createdByRunId: null, createdAt: new Date(opts.createdAt), updatedAt: new Date(opts.createdAt), metadata: { attachmentId, contentType, byteSize: opts.byteSize, contentPath, openPath: contentPath, downloadPath: `${contentPath}?download=1`, originalFilename: filename, }, } as IssueWorkProduct; } const DESIGN_GUIDE_OUTPUTS: IssueWorkProduct[] = [ sampleOutput("wp-vid", "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa", "video/mp4", "q3-summary.mp4", { byteSize: 19_293_798, isPrimary: true, createdAt: "2026-05-30T12:00:00Z", }), sampleOutput("wp-pdf", "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb", "application/pdf", "talking-points.pdf", { byteSize: 421_888, createdAt: "2026-05-30T11:52:00Z", }), ]; const DESIGN_GUIDE_DEGRADED_OUTPUTS: IssueWorkProduct[] = [ { ...sampleOutput("wp-broken", "cccccccc-cccc-4ccc-8ccc-cccccccccccc", "video/mp4", "corrupt-output.mp4", { byteSize: 0, isPrimary: true, createdAt: "2026-05-30T12:01:00Z", }), // Strip the path metadata so it fails the shared artifact schema. metadata: { attachmentId: "cccccccc-cccc-4ccc-8ccc-cccccccccccc", contentType: "video/mp4" }, } as IssueWorkProduct, ]; /* ------------------------------------------------------------------ */ /* Section wrapper */ /* ------------------------------------------------------------------ */ function Section({ title, children }: { title: string; children: React.ReactNode }) { return (

{title}

{children}
); } function SubSection({ title, children }: { title: string; children: React.ReactNode }) { return (

{title}

{children}
); } // Onboarding seam (design §6 + §12.5): the TeamCard tile in its "Pick a starter // team" 3-col grid, with the first defaultInstall tile selected. function TeamCardShowcase() { const [selectedId, setSelectedId] = useState(onboardingTeams[0]?.id ?? null); return (
{onboardingTeams.map((team) => ( setSelectedId(team.id)} /> ))}
); } /* ------------------------------------------------------------------ */ /* Color swatch */ /* ------------------------------------------------------------------ */ function Swatch({ name, cssVar }: { name: string; cssVar: string }) { return (

{cssVar}

{name}

); } /* ------------------------------------------------------------------ */ /* Page */ /* ------------------------------------------------------------------ */ export function DesignGuide() { 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("Click to edit this text"); const [inlineTitle, setInlineTitle] = useState("Editable Title"); const [inlineDesc, setInlineDesc] = useState( "This is an editable description. Click to edit it — the textarea auto-sizes to fit the content without layout shift." ); const [filters, setFilters] = useState([ { key: "status", label: "Status", value: "Active" }, { key: "priority", label: "Priority", value: "High" }, ]); const [allowExternal, setAllowExternal] = useState(false); const [allowUnpinned, setAllowUnpinned] = useState(false); const [allowLocalPath, setAllowLocalPath] = useState(false); return (
{/* Page header */}

Design Guide

Every component, style, and pattern used across Paperclip.

{/* ============================================================ */} {/* COVERAGE */} {/* ============================================================ */}

This page should be updated when new UI primitives or app-level patterns ship.

{[ "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) => ( {name} ))}
{[ "StatusBadge", "StatusIcon", "PriorityIcon", "EntityRow", "EmptyState", "MetricCard", "FilterBar", "InlineEditor", "PageSkeleton", "Identity", "CommentThread", "MarkdownEditor", "PropertiesPanel", "Sidebar", "CommandPalette", ].map((name) => ( {name} ))}
{/* ============================================================ */} {/* COLORS */} {/* ============================================================ */}
{/* ============================================================ */} {/* TYPOGRAPHY */} {/* ============================================================ */}

Page Title — text-xl font-bold

Section Title — text-lg font-semibold

Section Heading — text-sm font-semibold uppercase tracking-wide

Card Title — text-sm font-medium

Card Title Alt — text-sm font-semibold

Body text — text-sm

Muted description — text-sm text-muted-foreground

Tiny label — text-xs text-muted-foreground

Mono identifier — text-sm font-mono text-muted-foreground

Large stat — text-2xl font-bold

Log/code text — font-mono text-xs

{/* ============================================================ */} {/* SPACING & RADIUS */} {/* ============================================================ */}
{[ ["sm", "var(--radius-sm)"], ["md", "var(--radius-md)"], ["lg", "var(--radius-lg)"], ["xl", "var(--radius-xl)"], ["full", "9999px"], ].map(([label, radius]) => (
{label}
))}
{/* ============================================================ */} {/* BUTTONS */} {/* ============================================================ */}
{/* ============================================================ */} {/* BADGES */} {/* ============================================================ */}
Default Secondary Outline Destructive Ghost
{/* ============================================================ */} {/* STATUS BADGES & ICONS */} {/* ============================================================ */}
{[ "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) => ( ))}
{["backlog", "todo", "in_progress", "in_review", "done", "cancelled", "blocked"].map( (s) => (
{s}
) )}
Click the icon to change status (current: {status})
{["critical", "high", "medium", "low"].map((p) => (
{p}
))}
Click the icon to change (current: {priority})

The agents section uses a brand heartbeat capsule (8×16) plus a brand .task-chip. Four states only: idle (gray), running (blue, pulses), paused (amber), error (red, blinks). Motion honors prefers-reduced-motion.

{(["idle", "running", "paused", "error"] as const).map((label) => (
))}
{[ ["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]) => ( {label} ))}

Used wherever a task is referenced — in markdown, the Related Work tab, and activity summaries. Pass status to show the target issue's state at a glance. Use strikethrough for "removed" contexts.

{/* ============================================================ */} {/* FORM ELEMENTS */} {/* ============================================================ */}