{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "relationship-inspector",
  "type": "registry:component",
  "title": "Relationship Inspector",
  "description": "Right-dock panel listing inbound + outbound edges of the focused canvas object.",
  "dependencies": [
    "@vllnt/ui@^0.2.1"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "registry/default/relationship-inspector/relationship-inspector.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 * Direction of an edge relative to the focused object.\n *\n * @public\n */\nexport type RelationshipDirection = \"inbound\" | \"outbound\";\n\n/**\n * One edge in the relationship list.\n *\n * @public\n */\nexport type RelationshipEdge = {\n  /** Direction of the edge relative to the focused object. */\n  direction: RelationshipDirection;\n  /** Stable identifier — used as the React key. */\n  id: string;\n  /** Optional click handler — when provided, the row becomes a button. */\n  onActivate?: () => void;\n  /** Relation kind (e.g. `\"calls\"`, `\"emits\"`, `\"depends-on\"`). */\n  relation: string;\n  /** Target id (when outbound) or source id (when inbound). */\n  target: ReactNode;\n  /** Optional secondary line beneath the row. */\n  targetSublabel?: ReactNode;\n};\n\n/**\n * Localizable strings.\n *\n * @public\n */\nexport type RelationshipInspectorLabels = {\n  /** Empty-state copy. Defaults to `\"No relationships\"`. */\n  empty?: string;\n  /** Header for inbound edges. Defaults to `\"Inbound\"`. */\n  inbound?: string;\n  /** Header for outbound edges. Defaults to `\"Outbound\"`. */\n  outbound?: string;\n  /** Aria-label for the inspector. Defaults to `\"Relationship inspector\"`. */\n  region?: string;\n};\n\nconst DEFAULT_LABELS = {\n  empty: \"No relationships\",\n  inbound: \"Inbound\",\n  outbound: \"Outbound\",\n  region: \"Relationship inspector\",\n} as const satisfies Required<RelationshipInspectorLabels>;\n\n/**\n * Props for {@link RelationshipInspector}.\n *\n * @public\n */\nexport type RelationshipInspectorProps = {\n  /** Edges to render. Empty list shows the empty state. */\n  edges: RelationshipEdge[];\n  /** Localizable strings. */\n  labels?: RelationshipInspectorLabels;\n  /** Optional inspector title. Defaults to `\"Relationships\"`. */\n  title?: ReactNode;\n} & ComponentPropsWithoutRef<\"section\">;\n\nconst ARROW_GLYPH: Record<RelationshipDirection, string> = {\n  inbound: \"←\",\n  outbound: \"→\",\n};\n\nconst RowBody = (props: { edge: RelationshipEdge }): React.ReactElement => {\n  const { edge } = props;\n  return (\n    <span className=\"flex flex-1 items-center gap-2\">\n      <span aria-hidden=\"true\" className=\"text-muted-foreground\">\n        {ARROW_GLYPH[edge.direction]}\n      </span>\n      <span className=\"flex flex-1 flex-col text-left\">\n        <span className=\"truncate text-xs text-foreground\">{edge.target}</span>\n        {edge.targetSublabel ? (\n          <span className=\"truncate text-[10px] text-muted-foreground\">\n            {edge.targetSublabel}\n          </span>\n        ) : null}\n      </span>\n      <span className=\"rounded-full border border-border bg-background px-1.5 py-0.5 text-[10px] uppercase tracking-wide text-muted-foreground\">\n        {edge.relation}\n      </span>\n    </span>\n  );\n};\n\nconst Row = (props: { edge: RelationshipEdge }): React.ReactElement => {\n  const { edge } = props;\n  if (edge.onActivate) {\n    const handleClick = (): void => {\n      edge.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-relationship-direction={edge.direction}\n        data-relationship-row\n        onClick={handleClick}\n        type=\"button\"\n      >\n        <RowBody edge={edge} />\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-relationship-direction={edge.direction}\n      data-relationship-row\n    >\n      <RowBody edge={edge} />\n    </div>\n  );\n};\n\nconst Group = (props: {\n  edges: RelationshipEdge[];\n  heading: string;\n}): null | React.ReactElement => {\n  const { edges, heading } = props;\n  if (edges.length === 0) {\n    return null;\n  }\n  return (\n    <div className=\"space-y-1\" data-relationship-group={heading.toLowerCase()}>\n      <h4 className=\"px-2 text-[10px] font-medium uppercase tracking-wide text-muted-foreground\">\n        {heading}\n      </h4>\n      <ul className=\"space-y-0.5\">\n        {edges.map((edge) => (\n          <li key={edge.id}>\n            <Row edge={edge} />\n          </li>\n        ))}\n      </ul>\n    </div>\n  );\n};\n\n/**\n * Inspector panel listing inbound + outbound edges of the focused\n * object. Each row shows direction arrow, target id, optional sublabel,\n * and the relation kind chip. Pure presentation; the host computes\n * edges from the runtime graph and supplies an optional click handler\n * to jump to the related object.\n *\n * @example\n * ```tsx\n * <RelationshipInspector\n *   edges={[\n *     { id: \"1\", direction: \"inbound\",  target: \"research-2025\", relation: \"spawned-by\" },\n *     { id: \"2\", direction: \"outbound\", target: \"summary.md\",    relation: \"emits\" },\n *   ]}\n * />\n * ```\n *\n * @public\n */\nexport const RelationshipInspector = forwardRef<\n  HTMLElement,\n  RelationshipInspectorProps\n>((props, ref) => {\n  const { className, edges, labels, title = \"Relationships\", ...rest } = props;\n  const resolvedLabels = { ...DEFAULT_LABELS, ...labels };\n  const inbound = edges.filter((edge) => edge.direction === \"inbound\");\n  const outbound = edges.filter((edge) => edge.direction === \"outbound\");\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-relationship-inspector\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      {edges.length === 0 ? (\n        <p\n          className=\"px-2 py-3 text-center text-xs text-muted-foreground\"\n          data-relationship-state=\"empty\"\n        >\n          {resolvedLabels.empty}\n        </p>\n      ) : (\n        <div className=\"space-y-2\">\n          <Group edges={inbound} heading={resolvedLabels.inbound} />\n          <Group edges={outbound} heading={resolvedLabels.outbound} />\n        </div>\n      )}\n    </section>\n  );\n});\nRelationshipInspector.displayName = \"RelationshipInspector\";\n",
      "type": "registry:component"
    }
  ],
  "version": "0.2.1",
  "stability": "stable"
}
