{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "ai-profile",
  "title": "AI Profile",
  "description": "Token use, agent mix, streaks, and output over time.",
  "dependencies": [
    "lucide-react@^1.33.0",
    "recharts@^3.10.1"
  ],
  "registryDependencies": [
    "https://components.exe.xyz/r/app-shell.json",
    "https://components.exe.xyz/r/avatar.json",
    "https://components.exe.xyz/r/breadcrumb.json",
    "https://components.exe.xyz/r/button.json",
    "https://components.exe.xyz/r/chart-card.json",
    "https://components.exe.xyz/r/chip.json",
    "https://components.exe.xyz/r/contributions-card.json",
    "https://components.exe.xyz/r/icon-button.json",
    "https://components.exe.xyz/r/input.json",
    "https://components.exe.xyz/r/line-chart-card.json",
    "https://components.exe.xyz/r/sidebar.json"
  ],
  "files": [
    {
      "path": "src/components/templates/ai-profile.tsx",
      "content": "\"use client\"\n\nimport { useState } from \"react\"\nimport { BarChart3, Bot, Flame, Menu, Sparkles, User } from \"lucide-react\"\n\nimport {\n  Bar,\n  BarChart,\n  CartesianGrid,\n  ResponsiveContainer,\n  Tooltip,\n  XAxis,\n  YAxis,\n} from \"recharts\"\n\nimport { ContributionsCard } from \"@/components/charts/contributions-card\"\nimport { LineChartCard } from \"@/components/charts/line-chart-card\"\nimport {\n  ChartCard,\n  ChartPeriodSwitch,\n  ChartPlot,\n  PlotTooltip,\n  axisTick,\n  formatNumber,\n  usePrefersReducedMotion,\n} from \"@/components/charts/chart-card\"\nimport { Sidebar } from \"@/components/blocks/sidebar\"\nimport type { SidebarItem } from \"@/components/blocks/sidebar\"\nimport { AppShell } from \"@/components/templates/app-shell\"\nimport { Avatar, AvatarFallback, AvatarImage } from \"@/components/ui/avatar\"\nimport {\n  Breadcrumb,\n  BreadcrumbItem,\n  BreadcrumbList,\n  BreadcrumbPage,\n} from \"@/components/ui/breadcrumb\"\nimport { Button } from \"@/components/ui/button\"\nimport { Chip } from \"@/components/ui/chip\"\nimport { IconButton } from \"@/components/ui/icon-button\"\nimport { Input } from \"@/components/ui/input\"\n\nconst COVER = `data:image/svg+xml,${encodeURIComponent(\n  `<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 1200 280\"><rect width=\"1200\" height=\"280\" fill=\"#1a2421\"/><rect x=\"24\" y=\"24\" width=\"1152\" height=\"232\" fill=\"none\" stroke=\"#dfe6e1\" stroke-opacity=\"0.25\"/><path d=\"M0 210 L180 140 L320 190 L480 90 L640 160 L820 70 L1000 150 L1200 110 L1200 280 L0 280 Z\" fill=\"#c45c26\" fill-opacity=\"0.85\"/><path d=\"M0 230 L160 180 L300 210 L460 130 L640 190 L840 110 L1040 180 L1200 150 L1200 280 L0 280 Z\" fill=\"#3d6b5c\" fill-opacity=\"0.55\"/><text x=\"40\" y=\"52\" fill=\"#dfe6e1\" font-family=\"ui-monospace, monospace\" font-size=\"14\">PLOT 08 · FIELD NOTES</text></svg>`\n)}`\n\nfunction navItems(current: string): SidebarItem[] {\n  return [\n    { id: \"profile\", href: \"#profile\", label: \"Profile\", icon: User },\n    {\n      id: \"contributions\",\n      href: \"#contributions\",\n      label: \"Contributions\",\n      icon: Flame,\n    },\n    { id: \"agent-mix\", href: \"#agent-mix\", label: \"Agent mix\", icon: Bot },\n    {\n      id: \"token-history\",\n      href: \"#token-history\",\n      label: \"Token history\",\n      icon: BarChart3,\n    },\n    {\n      id: \"highlights\",\n      href: \"#highlights\",\n      label: \"Highlights\",\n      icon: Sparkles,\n    },\n  ].map((item) => ({ ...item, current: item.id === current }))\n}\n\nconst AGENT_SERIES = {\n  weekly: [\n    { label: \"Mon\", value: 4 },\n    { label: \"Tue\", value: 6 },\n    { label: \"Wed\", value: 5 },\n    { label: \"Thu\", value: 8 },\n    { label: \"Fri\", value: 7 },\n    { label: \"Sat\", value: 1 },\n    { label: \"Sun\", value: 1 },\n  ],\n  monthly: [\n    { label: \"W1\", value: 6 },\n    { label: \"W2\", value: 8 },\n    { label: \"W3\", value: 9 },\n    { label: \"W4\", value: 9 },\n  ],\n  yearly: [\n    { label: \"Jan\", value: 18 },\n    { label: \"Feb\", value: 21 },\n    { label: \"Mar\", value: 24 },\n    { label: \"Apr\", value: 22 },\n    { label: \"May\", value: 28 },\n    { label: \"Jun\", value: 32 },\n    { label: \"Jul\", value: 30 },\n    { label: \"Aug\", value: 27 },\n    { label: \"Sep\", value: 29 },\n    { label: \"Oct\", value: 31 },\n    { label: \"Nov\", value: 34 },\n    { label: \"Dec\", value: 36 },\n  ],\n}\n\nconst TOKEN_RANGES = [\n  {\n    id: \"weekly\",\n    label: \"Weekly\",\n    headline: 667.7,\n    delta: 9.4,\n    data: [\n      { label: \"14\", value: 82 },\n      { label: \"15\", value: 90 },\n      { label: \"16\", value: 88 },\n      { label: \"17\", value: 96 },\n      { label: \"18\", value: 102 },\n      { label: \"19\", value: 99 },\n      { label: \"Today\", value: 110 },\n    ],\n  },\n  {\n    id: \"monthly\",\n    label: \"Monthly\",\n    headline: 2140,\n    delta: 6.2,\n    data: [\n      { label: \"W1\", value: 480 },\n      { label: \"W2\", value: 520 },\n      { label: \"W3\", value: 545 },\n      { label: \"W4\", value: 595 },\n    ],\n  },\n  {\n    id: \"yearly\",\n    label: \"Yearly\",\n    headline: 18420,\n    delta: 14.1,\n    data: [\n      { label: \"Jan\", value: 1100 },\n      { label: \"Feb\", value: 1240 },\n      { label: \"Mar\", value: 1380 },\n      { label: \"Apr\", value: 1420 },\n      { label: \"May\", value: 1510 },\n      { label: \"Jun\", value: 1600 },\n      { label: \"Jul\", value: 1580 },\n      { label: \"Aug\", value: 1670 },\n    ],\n  },\n]\n\nexport function AiProfileTemplate({\n  profile = {\n    name: \"Niko Alvarez\",\n    handle: \"@northloop\",\n    planLabel: \"PRO\",\n    coverSrc: COVER,\n  },\n  contributions,\n  agents = AGENT_SERIES.monthly,\n  tokens,\n}: {\n  profile?: {\n    name: string\n    handle: string\n    avatarSrc?: string\n    coverSrc?: string\n    planLabel?: string\n  }\n  contributions?: {\n    headline?: number\n    delta?: number\n    stats?: { label: string; value: string }[]\n  }\n  agents?: { label: string; value: number }[]\n  tokens?: { label: string; value: number }[]\n} = {}) {\n  const [mobileOpen, setMobileOpen] = useState(false)\n  const [collapsed, setCollapsed] = useState(false)\n  const [shareLabel, setShareLabel] = useState(\"Share\")\n  const [editing, setEditing] = useState(false)\n  const [activeSection, setActiveSection] = useState(\"profile\")\n  const [displayName, setDisplayName] = useState(profile.name)\n  const [displayHandle, setDisplayHandle] = useState(profile.handle)\n\n  return (\n    <AppShell>\n      <Sidebar\n        variant=\"dashboard\"\n        collapsed={collapsed}\n        onCollapsedChange={setCollapsed}\n        mobileOpen={mobileOpen}\n        onMobileOpenChange={setMobileOpen}\n        items={navItems(activeSection)}\n        onNavigate={(id) => {\n          setActiveSection(id)\n          setMobileOpen(false)\n          window.requestAnimationFrame(() =>\n            document.getElementById(id)?.scrollIntoView({ block: \"start\" })\n          )\n        }}\n        user={{\n          name: displayName,\n          email: \"niko@northloop.dev\",\n          initials: \"NA\",\n          avatarSrc: profile.avatarSrc,\n        }}\n        team={{\n          name: \"Northloop Research\",\n          email: \"team@northloop.dev\",\n          initials: \"NR\",\n        }}\n      />\n      <div className=\"flex min-w-0 flex-1 flex-col\">\n        <header className=\"flex items-center gap-2 border-b border-border px-3 py-2\">\n          <IconButton\n            icon={Menu}\n            size=\"small\"\n            variant=\"ghost\"\n            aria-label=\"Open menu\"\n            className=\"md:hidden\"\n            onClick={() => setMobileOpen(true)}\n          />\n          <Breadcrumb>\n            <BreadcrumbList>\n              <BreadcrumbItem>\n                <BreadcrumbPage>Contributor profile</BreadcrumbPage>\n              </BreadcrumbItem>\n            </BreadcrumbList>\n          </Breadcrumb>\n        </header>\n        <main id=\"profile\" className=\"flex-1 overflow-auto\">\n          <div className=\"relative h-36 w-full overflow-hidden sm:h-44\">\n            <img\n              src={profile.coverSrc ?? COVER}\n              alt=\"\"\n              className=\"size-full object-cover\"\n            />\n          </div>\n          <div className=\"px-4 pb-4\">\n            <div className=\"flex flex-wrap items-end justify-between gap-3\">\n              <div className=\"flex min-w-0 basis-full items-end gap-3 sm:basis-auto\">\n                <Avatar className=\"-mt-8 size-16 shrink-0 ring-2 ring-background\">\n                  {profile.avatarSrc ? (\n                    <AvatarImage src={profile.avatarSrc} alt=\"\" />\n                  ) : null}\n                  <AvatarFallback className=\"text-lg\">NA</AvatarFallback>\n                </Avatar>\n                <div className=\"min-w-0\">\n                  <h1 className=\"font-heading text-xl font-semibold tracking-tight\">\n                    {displayName}\n                  </h1>\n                  <p className=\"flex min-w-0 flex-wrap items-center gap-2 font-mono text-[11px] text-muted-foreground\">\n                    {displayHandle}\n                    {profile.planLabel ? (\n                      <Chip variant=\"signal\">{profile.planLabel}</Chip>\n                    ) : null}\n                  </p>\n                </div>\n              </div>\n              <div className=\"flex w-full gap-2 sm:w-auto\">\n                <Button\n                  type=\"button\"\n                  size=\"sm\"\n                  variant=\"outline\"\n                  aria-live=\"polite\"\n                  onClick={async () => {\n                    try {\n                      await navigator.clipboard.writeText(window.location.href)\n                      setShareLabel(\"Link copied\")\n                    } catch {\n                      setShareLabel(\"Copy failed\")\n                    }\n                    window.setTimeout(() => setShareLabel(\"Share\"), 1600)\n                  }}\n                >\n                  {shareLabel}\n                </Button>\n                <Button\n                  type=\"button\"\n                  size=\"sm\"\n                  variant={editing ? \"secondary\" : \"outline\"}\n                  aria-pressed={editing}\n                  onClick={() => setEditing((open) => !open)}\n                >\n                  {editing ? \"Done\" : \"Edit\"}\n                </Button>\n              </div>\n            </div>\n            {editing ? (\n              <div className=\"mt-3 grid gap-2 rounded-[2px] border border-border bg-card p-3 sm:grid-cols-2\">\n                <Input\n                  value={displayName}\n                  aria-label=\"Display name\"\n                  onChange={(event) => setDisplayName(event.target.value)}\n                />\n                <Input\n                  value={displayHandle}\n                  aria-label=\"Profile handle\"\n                  onChange={(event) => setDisplayHandle(event.target.value)}\n                />\n              </div>\n            ) : null}\n            <div className=\"mt-4 grid grid-cols-1 gap-3 xl:grid-cols-2\">\n              <div id=\"contributions\" className=\"scroll-mt-3\">\n                <ContributionsCard\n                  title=\"Contributions this year\"\n                  headline={contributions?.headline ?? 0}\n                  headlineFormat={(n) => `$${formatNumber(Math.round(n))}`}\n                  delta={contributions?.delta ?? 14.8}\n                  stats={\n                    contributions?.stats ?? [\n                      { label: \"Lifetime tokens\", value: \"9B\" },\n                      { label: \"Peak tokens\", value: \"562.7M\" },\n                      { label: \"Longest task\", value: \"12h 54m\" },\n                      { label: \"Top streak\", value: \"62 days\" },\n                    ]\n                  }\n                />\n              </div>\n              <div id=\"agent-mix\" className=\"scroll-mt-3\">\n                <AgentsChart data={agents} />\n              </div>\n              <div id=\"token-history\" className=\"scroll-mt-3 xl:col-span-2\">\n                <LineChartCard\n                  title=\"Tokens\"\n                  headline={667.7}\n                  delta={9.4}\n                  format={(n) => `${n.toFixed(1)}M`}\n                  data={tokens}\n                  ranges={tokens ? undefined : TOKEN_RANGES}\n                />\n              </div>\n              <div\n                id=\"highlights\"\n                className=\"scroll-mt-3 rounded-[2px] border border-border bg-card p-4 xl:col-span-2\"\n              >\n                <p className=\"font-mono text-[9px] tracking-[0.12em] text-muted-foreground uppercase\">\n                  Highlights\n                </p>\n                <div className=\"mt-2 grid gap-2 text-xs sm:grid-cols-2\">\n                  <p>Shipped on 62 consecutive days.</p>\n                  <p>Fable handled 41% of accepted changes this month.</p>\n                </div>\n              </div>\n            </div>\n          </div>\n        </main>\n      </div>\n    </AppShell>\n  )\n}\n\nfunction AgentsChart({ data }: { data: { label: string; value: number }[] }) {\n  const reduced = usePrefersReducedMotion()\n  const [period, setPeriod] = useState(\"monthly\")\n  const series =\n    period === \"weekly\"\n      ? AGENT_SERIES.weekly\n      : period === \"yearly\"\n        ? AGENT_SERIES.yearly\n        : data\n  const headline = period === \"weekly\" ? 8 : period === \"yearly\" ? 214 : 32\n  const summary = `${headline} agents`\n\n  return (\n    <ChartCard\n      title=\"Agents\"\n      headline={headline}\n      delta={12}\n      summary={summary}\n      periodControl={\n        <ChartPeriodSwitch\n          value={period}\n          onChange={setPeriod}\n          options={[\n            { id: \"weekly\", label: \"Weekly\" },\n            { id: \"monthly\", label: \"Monthly\" },\n            { id: \"yearly\", label: \"Yearly\" },\n          ]}\n        />\n      }\n    >\n      <ChartPlot height={220} summary={summary}>\n        <ResponsiveContainer width=\"100%\" height=\"100%\">\n          <BarChart\n            data={series}\n            margin={{ top: 8, right: 8, left: 0, bottom: 0 }}\n            barCategoryGap=\"22%\"\n          >\n            <CartesianGrid\n              vertical={false}\n              stroke=\"var(--border)\"\n              strokeDasharray=\"1 4\"\n            />\n            <XAxis\n              dataKey=\"label\"\n              tick={axisTick}\n              tickLine={false}\n              axisLine={{ stroke: \"var(--border)\" }}\n            />\n            <YAxis\n              tick={axisTick}\n              tickLine={false}\n              axisLine={false}\n              width={28}\n            />\n            <Tooltip cursor={false} content={<PlotTooltip />} />\n            <Bar\n              dataKey=\"value\"\n              name=\"Agents\"\n              fill=\"var(--chart-3)\"\n              radius={2}\n              maxBarSize={36}\n              isAnimationActive={!reduced}\n              activeBar={{\n                fill: \"var(--chart-3)\",\n                stroke: \"var(--ring)\",\n                strokeWidth: 2,\n                radius: 2,\n              }}\n            />\n          </BarChart>\n        </ResponsiveContainer>\n      </ChartPlot>\n    </ChartCard>\n  )\n}\n",
      "type": "registry:block"
    }
  ],
  "type": "registry:block"
}