import { TaskDetailTasksPanel } from "@/components/task-detail/TaskDetailTasksPanel";
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}
{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 (
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.
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" }} />
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.
{/* ============================================================ */}
{/* FORM ELEMENTS */}
{/* ============================================================ */}
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 TitleCard description with supporting text.
Card content goes here. This is the main body area.
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.
Reset to defaults}
>
Ships with Paperclip and powers Briefs. It can be paused but not deleted.
>
}
>
Its built-in agent was paused 2 days ago, so new briefs aren't being generated.
Retry}
>
The linked issue reached a terminal state before a summary was written.
Compact variant for embedding inside dialogs and modals.
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.