{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "routing-assignment-panel",
  "type": "registry:component",
  "title": "Routing Assignment Panel",
  "description": "Right-dock panel listing the agent slots an active route dispatches to.",
  "dependencies": [
    "@vllnt/ui@^0.2.1"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/default/routing-assignment-panel/routing-assignment-panel.tsx",
      "content": "\"use client\";\n\nimport {\n  type ComponentPropsWithoutRef,\n  forwardRef,\n  type ReactNode,\n} from \"react\";\n\nimport { cn } from \"@vllnt/ui\";\n\n/**\n * Role of an agent in the routing graph.\n *\n * @public\n */\nexport type RoutingRole = \"fallback\" | \"primary\" | \"shadow\";\n\nconst ROLE_LABEL: Record<RoutingRole, string> = {\n  fallback: \"Fallback\",\n  primary: \"Primary\",\n  shadow: \"Shadow\",\n};\n\nconst ROLE_TONE: Record<RoutingRole, string> = {\n  fallback:\n    \"border-amber-500/40 bg-amber-500/10 text-amber-700 dark:text-amber-300\",\n  primary:\n    \"border-emerald-500/40 bg-emerald-500/10 text-emerald-700 dark:text-emerald-300\",\n  shadow: \"border-border bg-muted/40 text-muted-foreground\",\n};\n\n/**\n * One row in the routing assignment list.\n *\n * @public\n */\nexport type RoutingAssignment = {\n  /** Display name (e.g. `\"researcher\"`, `\"ranker\"`). */\n  agent: ReactNode;\n  /** Stable identifier — used as the React key. */\n  id: string;\n  /** Optional load fraction `0..1` rendered as a thin progress bar. */\n  load?: number;\n  /** Optional click handler — when provided, the row becomes a button. */\n  onActivate?: () => void;\n  /** Role of the agent for this slot. */\n  role: RoutingRole;\n};\n\n/**\n * Localizable strings.\n *\n * @public\n */\nexport type RoutingAssignmentPanelLabels = {\n  /** Empty-state copy. Defaults to `\"No assignments\"`. */\n  empty?: string;\n  /** Aria-label for the panel. Defaults to `\"Routing assignments\"`. */\n  region?: string;\n};\n\nconst DEFAULT_LABELS = {\n  empty: \"No assignments\",\n  region: \"Routing assignments\",\n} as const satisfies Required<RoutingAssignmentPanelLabels>;\n\n/**\n * Props for {@link RoutingAssignmentPanel}.\n *\n * @public\n */\nexport type RoutingAssignmentPanelProps = {\n  /** Assignments in render order. */\n  assignments: RoutingAssignment[];\n  /** Localizable strings. */\n  labels?: RoutingAssignmentPanelLabels;\n  /** Panel title. Defaults to `\"Routing\"`. */\n  title?: ReactNode;\n} & ComponentPropsWithoutRef<\"section\">;\n\nconst clampLoad = (value: number): number => {\n  if (value < 0) {\n    return 0;\n  }\n  if (value > 1) {\n    return 1;\n  }\n  return value;\n};\n\nconst RowBody = (props: {\n  assignment: RoutingAssignment;\n}): React.ReactElement => {\n  const { assignment } = props;\n  const load =\n    assignment.load === undefined ? null : clampLoad(assignment.load);\n  return (\n    <span className=\"flex flex-1 flex-col gap-1\">\n      <span className=\"flex items-center justify-between gap-2\">\n        <span className=\"truncate text-xs text-foreground\">\n          {assignment.agent}\n        </span>\n        <span\n          className={cn(\n            \"rounded-full border px-1.5 py-0.5 text-[10px] uppercase tracking-wide\",\n            ROLE_TONE[assignment.role],\n          )}\n        >\n          {ROLE_LABEL[assignment.role]}\n        </span>\n      </span>\n      {load === null ? null : (\n        <span\n          aria-hidden=\"true\"\n          className=\"h-1 w-full overflow-hidden rounded-full bg-muted/40\"\n        >\n          <span\n            className=\"block h-full rounded-full bg-foreground/50\"\n            style={{ width: `${load * 100}%` }}\n          />\n        </span>\n      )}\n    </span>\n  );\n};\n\nconst Row = (props: { assignment: RoutingAssignment }): React.ReactElement => {\n  const { assignment } = props;\n  if (assignment.onActivate) {\n    const handleClick = (): void => {\n      assignment.onActivate?.();\n    };\n    return (\n      <button\n        className=\"flex w-full items-center gap-2 rounded-md border border-transparent px-2 py-1.5 text-left transition-colors hover:border-border hover:bg-muted/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring\"\n        data-routing-assignment={assignment.id}\n        data-routing-role={assignment.role}\n        onClick={handleClick}\n        type=\"button\"\n      >\n        <RowBody assignment={assignment} />\n      </button>\n    );\n  }\n  return (\n    <div\n      className=\"flex w-full items-center gap-2 rounded-md px-2 py-1.5\"\n      data-routing-assignment={assignment.id}\n      data-routing-role={assignment.role}\n    >\n      <RowBody assignment={assignment} />\n    </div>\n  );\n};\n\n/**\n * Right-dock panel listing the agent slots that the active route\n * dispatches to: primary handler + fallbacks + shadow probes. Each row\n * shows the agent name, role chip, and optional load bar. Pure\n * presentation; the host computes the assignments from the routing\n * config + observed traffic.\n *\n * @example\n * ```tsx\n * <RoutingAssignmentPanel\n *   assignments={[\n *     { id: \"1\", agent: \"researcher\",     role: \"primary\",  load: 0.82 },\n *     { id: \"2\", agent: \"researcher-mini\", role: \"fallback\", load: 0.04 },\n *     { id: \"3\", agent: \"shadow-eval\",    role: \"shadow\" },\n *   ]}\n * />\n * ```\n *\n * @public\n */\nexport const RoutingAssignmentPanel = forwardRef<\n  HTMLElement,\n  RoutingAssignmentPanelProps\n>((props, ref) => {\n  const { assignments, className, labels, title = \"Routing\", ...rest } = props;\n  const resolvedLabels = { ...DEFAULT_LABELS, ...labels };\n  return (\n    <section\n      aria-label={resolvedLabels.region}\n      className={cn(\n        \"flex w-full flex-col gap-2 rounded-lg border bg-background p-3 text-foreground\",\n        className,\n      )}\n      data-routing-assignment-panel\n      ref={ref}\n      {...rest}\n    >\n      <header>\n        <h3 className=\"text-xs font-semibold uppercase tracking-wide text-muted-foreground\">\n          {title}\n        </h3>\n      </header>\n      {assignments.length === 0 ? (\n        <p\n          className=\"px-2 py-3 text-center text-xs text-muted-foreground\"\n          data-routing-state=\"empty\"\n        >\n          {resolvedLabels.empty}\n        </p>\n      ) : (\n        <ul className=\"space-y-0.5\">\n          {assignments.map((assignment) => (\n            <li key={assignment.id}>\n              <Row assignment={assignment} />\n            </li>\n          ))}\n        </ul>\n      )}\n    </section>\n  );\n});\nRoutingAssignmentPanel.displayName = \"RoutingAssignmentPanel\";\n",
      "type": "registry:component"
    }
  ],
  "version": "0.2.1",
  "stability": "stable"
}
