{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "limp-range",
  "title": "Opponent limp range",
  "description": "The hands your opponents showed down after limping, on the 13×13 matrix. Needs your API key.",
  "registryDependencies": [
    "https://www.pokerstudy.ai/r/poker-core.json",
    "https://www.pokerstudy.ai/r/hand-matrix.json",
    "https://www.pokerstudy.ai/r/pokerstudy-api.json"
  ],
  "files": [
    {
      "path": "src/components/pokerstudy/limp-range.tsx",
      "content": "\"use client\";\n\nimport { HandMatrix, type MatrixCell } from \"@/components/pokerstudy/hand-matrix\";\nimport { PokerStudyStatus, usePokerStudy } from \"@/components/pokerstudy/use-pokerstudy\";\nimport { ACTION_COLORS, ALL_HANDS, type LimpRange } from \"@/lib/pokerstudy/poker\";\n\n/** Shown limp hands on the 13×13 matrix, each cell filled by its share of the most-shown class. */\nexport function LimpRangeView({ data }: { data: LimpRange }) {\n  const counts = new Map(data.classes.map((c) => [c.handClass, c.count]));\n  const max = Math.max(1, ...counts.values());\n  const cells: (MatrixCell | null)[] = ALL_HANDS.map((h) => {\n    const n = counts.get(h) ?? 0;\n    return n > 0 ? { label: h, freqs: [n / max, 1 - n / max] } : null;\n  });\n  const positions = Object.entries(data.byPosition);\n  return (\n    <div className=\"flex w-full max-w-[560px] flex-col gap-4\">\n      <div className=\"flex flex-wrap items-baseline justify-between gap-2 text-sm\">\n        <span className=\"font-medium text-foreground\">{data.shownLimps} limps shown down</span>\n        <span className=\"font-mono text-xs text-muted-foreground\">\n          {positions.map(([p, n]) => `${p} ${n}`).join(\" · \")}\n        </span>\n      </div>\n      <HandMatrix\n        cells={cells}\n        colors={[ACTION_COLORS.Call, \"rgba(255,255,255,0.06)\"]}\n        legend={[{ label: \"Limped and shown; a fuller cell was shown more often\", color: ACTION_COLORS.Call }]}\n      />\n      {data.note ? <p className=\"text-xs leading-relaxed text-muted-foreground\">{data.note}</p> : null}\n    </div>\n  );\n}\n\n/**\n * What your opponents limp with, from the hands they showed down in your\n * uploaded histories. Filter by `opponentId` or `position`. Private data: see\n * `authorizePrivate`.\n */\nexport function LimpRange({\n  opponentId,\n  position,\n  endpoint = \"/api/pokerstudy\",\n  initialData,\n}: {\n  opponentId?: string;\n  position?: string[];\n  endpoint?: string;\n  initialData?: LimpRange;\n}) {\n  const state = usePokerStudy<LimpRange>(`${endpoint}/limp-range`, {\n    body: { opponent_id: opponentId ?? null, position: position ?? null, limit: 169 },\n    initialData,\n  });\n  if (state.status !== \"ready\") return <PokerStudyStatus state={state} />;\n  if (!state.data.found) return <p className=\"text-sm text-muted-foreground\">Opponent not found.</p>;\n  return <LimpRangeView data={state.data} />;\n}\n",
      "type": "registry:component",
      "target": "components/pokerstudy/limp-range.tsx"
    }
  ],
  "type": "registry:block"
}