({
commits,
truncateHash = 8,
railWidth = 24,
className,
...props
}: CommitGraphProps)
| 500 | // Main component |
| 501 | |
| 502 | function CommitGraph({ |
| 503 | commits, |
| 504 | truncateHash = 8, |
| 505 | railWidth = 24, |
| 506 | className, |
| 507 | ...props |
| 508 | }: CommitGraphProps) { |
| 509 | // Simple mode: if no commit has parents, infer a linear topology |
| 510 | const hasTopology = commits.some((c) => c.parents && c.parents.length > 0) |
| 511 | const resolvedCommits = hasTopology |
| 512 | ? commits |
| 513 | : commits.map((c, i) => ({ |
| 514 | ...c, |
| 515 | parents: i < commits.length - 1 ? [commits[i + 1].hash] : [], |
| 516 | })) |
| 517 | |
| 518 | if (resolvedCommits.length === 0) { |
| 519 | return ( |
| 520 | <div |
| 521 | data-slot="commit-graph" |
| 522 | className={cn( |
| 523 | "flex items-center justify-center rounded-xl border border-border/60 bg-card py-10 text-sm text-muted-foreground shadow-sm", |
| 524 | className |
| 525 | )} |
| 526 | {...props} |
| 527 | > |
| 528 | No commits. |
| 529 | </div> |
| 530 | ) |
| 531 | } |
| 532 | |
| 533 | const rows = computeLayout(resolvedCommits) |
| 534 | const maxRails = Math.max( |
| 535 | ...rows.map((r) => |
| 536 | Math.max( |
| 537 | r.rail + 1, |
| 538 | r.rails.length, |
| 539 | ...r.edges.map((e) => Math.max(e.fromRail, e.toRail) + 1) |
| 540 | ) |
| 541 | ) |
| 542 | ) |
| 543 | const svgWidth = maxRails * railWidth |
| 544 | |
| 545 | return ( |
| 546 | <div |
| 547 | data-slot="commit-graph" |
| 548 | className={cn( |
| 549 | "overflow-hidden rounded-xl border border-border/60 bg-card shadow-sm", |
| 550 | className |
| 551 | )} |
| 552 | {...props} |
| 553 | > |
| 554 | <div className="flex flex-col overflow-x-auto"> |
| 555 | {rows.map((row, i) => ( |
| 556 | <CommitDetail |
| 557 | key={`${row.commit.hash}-${i}`} |
| 558 | commit={row.commit} |
| 559 | hashLength={truncateHash} |
nothing calls this directly
no test coverage detected