{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "ai-context",
  "title": "AI: Context",
  "description": "A token-usage and context-window meter with a hover breakdown of input, output and cost.",
  "dependencies": [
    "tokenlens@^1.3.1"
  ],
  "registryDependencies": [
    "@nx-ui/utils",
    "@nx-ui/button",
    "@nx-ui/hover-card",
    "@nx-ui/progress",
    "@nx-ui/ai-types"
  ],
  "files": [
    {
      "path": "components/ai/context.tsx",
      "content": "\"use client\"\n\nimport { type ComponentProps, createContext, useContext } from \"react\"\nimport { getUsage } from \"tokenlens\"\n\nimport { Button } from \"@/components/ui/button\"\nimport {\n  HoverCard,\n  HoverCardContent,\n  HoverCardTrigger,\n} from \"@/components/ui/hover-card\"\nimport { Progress } from \"@/components/ui/progress\"\nimport { cn } from \"@/lib/utils\"\n\nimport type { LanguageModelUsage } from \"./types\"\n\nconst PERCENT_MAX = 100\nconst ICON_RADIUS = 10\nconst ICON_VIEWBOX = 24\nconst ICON_CENTER = 12\nconst ICON_STROKE_WIDTH = 2\n\ntype ModelId = string\n\ntype ContextSchema = {\n  usedTokens: number\n  maxTokens: number\n  usage?: LanguageModelUsage\n  modelId?: ModelId\n}\n\nconst ContextContext = createContext<ContextSchema | null>(null)\n\nconst useContextValue = () => {\n  const context = useContext(ContextContext)\n\n  if (!context) {\n    throw new Error(\"Context components must be used within Context\")\n  }\n\n  return context\n}\n\nexport type ContextProps = ComponentProps<typeof HoverCard> & ContextSchema\n\nexport const Context = ({\n  usedTokens,\n  maxTokens,\n  usage,\n  modelId,\n  ...props\n}: ContextProps) => (\n  <ContextContext.Provider\n    value={{\n      usedTokens,\n      maxTokens,\n      usage,\n      modelId,\n    }}\n  >\n    <HoverCard {...props} />\n  </ContextContext.Provider>\n)\n\nconst ContextIcon = () => {\n  const { usedTokens, maxTokens } = useContextValue()\n  const circumference = 2 * Math.PI * ICON_RADIUS\n  const usedPercent = usedTokens / maxTokens\n  const dashOffset = circumference * (1 - usedPercent)\n\n  return (\n    <svg\n      aria-label=\"Model context usage\"\n      height=\"20\"\n      role=\"img\"\n      style={{ color: \"currentcolor\" }}\n      viewBox={`0 0 ${ICON_VIEWBOX} ${ICON_VIEWBOX}`}\n      width=\"20\"\n    >\n      <circle\n        cx={ICON_CENTER}\n        cy={ICON_CENTER}\n        fill=\"none\"\n        opacity=\"0.25\"\n        r={ICON_RADIUS}\n        stroke=\"currentColor\"\n        strokeWidth={ICON_STROKE_WIDTH}\n      />\n      <circle\n        cx={ICON_CENTER}\n        cy={ICON_CENTER}\n        fill=\"none\"\n        opacity=\"0.7\"\n        r={ICON_RADIUS}\n        stroke=\"currentColor\"\n        strokeDasharray={`${circumference} ${circumference}`}\n        strokeDashoffset={dashOffset}\n        strokeLinecap=\"round\"\n        strokeWidth={ICON_STROKE_WIDTH}\n        style={{ transformOrigin: \"center\", transform: \"rotate(-90deg)\" }}\n      />\n    </svg>\n  )\n}\n\nexport type ContextTriggerProps = ComponentProps<typeof Button>\n\nexport const ContextTrigger = ({ children, ...props }: ContextTriggerProps) => {\n  const { usedTokens, maxTokens } = useContextValue()\n  const usedPercent = usedTokens / maxTokens\n  const renderedPercent = new Intl.NumberFormat(\"en-US\", {\n    style: \"percent\",\n    maximumFractionDigits: 1,\n  }).format(usedPercent)\n\n  return (\n    <HoverCardTrigger\n      render={\n        children ? undefined : (\n          <Button type=\"button\" variant=\"ghost\" {...props}>\n            <span className=\"font-medium text-muted-foreground\">\n              {renderedPercent}\n            </span>\n            <ContextIcon />\n          </Button>\n        )\n      }\n    >\n      {children}\n    </HoverCardTrigger>\n  )\n}\n\nexport type ContextContentProps = ComponentProps<typeof HoverCardContent>\n\nexport const ContextContent = ({ className, ...props }: ContextContentProps) => (\n  <HoverCardContent\n    className={cn(\"min-w-60 divide-y overflow-hidden p-0\", className)}\n    {...props}\n  />\n)\n\nexport type ContextContentHeaderProps = ComponentProps<\"div\">\n\nexport const ContextContentHeader = ({\n  children,\n  className,\n  ...props\n}: ContextContentHeaderProps) => {\n  const { usedTokens, maxTokens } = useContextValue()\n  const usedPercent = usedTokens / maxTokens\n  const displayPct = new Intl.NumberFormat(\"en-US\", {\n    style: \"percent\",\n    maximumFractionDigits: 1,\n  }).format(usedPercent)\n  const used = new Intl.NumberFormat(\"en-US\", {\n    notation: \"compact\",\n  }).format(usedTokens)\n  const total = new Intl.NumberFormat(\"en-US\", {\n    notation: \"compact\",\n  }).format(maxTokens)\n\n  return (\n    <div className={cn(\"w-full space-y-2 p-3\", className)} {...props}>\n      {children ?? (\n        <>\n          <div className=\"flex items-center justify-between gap-3 text-xs\">\n            <p>{displayPct}</p>\n            <p className=\"font-mono text-muted-foreground\">\n              {used} / {total}\n            </p>\n          </div>\n          <div className=\"space-y-2\">\n            <Progress className=\"bg-muted\" value={usedPercent * PERCENT_MAX} />\n          </div>\n        </>\n      )}\n    </div>\n  )\n}\n\nexport type ContextContentBodyProps = ComponentProps<\"div\">\n\nexport const ContextContentBody = ({\n  children,\n  className,\n  ...props\n}: ContextContentBodyProps) => (\n  <div className={cn(\"w-full p-3\", className)} {...props}>\n    {children}\n  </div>\n)\n\nexport type ContextContentFooterProps = ComponentProps<\"div\">\n\nexport const ContextContentFooter = ({\n  children,\n  className,\n  ...props\n}: ContextContentFooterProps) => {\n  const { modelId, usage } = useContextValue()\n  const costUSD = modelId\n    ? getUsage({\n        modelId,\n        usage: {\n          input: usage?.inputTokens ?? 0,\n          output: usage?.outputTokens ?? 0,\n        },\n      }).costUSD?.totalUSD\n    : undefined\n  const totalCost = new Intl.NumberFormat(\"en-US\", {\n    style: \"currency\",\n    currency: \"USD\",\n  }).format(costUSD ?? 0)\n\n  return (\n    <div\n      className={cn(\n        \"flex w-full items-center justify-between gap-3 bg-secondary p-3 text-xs\",\n        className\n      )}\n      {...props}\n    >\n      {children ?? (\n        <>\n          <span className=\"text-muted-foreground\">Total cost</span>\n          <span>{totalCost}</span>\n        </>\n      )}\n    </div>\n  )\n}\n\nexport type ContextInputUsageProps = ComponentProps<\"div\">\n\nexport const ContextInputUsage = ({\n  className,\n  children,\n  ...props\n}: ContextInputUsageProps) => {\n  const { usage, modelId } = useContextValue()\n  const inputTokens = usage?.inputTokens ?? 0\n\n  if (children) {\n    return children\n  }\n\n  if (!inputTokens) {\n    return null\n  }\n\n  const inputCost = modelId\n    ? getUsage({\n        modelId,\n        usage: { input: inputTokens, output: 0 },\n      }).costUSD?.totalUSD\n    : undefined\n  const inputCostText = new Intl.NumberFormat(\"en-US\", {\n    style: \"currency\",\n    currency: \"USD\",\n  }).format(inputCost ?? 0)\n\n  return (\n    <div\n      className={cn(\"flex items-center justify-between text-xs\", className)}\n      {...props}\n    >\n      <span className=\"text-muted-foreground\">Input</span>\n      <TokensWithCost costText={inputCostText} tokens={inputTokens} />\n    </div>\n  )\n}\n\nexport type ContextOutputUsageProps = ComponentProps<\"div\">\n\nexport const ContextOutputUsage = ({\n  className,\n  children,\n  ...props\n}: ContextOutputUsageProps) => {\n  const { usage, modelId } = useContextValue()\n  const outputTokens = usage?.outputTokens ?? 0\n\n  if (children) {\n    return children\n  }\n\n  if (!outputTokens) {\n    return null\n  }\n\n  const outputCost = modelId\n    ? getUsage({\n        modelId,\n        usage: { input: 0, output: outputTokens },\n      }).costUSD?.totalUSD\n    : undefined\n  const outputCostText = new Intl.NumberFormat(\"en-US\", {\n    style: \"currency\",\n    currency: \"USD\",\n  }).format(outputCost ?? 0)\n\n  return (\n    <div\n      className={cn(\"flex items-center justify-between text-xs\", className)}\n      {...props}\n    >\n      <span className=\"text-muted-foreground\">Output</span>\n      <TokensWithCost costText={outputCostText} tokens={outputTokens} />\n    </div>\n  )\n}\n\nexport type ContextReasoningUsageProps = ComponentProps<\"div\">\n\nexport const ContextReasoningUsage = ({\n  className,\n  children,\n  ...props\n}: ContextReasoningUsageProps) => {\n  const { usage, modelId } = useContextValue()\n  const reasoningTokens = usage?.reasoningTokens ?? 0\n\n  if (children) {\n    return children\n  }\n\n  if (!reasoningTokens) {\n    return null\n  }\n\n  const reasoningCost = modelId\n    ? getUsage({\n        modelId,\n        usage: { reasoningTokens },\n      }).costUSD?.totalUSD\n    : undefined\n  const reasoningCostText = new Intl.NumberFormat(\"en-US\", {\n    style: \"currency\",\n    currency: \"USD\",\n  }).format(reasoningCost ?? 0)\n\n  return (\n    <div\n      className={cn(\"flex items-center justify-between text-xs\", className)}\n      {...props}\n    >\n      <span className=\"text-muted-foreground\">Reasoning</span>\n      <TokensWithCost costText={reasoningCostText} tokens={reasoningTokens} />\n    </div>\n  )\n}\n\nexport type ContextCacheUsageProps = ComponentProps<\"div\">\n\nexport const ContextCacheUsage = ({\n  className,\n  children,\n  ...props\n}: ContextCacheUsageProps) => {\n  const { usage, modelId } = useContextValue()\n  const cacheTokens = usage?.cachedInputTokens ?? 0\n\n  if (children) {\n    return children\n  }\n\n  if (!cacheTokens) {\n    return null\n  }\n\n  const cacheCost = modelId\n    ? getUsage({\n        modelId,\n        usage: { cacheReads: cacheTokens, input: 0, output: 0 },\n      }).costUSD?.totalUSD\n    : undefined\n  const cacheCostText = new Intl.NumberFormat(\"en-US\", {\n    style: \"currency\",\n    currency: \"USD\",\n  }).format(cacheCost ?? 0)\n\n  return (\n    <div\n      className={cn(\"flex items-center justify-between text-xs\", className)}\n      {...props}\n    >\n      <span className=\"text-muted-foreground\">Cache</span>\n      <TokensWithCost costText={cacheCostText} tokens={cacheTokens} />\n    </div>\n  )\n}\n\nconst TokensWithCost = ({\n  tokens,\n  costText,\n}: {\n  tokens?: number\n  costText?: string\n}) => (\n  <span>\n    {tokens === undefined\n      ? \"—\"\n      : new Intl.NumberFormat(\"en-US\", {\n          notation: \"compact\",\n        }).format(tokens)}\n    {costText ? (\n      <span className=\"ml-2 text-muted-foreground\">• {costText}</span>\n    ) : null}\n  </span>\n)\n",
      "type": "registry:ui",
      "target": "components/ai/context.tsx"
    }
  ],
  "type": "registry:ui"
}