import { TaskChatProjectCreatedCard } from "@/components/task-chat/TaskChatProjectCreatedCard";
import { TaskDetailTasksPanel } from "@/components/task-detail/TaskDetailTasksPanel";
import { FileTree } from "@/components/FileTree";
import { AiConnectionDesignExamples } from "@/components/ai-connections/AiConnectionDesignExamples";
import { SavedProviderKeySelect } from "../components/onboarding/SavedProviderKeySelect";
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 { TaskTreeControlDialog, TaskTreeControlMenuItems } from "@/components/TaskTreeControls";
import { useState } from "react";
import { ServicesList } from "./apps/app-detail/ServicesPanel";
import { ComposioProvenanceChip } from "./apps/ComposioProvenanceChip";
import type { ComposioServiceRow } from "./apps/composio-services";
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 { 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 */
/* ------------------------------------------------------------------ */
/**
* Composio service rows for the design guide (PAP-17865). One row per state, so
* a reader can compare all four side by side rather than connecting a real
* Composio project to see them.
*/
const DESIGN_GUIDE_COMPOSIO_ROWS: ComposioServiceRow[] = [
{
toolkitSlug: "github",
name: "GitHub",
description: "Issues, pull requests, and repository actions",
logoUrl: null,
state: "connected",
connectedAccountStatus: "ACTIVE",
childConnectionId: "design-guide-child",
toolCount: 42,
noAuth: false,
},
{
toolkitSlug: "hubspot",
name: "HubSpot",
description: "CRM contacts and deals",
logoUrl: null,
state: "attention",
connectedAccountStatus: "EXPIRED",
childConnectionId: "design-guide-child-2",
toolCount: 18,
noAuth: false,
},
{
toolkitSlug: "slack",
name: "Slack",
description: "Channels and messages",
logoUrl: null,
state: "pending",
connectedAccountStatus: "INITIALIZING",
childConnectionId: null,
toolCount: 12,
noAuth: false,
},
{
toolkitSlug: "gmail",
name: "Gmail",
description: "Read and send mail",
logoUrl: null,
state: "not_connected",
connectedAccountStatus: null,
childConnectionId: null,
toolCount: 9,
noAuth: false,
},
];
function Section({ title, children }: { title: string; children: React.ReactNode }) {
return (
{title}
{cssVar}
{name}
{running ? "Running: type to switch Stop to Send." : "Paused: resume from the menu."}
Every component, style, and pattern used across Paperclip.
This page should be updated when new UI primitives or app-level patterns ship.
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.
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
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.
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.
Title (single-line)
Body text (single-line)
Description (multiline, auto-sizing)
Current value: {selectValue}
Agent heartbeat
Last run succeeded 24s ago. Next timer run in 9m.
The dashboard and Live runs page use the same compact cards. In-progress task icons animate across the app, including between runs, to represent task workflow status. Live indicators report active execution. Open a run to view its status and transcript.
Card content goes here. This is the main body area.
Overview tab content.
Runs tab content.
Config tab content.
Costs tab content.
Summary content with underline tabs.
Details content.
Comments content.
Layout accepts sidebarSections to compose additional SidebarSection groups inside the shared sidebar. Use SidebarNavItem for each row, with sibling action buttons for starring or menus. Starred agent conversations precede recent conversations without a divider. Stars appear on hover or keyboard focus. Task breadcrumbs support leading identity and trailing actions beside the label, including single-item task headers; see the Agent chat Storybook.
Started working on the authentication module. Will need API keys configured.
API keys have been added to the vault. Please proceed.
| Model | Tokens | Cost |
|---|---|---|
| claude-sonnet-4-20250514 | 1.2M | $18.00 |
| claude-haiku-4-20250506 | 500k | $1.25 |
| Total | 1.7M | $19.25 |
Horizontal
Components from the Team Catalog browse/install surface (/teams-catalog).
Fixtures are shared with the Storybook stories.
Installed teams collapse under INSTALLED · N; an out-of-date
install (server originHash ≠ catalog contentHash)
shows the amber ↑ badge (PAP-10256).
Square tile for the onboarding “Pick a starter team” grid. Selected tile gets{" "}
ring-2 ring-ring. Drives the{" "}
useInstallTeamCatalogEntry simplified flow.
When an issue has produced no artifact work products, the Output section renders nothing at all (no placeholder card).
Persistent at the top of the Tools & Access surface. Tints to denied-detected when
governed tool calls were denied or failed in the last hour. Observability only — enforcement lives
in the tool gateway.
Static governance copy with a tone. Used for the PAP-10400 trust-tier banner on Runtime and the
effective-access banner on Agent → Tools. Pass title/body and an optional{" "}
icon.
Signed payload sha256 + expiry surface on every variant (PAP-10400). The{" "}
stale variant tints the border amber, banners the catalog-hash mismatch, strikes through
the previous hash next to the current one, and renders Approve disabled until the request
is re-issued.
Identical content; the three buttons stack full-width in the order Approve / Deny / Edit & re-sign, and the bindings table uses a 70px label column.
Two-column key/value block with mono values. Lives inside ActionCard and is reused
standalone in the audit row drilldown.
Policy decisions, connection/runtime health, and catalog quarantine all route through the canonical{" "}
StatusBadge keys defined in lib/status-colors.
A broker connection (Composio) fronts many services, so its detail page lists toolkits
with per-service state instead of one credential. Row state comes from Composio's own
account status, which is why there is a fourth attention state alongside the
three the design asks for: an expired credential is neither connected nor still settling.
Shown wherever a brokered child connection appears, so the parent/child coupling is legible. Links to the broker's Services tab when the parent is known.
Loading, errors, empty search, mobile, and short viewports are covered in the Project repos Storybook stories.
Reusable env-var editor (agents, projects, environments, routines). One shared grid, an in-field Text/Secret source switch, a fuzzy secret picker with a pinned “Create secret” item, automatic sensitive-value detection, and inline secret-health warnings. See the Storybook Product/Environment Variables Editor stories for all 10 states.
Recovery runs in the background. Task lists keep their ordinary status without execution badges. Active transcript headers keep saying Working during automatic recovery. Recovery decisions and attempts belong in the run log; there is no execution status card or reconciliation form.
The task card is the dialog host for the shared connection setup flow. Provider forms, validation, OAuth, access selection, and completion come from the same feature module as the full-page Apps setup; this card owns only audience, dialog, and task refresh behavior. Pending connections stay in the timeline beside a usable composer. The independently addressable Connections/In-task connections stories cover access, OAuth recovery, narrow layouts, completion, and historical outcomes.
Design-system wrapper over react-resizable-panels{" "} (Skill Studio D2). Drag a handle to resize; panels accept percentage or pixel (minSize="240px") constraints and the middle panel is collapsible. Use anywhere a split view is needed.
Token-backed full-width notice (brandBanner tones). Use{" "} info for provenance/context and{" "} warning for paused/attention. Supports an optional bold title and a trailing actions slot. Replaces hand-rolled{" "} bg-yellow-*/bg-blue-*{" "} banners.
Cached file browsers include folders in selection, including empty folders. Other file trees select files by default.
A derived lifecycle chip (amber) for attention states. The lifecycle chip is separate from the agent status vocabulary and only shows for{" "} needs_setup / pending_approval.
<BuiltInAgentGate agentKey> composes{" "} PageSkeleton + EmptyState{" "} + InlineBanner to render the loading / setup / pending-approval / paused / ready states of a feature that depends on a built-in agent.