import { AnimatedDialogContent } from "@/components/AnimatedDialogContent"; import { ExternalAgentPresetPicker, DotConnectionChecks } from "@/components/new-agent/ExternalAgentInviteContent"; import { AgentMailApiKeyField } from "@/features/connections/AgentMailApiKeyField"; import { TaskBrowserFooter } from "@/components/task-side-panel/TaskBrowserFooter"; import { TaskBrowserActivity } from "@/components/task-side-panel/TaskBrowserActivity"; import type { TaskBrowser } from "@paperclipai/shared"; import { DispositionRecoveryNotice } from "../components/DispositionRecoveryNotice"; import { CloudSignIn } from "../components/CloudSignIn"; import { CloudAccessError } from "../components/CloudAccessGate"; import { SetupPrompt } from "./apps/chat/SetupPrompt"; import { MediaArtifactCard } from "@/components/artifacts/MediaArtifactCard"; import { SkillSourceTree } from "./skills/SkillSourceTree"; import { SkillBinaryFile } from "../components/SkillBinaryFile"; import { WebhookUrlWarning } from "@/components/routine-triggers/WebhookUrlWarning"; import { SetupWizardNavigation, SetupWizardFooter } from "../components/SetupWizard"; import { RemoteMcpDesignExample } from "@/features/connections/remote-mcp/RemoteMcpDesignExample"; import { AgentConversationSidebar } from "@/components/AgentConversationSidebar"; import { AgentChatPicker } from "@/components/AgentChatPicker"; import { TaskChatProjectCreatedCard } from "@/components/task-chat/TaskChatProjectCreatedCard"; import { TextAttachmentPreview } from "@/components/task-side-panel/TaskAttachmentPanel"; import { AnnouncementCard } from "@/components/AnnouncementCard"; import { announcementPreview, announcementAnimationPreview, announcementAnimationPreviewSrc } from "@/lib/announcement-preview"; import { TaskDetailTasksPanel } from "@/components/task-detail/TaskDetailTasksPanel"; import { AiConnectionDesignExamples } from "@/components/ai-connections/AiConnectionDesignExamples"; import { SavedProviderKeySelect } from "../components/onboarding/SavedProviderKeySelect"; import { AgentAvatar } from "@/components/AgentAvatar"; import { AgentCharacter } from "@/components/AgentCharacter"; import { AGENT_PALETTE_IDS, appearanceForPalette } from "@paperclipai/shared"; import { RepositoryEditor } from "@/components/RepositoryEditor"; import { TaskChatRunnerActivityGroup } from "@/components/task-chat/TaskChatRunnerActivityGroup"; import { TaskChatMarker } from "@/components/task-chat/TaskChatMarker"; import { TaskChatComposer } from "@/components/task-chat/TaskChatComposer"; import { ComposerAddMenu, ComposerModeChip } from "@/components/task-chat/ComposerAddMenu"; import type { IssueWorkMode } from "@paperclipai/shared"; import { TaskTreeControlDialog, TaskTreeControlMenuItems } from "@/components/TaskTreeControls"; 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 { ThemeToggle } from "@/components/ThemeToggle"; import { Badge } from "@/components/ui/badge"; import { InlineBanner } from "@/components/InlineBanner"; import { BuiltInLifecycleChip } from "@/components/BuiltInAgentBadges"; 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 { ResizableHandle, ResizablePanel, ResizablePanelGroup, } from "@/components/ui/resizable-panels"; 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 { AgentCapsule, AGENT_GRADIENT_COUNT } from "@/components/AgentCapsule"; import { AgentRunCard } from "@/components/ActiveAgentsPanel"; import { StatusBadge, IssueStatusBadge } from "@/components/StatusBadge"; import { StatusIcon } from "@/components/StatusIcon"; import { EnforcementBanner } from "@/components/EnforcementBanner"; import { ActionCard, ActionCardMobile, BindingsTable } from "@/components/actions/ActionCard"; import { PriorityIcon } from "@/components/PriorityIcon"; import { SHOW_TASK_PRIORITY_UI } from "@/lib/ui-flags"; 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 { AppLogo } from "@/pages/apps/AppLogo"; import { IssueReferencePill } from "@/components/IssueReferencePill"; import { LockedIssueChip } from "@/components/LockedIssueChip"; import { MembershipAction } from "@/components/MembershipAction"; import { IssueOutputSection } from "@/components/issue-output/IssueOutputSection"; import { EnvironmentVariablesEditor } from "@/components/environment-variables-editor"; import { IssueThreadInteractionCard } from "@/components/IssueThreadInteractionCard"; import { connectedConnectionIntentInteraction, issueThreadInteractionFixtureMeta, pendingConnectionIntentInteraction, retryConnectionIntentInteraction, } from "@/fixtures/issueThreadInteractionFixtures"; import type { CompanySecret, EnvBinding, Issue } from "@paperclipai/shared"; import { CollectionToolbar } from "@/components/CollectionToolbar"; import { IssueRow } from "@/components/IssueRow"; 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, ]; const DESIGN_GUIDE_TASK = { id: "design-guide-task", identifier: "PAP-427", title: "Reconcile the navigation model across operator surfaces", status: "in_progress", priority: "medium", blockerAttention: false, } as unknown as Issue; /* ------------------------------------------------------------------ */ /* 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)} /> ))}
); } // Reusable environment-variables editor: one shared grid, in-field source // switch, fuzzy secret picker, sensitive-value detection, inline health. const DESIGN_GUIDE_SECRETS: CompanySecret[] = [ { id: "dg-github", companyId: "dg", scope: "company", ownerUserId: null, userSecretDefinitionId: null, key: "github_token", name: "GITHUB_TOKEN", provider: "local_encrypted", status: "active", managedMode: "paperclip_managed", externalRef: null, providerConfigId: null, providerMetadata: null, latestVersion: 3, description: null, lastResolvedAt: null, lastRotatedAt: null, deletedAt: null, createdByAgentId: null, createdByUserId: null, createdAt: new Date("2026-03-01T10:00:00.000Z"), updatedAt: new Date("2026-03-01T10:00:00.000Z"), }, { id: "dg-db", companyId: "dg", scope: "company", ownerUserId: null, userSecretDefinitionId: null, key: "db_connection", name: "DB_CONNECTION", provider: "local_encrypted", status: "active", managedMode: "paperclip_managed", externalRef: null, providerConfigId: null, providerMetadata: null, latestVersion: 3, description: null, lastResolvedAt: null, lastRotatedAt: null, deletedAt: null, createdByAgentId: null, createdByUserId: null, createdAt: new Date("2026-03-01T10:00:00.000Z"), updatedAt: new Date("2026-03-01T10:00:00.000Z"), }, ]; function EnvironmentVariablesEditorShowcase() { const [env, setEnv] = useState>({ NODE_ENV: { type: "plain", value: "production" }, GH_TOKEN: { type: "secret_ref", secretId: "dg-github", version: "latest" }, DB_URL: { type: "secret_ref", secretId: "dg-db", version: 3 }, STRIPE_API_KEY: { type: "plain", value: "demo-value-not-a-credential" }, }); return (
setEnv(next ?? {})} onCreateSecret={async (name) => ({ ...DESIGN_GUIDE_SECRETS[0]!, id: `dg-${name}`, key: name, name: name.toUpperCase(), latestVersion: 1, })} />
); } /* ------------------------------------------------------------------ */ /* Color swatch */ /* ------------------------------------------------------------------ */ function Swatch({ name, cssVar }: { name: string; cssVar: string }) { return (

{cssVar}

{name}

); } /* ------------------------------------------------------------------ */ /* Page */ /* ------------------------------------------------------------------ */ function TaskExecutionControlsExample() { const [running, setRunning] = useState(true); const [dialogMode, setDialogMode] = useState<"resume" | "cancel" | "restore" | null>(null); const [wake, setWake] = useState(true); return
setRunning(false)} onResume={() => setDialogMode("resume")} onCancel={() => setDialogMode("cancel")} onRestore={() => setDialogMode("restore")} />

{running ? "Running: type to switch Stop to Send." : "Paused: resume from the menu."}

{!running ? : null} setDialogMode("resume") } : null} onAdd={async () => {}} workMode="standard" stopScope="subtree" onStop={running ? async () => setRunning(false) : undefined} /> { if (!open) setDialogMode(null); }} mode={dialogMode ?? "cancel"} scope="subtree" affectedCount={3} affectedAgentCount={2} loading={false} pending={false} valid wakeAgents={wake} onWakeAgentsChange={setWake} onRetry={() => {}} onApply={() => { setRunning(dialogMode !== "cancel" && wake); setDialogMode(null); }} />
; } function TaskPendingInputExample() { const [open, setOpen] = useState(true); const [pending, setPending] = useState(true); return
{}} workMode="standard" takeover={pending && open ? { id: "design-question", label: "Question", pendingCount: 1, content:

Should the agent use the existing draft?

, onDismiss: () => setOpen(false), onSkip: () => setPending(false), } : null} pendingTakeover={pending ? { count: 1, label: "Question", onOpen: () => setOpen(true) } : null} />
; } function AgentChatPickerExample() { const [state, setState] = useState<"closed" | "empty" | "loading" | "error">("closed"); return
{ if (!open) setState("closed"); }} onSelect={() => {}} loading={state === "loading"} error={state === "error" ? new Error("Unavailable") : null} onRetry={() => setState("empty")} />
; } function ComposerActionsExample() { const [mode, setMode] = useState("standard"); return
{}} onGoal={() => {}} /> setMode("standard")} /> Plus menu · removable mode chip
; } export function DesignGuide() { const [agentmailDemoKey, setAgentmailDemoKey] = useState(""); const [wizardStep, setWizardStep] = useState(0); 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" }, // PAP-411: priority filter demo row suppressed while SHOW_TASK_PRIORITY_UI is off. ...(SHOW_TASK_PRIORITY_UI ? [{ key: "priority", label: "Priority", value: "High" } as FilterValue] : []), ]); 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", "resizable-panels", "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", "EnvironmentVariablesEditor", "InlineBanner", "BuiltInAgentGate", "BuiltInLifecycleChip", "CollectionToolbar", "IssueRow", "ContextualSidebarFrame", ].map((name) => ( {name} ))}
{}} /> {}} /> {}} />

CollectionToolbar owns shared geometry while each page owns its state and behavior. The canonical task row is opt-in during migration: status leads, unread work uses title emphasis, metadata remains stable, and the task identifier trails.

Recent tasks} search={} controls={} actions={} feedback={1 task · Updated newest first} />
Updated 12m ago} actions={} />
{/* ============================================================ */} {/* 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", "blocked", "cancelled"].map( (s) => ( ) )}
{["backlog", "todo", "in_progress", "in_review", "done", "cancelled", "blocked"].map( (s) => (
{s}
) )}
Click the icon to change status (current: {status})
{/* PAP-411: PriorityIcon showcase gated behind SHOW_TASK_PRIORITY_UI per board decision. */} {SHOW_TASK_PRIORITY_UI && (
{["critical", "high", "medium", "low"].map((p) => (
{p}
))}
Click the icon to change (current: {priority})
)}
{(["running", "active", "paused", "error", "archived"] as const).map((label) => (
{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 variant="property" for compact badges with direct navigation. Pass onRemove for a separate blocker removal control with reserved space. Use strikethrough for "removed" contexts.

window.alert("Blocker removed")} issue={{ id: "demo-blocker", identifier: "PAP-303", title: "Hover or focus to remove blocker", status: "in_review" }} />

Existence-only reference to a private task shown from a surface the viewer can see (a blocker edge, a mention). Mono id + lock, dashed muted border,{" "} no title, never a link. Falls back to "Private" when even the identifier is withheld.

{/* ============================================================ */} {/* AGENT CAPSULE */} {/* ============================================================ */}

The brand "capsule is the agent" motif. A single agent reads as a tall pill that moves through three states as it comes to life. The online fill uses the live brand agent-gradient tokens (--agent-Na →{" "} --agent-Nb); prefers-reduced-motion{" "} skips the liquid rise and pulses and renders the final state.

slot
configured
online
online · blue glow
sm
md
lg
custom px
{Array.from({ length: AGENT_GRADIENT_COUNT }, (_, i) => (
{i + 1}
))}
{/* ============================================================ */} {/* FORM ELEMENTS */} {/* ============================================================ */}