{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "context",
  "type": "registry:component",
  "title": "Context",
  "description": "Compact context-window usage with an automatically derived token graph and anchored detail inspector.",
  "dependencies": [
    "lucide-react@^1.27.0"
  ],
  "registryDependencies": [
    "http://127.0.0.1:3000/r/button.json",
    "http://127.0.0.1:3000/r/core.json",
    "http://127.0.0.1:3000/r/popover.json",
    "http://127.0.0.1:3000/r/scroll-area.json"
  ],
  "files": [
    {
      "path": "src/registry/sources/control-ui/context-model.ts",
      "target": "@components/control-ui/context-model.ts",
      "type": "registry:component",
      "content": "import type { ContextSegment, ContextSegmentKind, ContextStatus } from \"@/components/control-ui/contracts\";\n\nexport type ContextModelSegment = {\n  segment: ContextSegment;\n  kind: ContextSegmentKind;\n  tokens: number;\n  start: number;\n  width: number;\n  ratio: number | null;\n};\n\nexport type ContextModel = {\n  segments: readonly ContextModelSegment[];\n  usedTokens: number;\n  maxTokens: number | null;\n  remainingTokens: number | null;\n  overageTokens: number;\n  ratio: number | null;\n  domain: number;\n  limitPosition: number | null;\n  status: ContextStatus;\n};\n\nfunction deriveContextModelSegments(segments: readonly ContextSegment[], domain: number, validLimit: number | null): ContextModelSegment[] {\n  let consumedTokens = 0;\n  return segments.map((segment) => {\n    const tokens = Number.isFinite(segment.tokens) && segment.tokens > 0 ? segment.tokens : 0;\n    const modelSegment = {\n      segment,\n      kind: segment.kind ?? \"other\",\n      tokens,\n      start: (consumedTokens / domain) * 100,\n      width: (tokens / domain) * 100,\n      ratio: validLimit === null ? null : tokens / validLimit,\n    } satisfies ContextModelSegment;\n    consumedTokens += tokens;\n    return modelSegment;\n  });\n}\n\nfunction deriveValidatedUsedTokens(segments: readonly ContextSegment[]): number {\n  const ids = new Set<string>();\n  let usedTokens = 0;\n  for (const segment of segments) {\n    if (ids.has(segment.id)) throw new Error(`Context segment ids must be unique: \"${segment.id}\".`);\n    ids.add(segment.id);\n    usedTokens += Number.isFinite(segment.tokens) && segment.tokens > 0 ? segment.tokens : 0;\n  }\n  return usedTokens;\n}\n\nexport function deriveContextModel(segments: readonly ContextSegment[], maxTokens?: number | null): ContextModel {\n  const usedTokens = deriveValidatedUsedTokens(segments);\n\n  const validLimit = Number.isFinite(maxTokens) && (maxTokens ?? 0) > 0 ? (maxTokens ?? null) : null;\n  const domain = Math.max(validLimit ?? 0, usedTokens, 1);\n  const remainingTokens = validLimit === null ? null : Math.max(0, validLimit - usedTokens);\n  const overageTokens = validLimit === null ? 0 : Math.max(0, usedTokens - validLimit);\n  const ratio = validLimit === null ? null : usedTokens / validLimit;\n  const limitPosition = validLimit === null ? null : (validLimit / domain) * 100;\n  let status: ContextStatus = \"normal\";\n  if (validLimit === null) status = \"unavailable\";\n  else if (overageTokens > 0) status = \"over-limit\";\n  const modelSegments = deriveContextModelSegments(segments, domain, validLimit);\n\n  return {\n    segments: modelSegments,\n    usedTokens,\n    maxTokens: validLimit,\n    remainingTokens,\n    overageTokens,\n    ratio,\n    domain,\n    limitPosition,\n    status,\n  } satisfies ContextModel;\n}\n"
    },
    {
      "path": "src/registry/sources/control-ui/context.tsx",
      "target": "@components/control-ui/context.tsx",
      "type": "registry:component",
      "content": "\"use client\";\n\nimport { X } from \"lucide-react\";\nimport type { ComponentProps, ReactNode } from \"react\";\nimport { createContext, useContext } from \"react\";\n\nimport type { ContextProps, ContextSegmentKind } from \"@/components/control-ui/contracts\";\nimport { cn } from \"@/components/control-ui/lib/cn\";\nimport { skinSlot } from \"@/components/control-ui/skin\";\nimport { Button } from \"@/components/control-ui/ui/button\";\nimport {\n  Popover,\n  PopoverClose,\n  PopoverContent,\n  PopoverDescription,\n  PopoverTitle,\n  PopoverTrigger,\n} from \"@/components/control-ui/ui/popover\";\nimport { ScrollArea } from \"@/components/control-ui/ui/scroll-area\";\nimport type { ContextModel } from \"./context-model\";\nimport { deriveContextModel } from \"./context-model\";\n\ntype ContextValue = {\n  model: ContextModel;\n  modelName: string | undefined;\n  numberFormatter: Intl.NumberFormat;\n  percentageFormatter: Intl.NumberFormat;\n};\n\nconst ContextValueContext = createContext<ContextValue | null>(null);\n\nconst kindClasses = {\n  system: { graph: \"fill-foreground/75\", indicator: \"bg-foreground/75\" },\n  tool: { graph: \"fill-primary\", indicator: \"bg-primary\" },\n  message: { graph: \"fill-primary/65\", indicator: \"bg-primary/65\" },\n  source: { graph: \"fill-accent-foreground/65\", indicator: \"bg-accent-foreground/65\" },\n  reasoning: { graph: \"fill-foreground/50\", indicator: \"bg-foreground/50\" },\n  cache: { graph: \"fill-muted-foreground/55\", indicator: \"bg-muted-foreground/55\" },\n  other: { graph: \"fill-border\", indicator: \"bg-border\" },\n} satisfies Record<ContextSegmentKind, { graph: string; indicator: string }>;\n\nfunction useContextValue(): ContextValue {\n  const context = useContext(ContextValueContext);\n  if (!context) throw new Error(\"Context parts must be rendered inside <Context>.\");\n  return context;\n}\n\nexport function Context({\n  segments,\n  maxTokens,\n  model: modelName,\n  locale,\n  open,\n  defaultOpen,\n  onOpenChange,\n  className,\n  children,\n  ...props\n}: ContextProps) {\n  const model = deriveContextModel(segments, maxTokens);\n  const context = {\n    model,\n    modelName,\n    numberFormatter: new Intl.NumberFormat(locale),\n    percentageFormatter: new Intl.NumberFormat(locale, {\n      style: \"percent\",\n      maximumFractionDigits: 1,\n    }),\n  } satisfies ContextValue;\n\n  return (\n    <ContextValueContext.Provider value={context}>\n      <div\n        {...props}\n        data-control-ui=\"context\"\n        data-slot=\"root\"\n        data-status={model.status}\n        className={cn(\"inline-flex\", skinSlot(\"context\", \"root\", { status: model.status }), className)}\n      >\n        <Popover open={open} defaultOpen={defaultOpen} onOpenChange={onOpenChange}>\n          {children ?? (\n            <>\n              <ContextTrigger />\n              <ContextContent />\n            </>\n          )}\n        </Popover>\n      </div>\n    </ContextValueContext.Provider>\n  );\n}\n\nexport type ContextTriggerProps = ComponentProps<typeof Button>;\n\nexport function ContextTrigger({\n  \"aria-label\": ariaLabel,\n  variant = \"surface\",\n  size = \"sm\",\n  className,\n  children,\n  ...props\n}: ContextTriggerProps) {\n  const { model, numberFormatter, percentageFormatter } = useContextValue();\n  const percentage = model.ratio === null ? null : percentageFormatter.format(model.ratio);\n  const visualPercentage = Math.min(100, Math.max(0, (model.ratio ?? 0) * 100));\n  const shortLabel = percentage === null ? \"Context unavailable\" : `${percentage} context`;\n  const accessibleLabel =\n    model.maxTokens === null\n      ? `Context window: ${numberFormatter.format(model.usedTokens)} tokens used; limit unavailable`\n      : `Context window: ${numberFormatter.format(model.usedTokens)} of ${numberFormatter.format(model.maxTokens)} tokens used (${percentage})${\n          model.overageTokens > 0 ? `; ${numberFormatter.format(model.overageTokens)} tokens over limit` : \"\"\n        }`;\n\n  return (\n    <span\n      data-control-ui=\"context\"\n      data-slot=\"trigger\"\n      data-status={model.status}\n      className={cn(\"relative inline-flex\", skinSlot(\"context\", \"trigger\", { status: model.status }))}\n    >\n      <Button\n        {...props}\n        render={<PopoverTrigger />}\n        variant={variant}\n        size={size}\n        aria-label={ariaLabel ?? accessibleLabel}\n        className={cn(\"group/context after:absolute after:-inset-1.5 after:content-['']\", className)}\n      >\n        {children ?? (\n          <>\n            <svg\n              data-control-ui=\"context\"\n              data-slot=\"trigger-indicator\"\n              data-status={model.status}\n              viewBox=\"0 0 16 16\"\n              aria-hidden=\"true\"\n              className={cn(\"size-4 shrink-0 -rotate-90 fill-none\", skinSlot(\"context\", \"trigger-indicator\", { status: model.status }))}\n            >\n              <circle\n                cx=\"8\"\n                cy=\"8\"\n                r=\"6.25\"\n                strokeWidth=\"1.5\"\n                className={model.status === \"unavailable\" ? \"stroke-muted\" : \"stroke-border/70\"}\n              />\n              {model.status !== \"unavailable\" ? (\n                <circle\n                  cx=\"8\"\n                  cy=\"8\"\n                  r=\"6.25\"\n                  pathLength=\"100\"\n                  strokeDasharray={`${visualPercentage} 100`}\n                  strokeLinecap=\"round\"\n                  strokeWidth=\"1.5\"\n                  className={model.status === \"over-limit\" ? \"stroke-destructive-text\" : \"stroke-primary\"}\n                />\n              ) : null}\n            </svg>\n            <span\n              data-control-ui=\"context\"\n              data-slot=\"trigger-label\"\n              className={cn(\n                \"max-w-0 overflow-hidden opacity-0 transition-[max-width,opacity] duration-[var(--duration-fast)] ease-[var(--ease-standard)] group-hover/context:max-w-32 group-hover/context:opacity-100 group-focus-visible/context:max-w-32 group-focus-visible/context:opacity-100 group-data-[popup-open]/context:max-w-32 group-data-[popup-open]/context:opacity-100\",\n                skinSlot(\"context\", \"trigger-label\", {}),\n              )}\n            >\n              {shortLabel}\n            </span>\n          </>\n        )}\n      </Button>\n    </span>\n  );\n}\n\nexport type ContextContentProps = ComponentProps<typeof PopoverContent>;\n\nexport function ContextContent({\n  \"aria-label\": ariaLabel,\n  side = \"top\",\n  align = \"end\",\n  sideOffset = 8,\n  collisionPadding = 16,\n  padding = \"none\",\n  className,\n  children,\n  ...props\n}: ContextContentProps) {\n  const { modelName } = useContextValue();\n\n  return (\n    <PopoverContent\n      {...props}\n      // default panel carries no title, so popup needs name of its own\n      aria-label={ariaLabel ?? (modelName ? `Context window · ${modelName}` : \"Context window\")}\n      side={side}\n      align={align}\n      sideOffset={sideOffset}\n      collisionPadding={collisionPadding}\n      padding={padding}\n      className={cn(\"w-[min(28rem,calc(100vw-2rem))] overflow-hidden\", className)}\n    >\n      <div data-control-ui=\"context\" data-slot=\"content\" className={cn(\"overflow-hidden\", skinSlot(\"context\", \"content\", {}))}>\n        {children ?? (\n          <ScrollArea maxHeight=\"min(36rem, calc(100dvh - 8rem))\" lockAxis=\"x\">\n            <div className=\"grid gap-4 p-4\">\n              <ContextSummary />\n              <ContextGraph />\n              <ContextLegend />\n            </div>\n          </ScrollArea>\n        )}\n      </div>\n    </PopoverContent>\n  );\n}\n\nexport type ContextHeaderProps = ComponentProps<\"div\">;\n\nexport function ContextHeader({ className, children, ...props }: ContextHeaderProps) {\n  return (\n    <div\n      {...props}\n      data-control-ui=\"context\"\n      data-slot=\"header\"\n      className={cn(\"flex items-start gap-3 p-4 pb-0\", skinSlot(\"context\", \"header\", {}), className)}\n    >\n      {children ?? (\n        <>\n          <div className=\"min-w-0 flex-1\">\n            <ContextTitle />\n            <ContextDescription />\n          </div>\n          <ContextClose />\n        </>\n      )}\n    </div>\n  );\n}\n\nexport type ContextTitleProps = ComponentProps<typeof PopoverTitle>;\n\nexport function ContextTitle({ className, children, ...props }: ContextTitleProps) {\n  return (\n    <PopoverTitle {...props} className={cn(\"text-sm font-medium\", className)}>\n      {children ?? \"Context window\"}\n    </PopoverTitle>\n  );\n}\n\nexport type ContextDescriptionProps = ComponentProps<typeof PopoverDescription>;\n\nexport function ContextDescription({ className, children, ...props }: ContextDescriptionProps) {\n  const { modelName } = useContextValue();\n  return (\n    <PopoverDescription {...props} className={cn(\"text-caption text-muted-foreground\", className)}>\n      {children ?? modelName ?? \"Token usage by context segment\"}\n    </PopoverDescription>\n  );\n}\n\nexport type ContextSummaryProps = ComponentProps<\"div\">;\n\nexport function ContextSummary({ className, children, ...props }: ContextSummaryProps) {\n  const { model, numberFormatter, percentageFormatter } = useContextValue();\n  const percentage = model.ratio === null ? null : percentageFormatter.format(model.ratio);\n\n  return (\n    <div\n      {...props}\n      data-control-ui=\"context\"\n      data-slot=\"summary\"\n      data-status={model.status}\n      className={cn(\n        \"flex items-baseline justify-between gap-3 text-sm\",\n        skinSlot(\"context\", \"summary\", { status: model.status }),\n        className,\n      )}\n    >\n      {children ?? (\n        <>\n          <span>{percentage === null ? `${numberFormatter.format(model.usedTokens)} tokens used` : `${percentage} used`}</span>\n          <span className=\"tabular-nums text-muted-foreground\">\n            {model.maxTokens === null\n              ? \"Limit unavailable\"\n              : `${numberFormatter.format(model.usedTokens)} / ${numberFormatter.format(model.maxTokens)} tokens`}\n          </span>\n        </>\n      )}\n    </div>\n  );\n}\n\nexport type ContextGraphProps = Omit<ComponentProps<\"svg\">, \"children\">;\n\nexport function ContextGraph({ className, ...props }: ContextGraphProps) {\n  const { model } = useContextValue();\n\n  return (\n    <svg\n      {...props}\n      data-control-ui=\"context\"\n      data-slot=\"graph\"\n      data-status={model.status}\n      viewBox=\"0 0 100 10\"\n      preserveAspectRatio=\"none\"\n      aria-hidden=\"true\"\n      className={cn(\"h-2.5 w-full overflow-hidden rounded-full\", skinSlot(\"context\", \"graph\", { status: model.status }), className)}\n    >\n      <rect\n        data-control-ui=\"context\"\n        data-slot=\"track\"\n        x=\"0\"\n        y=\"0\"\n        width=\"100\"\n        height=\"10\"\n        rx=\"5\"\n        className={cn(\"fill-muted\", skinSlot(\"context\", \"track\", {}))}\n      />\n      {model.segments.map((segment) => (\n        <rect\n          key={segment.segment.id}\n          data-control-ui=\"context\"\n          data-slot=\"segment\"\n          data-kind={segment.kind}\n          x={segment.start}\n          y=\"0\"\n          width={segment.width}\n          height=\"10\"\n          className={cn(kindClasses[segment.kind].graph, skinSlot(\"context\", \"segment\", { kind: segment.kind }))}\n        />\n      ))}\n      {model.status === \"over-limit\" && model.limitPosition !== null ? (\n        <>\n          <rect x={model.limitPosition} y=\"0\" width={100 - model.limitPosition} height=\"10\" className=\"fill-destructive-text/22\" />\n          <line\n            data-control-ui=\"context\"\n            data-slot=\"limit-marker\"\n            x1={model.limitPosition}\n            x2={model.limitPosition}\n            y1=\"0\"\n            y2=\"10\"\n            vectorEffect=\"non-scaling-stroke\"\n            className={cn(\"stroke-destructive-text\", skinSlot(\"context\", \"limit-marker\", {}))}\n          />\n        </>\n      ) : null}\n    </svg>\n  );\n}\n\nexport type ContextLegendProps = ComponentProps<\"ul\">;\n\ntype ContextLegendRowProps = {\n  kind: ContextSegmentKind;\n  label: ReactNode;\n  description?: ReactNode;\n  value?: ReactNode;\n  indicatorClassName: string;\n};\n\nfunction ContextLegendRow({ kind, label, description, value, indicatorClassName }: ContextLegendRowProps) {\n  return (\n    <li\n      data-control-ui=\"context\"\n      data-slot=\"legend-item\"\n      data-kind={kind}\n      className={cn(\"grid grid-cols-[minmax(0,1fr)_auto] items-start gap-3 py-1.5\", skinSlot(\"context\", \"legend-item\", { kind }))}\n    >\n      <div className=\"flex min-w-0 items-start gap-2\">\n        <span\n          data-control-ui=\"context\"\n          data-slot=\"legend-indicator\"\n          data-kind={kind}\n          aria-hidden=\"true\"\n          className={cn(\"mt-1 size-2.5 shrink-0 rounded-full\", indicatorClassName, skinSlot(\"context\", \"legend-indicator\", { kind }))}\n        />\n        <div className=\"min-w-0\">\n          <div className=\"text-sm\">{label}</div>\n          {description !== undefined && description !== null ? (\n            <div className=\"text-caption text-muted-foreground\">{description}</div>\n          ) : null}\n        </div>\n      </div>\n      {value !== undefined && value !== null ? (\n        <span\n          data-control-ui=\"context\"\n          data-slot=\"legend-value\"\n          className={cn(\"text-caption tabular-nums text-muted-foreground\", skinSlot(\"context\", \"legend-value\", {}))}\n        >\n          {value}\n        </span>\n      ) : null}\n    </li>\n  );\n}\n\nexport function ContextLegend({ className, children, ...props }: ContextLegendProps) {\n  const { model, numberFormatter, percentageFormatter } = useContextValue();\n\n  return (\n    <ul {...props} data-control-ui=\"context\" data-slot=\"legend\" className={cn(\"grid\", skinSlot(\"context\", \"legend\", {}), className)}>\n      {children ?? (\n        <>\n          {model.segments.map((segment) => (\n            <ContextLegendRow\n              key={segment.segment.id}\n              kind={segment.kind}\n              label={segment.segment.label}\n              description={segment.segment.description}\n              indicatorClassName={kindClasses[segment.kind].indicator}\n              value={`${numberFormatter.format(segment.tokens)} tokens${\n                segment.ratio === null ? \"\" : ` · ${percentageFormatter.format(segment.ratio)}`\n              }`}\n            />\n          ))}\n          {model.status === \"normal\" && model.remainingTokens !== null ? (\n            <ContextLegendRow\n              kind=\"other\"\n              label=\"Available\"\n              indicatorClassName=\"bg-muted\"\n              value={`${numberFormatter.format(model.remainingTokens)} tokens · ${percentageFormatter.format(\n                model.remainingTokens / (model.maxTokens ?? 1),\n              )}`}\n            />\n          ) : null}\n          {model.status === \"over-limit\" ? (\n            <ContextLegendRow\n              kind=\"other\"\n              label=\"Over limit\"\n              indicatorClassName=\"bg-destructive-text\"\n              value={`${numberFormatter.format(model.overageTokens)} tokens over limit`}\n            />\n          ) : null}\n          {model.status === \"unavailable\" ? (\n            <ContextLegendRow kind=\"other\" label=\"Limit unavailable\" indicatorClassName=\"bg-muted\" />\n          ) : null}\n        </>\n      )}\n    </ul>\n  );\n}\n\nexport type ContextCloseProps = ComponentProps<typeof Button>;\n\nexport function ContextClose({\n  variant = \"ghost\",\n  size = \"xs\",\n  iconOnly = true,\n  \"aria-label\": ariaLabel = \"Close context window\",\n  className,\n  children,\n  ...props\n}: ContextCloseProps) {\n  return (\n    <Button\n      {...props}\n      render={<PopoverClose />}\n      variant={variant}\n      size={size}\n      iconOnly={iconOnly}\n      aria-label={ariaLabel}\n      className={cn(\"ml-auto\", className)}\n    >\n      {children ?? <X aria-hidden=\"true\" className=\"size-4\" />}\n    </Button>\n  );\n}\n"
    }
  ],
  "meta": {}
}
