- Accordion
- Agent Thinking
- Alert
- Alert Dialog
- Announcement
- Aspect Ratio
- Attachment
- Avatar
- Badge
- Breadcrumb
- Bubble
- Button
- Button Group
- Calendar
- Card
- Carousel
- Checkbox
- Checkbox Card
- Code Block
- Collapsible
- Color Picker
- Combobox
- Command
- Composer
- Composer Loader
- Context Menu
- Data Grid
- Data Table
- Date Picker
- Dialog
- Direction
- Drawer
- Dropdown Menu
- Empty
- Field
- File Upload
- Hover Card
- Input
- Input Group
- Input Otp
- Item
- Kbd
- Label
- Marker
- Menubar
- Message
- Message Scroller
- Meter
- Native Select
- Navigation Menu
- Number Field
- Pagination
- Period Switcher
- Popover
- Progress
- Questionnaire
- Radio Card
- Radio Group
- Resizable
- Reveal
- Scroll Area
- Search Field
- Select
- Separator
- Sheet
- Sidebar
- Skeleton
- Slider
- Social Button
- Sonner
- Spinner
- Status Dot
- Switch
- Switch Card
- Table
- Tabs
- Tag Group
- Textarea
- Theme Toggle
- Time Field
- Toggle
- Toggle Group
- Tooltip
- Trace
- Tree View
- Agent Creator 01
- Agent Limits Card 01
- Agent Progress 01
- AI Chat 01
- AI Image Generation 01
- AI Profile 01
- Alerts Card 01
- Auth Card 01
- Calendar 01
- Composer 01
- Composer Attachments 01
- Composer Panel 01
- Finance Dashboard 01
- Home Dashboard 01
- Marketing Dashboard 01
- Medical Profile 01
- Meeting Scheduler 01
- Multi Agent Chat 01
- Notification Center 01
- Patient Info Card 01
- Project Board 01
- Questionnaire 01
- Settings Modal 01
- Sidebar 01
- Stat Cards 01
- Task List 01
- Web Search 01
- Activity Rings Card 01
- Area Chart Card 01
- Bar List Card 01
- Combo Chart Card 01
- Contributions Card 01
- Earnings Chart Card 01
- Funnel Chart Card 01
- Heatmap Chart Card 01
- Line Chart Card 01
- Most Active Days Card 01
- Orders Chart Card 01
- Radar Chart Card 01
- Radial Chart Card 01
- Revenue Chart Card 01
- Sankey Chart Card 01
- Scatter Chart Card 01
- Sleep Score Card 01
- Stage Bars Card 01
- Steps Card 01
A
Alex Morgan
@alexmPro
Contributions this year
$5,318+9.4%
- 7.2M
- Lifetime tokens
- 840K
- Peak tokens
- 9h 18m
- Longest task
- 48 days
- Top streak
Activity
Nov 1 to Nov 30
282 agents
November
Nov 1Today
Last 30 days
8.6M tokens
+18.2%Oct 8Today
Installation
Set up the @cobalt registry first, as described in Installation, then add the block:
$ pnpm dlx shadcn@latest add @cobalt/ai-profile-01
The example uses local sample data and renders the profile body without navigation or heading, inside a scrollable preview. Rows, heatmap cells, bars and the usage line animate in on mount, and every animation stops at its final readable state under prefers-reduced-motion.
Source
"use client";
import { RiCheckLine, RiEditBoxLine, RiShare2Line } from "@remixicon/react";
import { cn } from "cn";
import * as React from "react";
import { AiProfileAgentsCard } from "@/components/blocks/ai-profile-01/components/ai-profile-agents-card";
import { profileCoverUrl } from "@/components/blocks/ai-profile-01/components/ai-profile-cover";
import { profileStats } from "@/components/blocks/ai-profile-01/components/ai-profile-data";
import { AiProfileHeatmap } from "@/components/blocks/ai-profile-01/components/ai-profile-heatmap";
import { AiProfileTokensCard } from "@/components/blocks/ai-profile-01/components/ai-profile-tokens-card";
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { TooltipProvider } from "@/components/ui/tooltip";
// 500ms, 12px rise. Each row sets --delay in 40ms steps.
const rise = "ai-profile-01-rise";
function Row({
step,
className,
children,
}: {
step: number;
className?: string;
children: React.ReactNode;
}) {
return (
<div
className={cn(rise, className)}
style={{ "--delay": `${step * 40}ms` } as React.CSSProperties}
>
{children}
</div>
);
}
/**
* A single-column profile: cover, identity, contribution total, stat tiles and
* an activity heatmap, then an agents card and a token usage card. It carries
* no navigation or heading, so it drops into an app shell or any page. Pass
* `popupClassName` (a theme scope) so tooltips pick up the same tokens.
*/
export function AiProfile({
className,
popupClassName,
}: {
className?: string;
popupClassName?: string;
}) {
const [name, setName] = React.useState("Alex Morgan");
const [editing, setEditing] = React.useState(false);
const [copied, setCopied] = React.useState(false);
React.useEffect(() => {
if (!copied) {
return;
}
const timer = window.setTimeout(() => setCopied(false), 1800);
return () => window.clearTimeout(timer);
}, [copied]);
const share = async () => {
try {
await navigator.clipboard.writeText(window.location.href);
} catch {
// Clipboard can be blocked; the button still confirms the action.
}
setCopied(true);
};
return (
<TooltipProvider delay={300}>
<div
className={cn(
"@container mx-auto flex w-full max-w-[680px] min-w-0 flex-col gap-4",
className
)}
>
<Row step={0}>
<Card radius={24} size="flush" variant="outline">
{/* oxlint-disable-next-line next/no-img-element -- blocks stay framework-agnostic, so no next/image */}
<img
alt="Illustrated cover: snowy peaks under a low sun"
className="h-28 w-full object-cover @xl:h-[165px]"
src={profileCoverUrl}
/>
<div className="flex flex-col gap-4 p-4 pt-0">
<div className="flex items-start justify-between gap-2">
<Avatar className="-mt-10 size-20">
<AvatarFallback>
{(name.trim() || "?").charAt(0).toUpperCase()}
</AvatarFallback>
</Avatar>
<div className="mt-5 -mb-3 mr-1 flex gap-2">
<Button variant="outline" size="sm" onClick={share}>
{copied ? (
<RiCheckLine data-icon="inline-start" />
) : (
<RiShare2Line data-icon="inline-start" />
)}
{copied ? "Copied" : "Share"}
</Button>
<Button
variant="outline"
size="sm"
onClick={() => setEditing((value) => !value)}
>
{editing ? (
<RiCheckLine data-icon="inline-start" />
) : (
<RiEditBoxLine data-icon="inline-start" />
)}
{editing ? "Done" : "Edit"}
</Button>
</div>
</div>
<div className="-mt-1 flex flex-col gap-0.5">
{editing ? (
<div className="flex max-w-60 flex-col gap-1">
<Label htmlFor="ai-profile-display-name">
Display name
</Label>
<Input
id="ai-profile-display-name"
size="sm"
value={name}
onChange={(event) => setName(event.target.value)}
/>
</div>
) : (
<h2 className="text-xl font-medium">
{name.trim() || "Your name"}
</h2>
)}
<p className="flex items-center gap-1.5 text-base text-muted-foreground">
@alexm
<Badge variant="caption" tone="neutral">
Pro
</Badge>
</p>
</div>
<div className="-mb-1 flex flex-col gap-1">
<p className="text-sm text-muted-foreground">
Contributions this year
</p>
<div className="flex items-center gap-2">
<span className="text-2xl leading-7 font-medium tabular-nums">
$5,318
</span>
<Badge variant="label" tone="purple">
+9.4%
</Badge>
</div>
</div>
<dl className="grid grid-cols-2 gap-2 @xl:grid-cols-4">
{profileStats.map((stat, index) => (
<Row key={stat.caption} step={index + 1} className="grid">
<Card variant="muted" size="xs" radius={12}>
<CardContent className="flex flex-col gap-0">
<dd className="text-sm leading-4.5 font-medium">
{stat.value}
</dd>
<dt className="text-xs leading-4.5 text-muted-foreground">
{stat.caption}
</dt>
</CardContent>
</Card>
</Row>
))}
</dl>
<AiProfileHeatmap popupClassName={popupClassName} />
</div>
</Card>
</Row>
<Row step={1}>
<AiProfileAgentsCard />
</Row>
<Row step={2}>
<AiProfileTokensCard />
</Row>
</div>
</TooltipProvider>
);
} On this page