diff --git a/frontend/src/renderer/components/PRSummaryDisplay.test.tsx b/frontend/src/renderer/components/PRSummaryDisplay.test.tsx index 373d1e43e..cc183b174 100644 --- a/frontend/src/renderer/components/PRSummaryDisplay.test.tsx +++ b/frontend/src/renderer/components/PRSummaryDisplay.test.tsx @@ -52,4 +52,29 @@ describe("PRSummaryParts", () => { expect(screen.queryByText("types")).not.toBeInTheDocument(); expect(screen.getByText("+1 check")).toBeInTheDocument(); }); + + it("counts overflow beyond helper-truncated links", () => { + render( + , + ); + + expect(screen.getByText("unit")).toBeInTheDocument(); + expect(screen.getByText("lint")).toBeInTheDocument(); + expect(screen.getByText("types")).toBeInTheDocument(); + expect(screen.queryByText("build")).not.toBeInTheDocument(); + expect(screen.getByText("+1 check")).toBeInTheDocument(); + }); }); diff --git a/frontend/src/renderer/components/PRSummaryDisplay.tsx b/frontend/src/renderer/components/PRSummaryDisplay.tsx index b9385b97b..bf45f5fc4 100644 --- a/frontend/src/renderer/components/PRSummaryDisplay.tsx +++ b/frontend/src/renderer/components/PRSummaryDisplay.tsx @@ -97,7 +97,10 @@ export function PRSummaryParts({ > {parts.map((part) => { const links = part.links.slice(0, maxLinks); - const overflowLabel = overflowPartLabel(part.links.length - links.length, part.overflowNoun); + const overflowLabel = overflowPartLabel( + (part.linkTotal ?? part.links.length) - links.length, + part.overflowNoun, + ); return (
diff --git a/frontend/src/renderer/lib/pr-display.ts b/frontend/src/renderer/lib/pr-display.ts index f5b4c71ea..da1861df0 100644 --- a/frontend/src/renderer/lib/pr-display.ts +++ b/frontend/src/renderer/lib/pr-display.ts @@ -41,6 +41,7 @@ export type PRSummaryPart = { status: string; summary?: string; links: PRSummaryLink[]; + linkTotal?: number; overflowLabel?: string; overflowNoun?: string; tone: PRDisplayTone; @@ -124,6 +125,7 @@ export function prSummaryParts(pr: SessionPRSummary): PRSummaryPart[] { status: ciLabel(pr.ci.state), summary: ciSummary(pr), links: ciLinks(pr), + linkTotal: pr.ci.state === "failing" ? pr.ci.failingChecks.length : 0, overflowLabel: pr.ci.state === "failing" ? overflowLabel(pr.ci.failingChecks.length, 3, "check") : undefined, overflowNoun: "check", tone: ciTone(pr.ci.state), @@ -134,6 +136,7 @@ export function prSummaryParts(pr: SessionPRSummary): PRSummaryPart[] { status: mergeabilityLabel(pr.mergeability.state), summary: mergeSummary(pr), links: mergeLinks(pr), + linkTotal: mergeLinkTotal(pr), overflowLabel: mergeOverflowLabel(pr), overflowNoun: mergeOverflowNoun(pr), tone: mergeabilityTone(pr.mergeability.state), @@ -144,6 +147,7 @@ export function prSummaryParts(pr: SessionPRSummary): PRSummaryPart[] { status: reviewLabel(pr.review.decision), summary: reviewSummary(pr), links: reviewLinks(pr), + linkTotal: reviewLinkTotal(pr), overflowLabel: pr.state === "draft" || pr.review.decision === "review_required" ? undefined @@ -254,10 +258,34 @@ function mergeOverflowLabel(pr: SessionPRSummary): string | undefined { return undefined; } +function mergeLinkTotal(pr: SessionPRSummary): number { + if (pr.state === "merged" || pr.state === "closed") { + return 0; + } + if (pr.mergeability.state === "conflicting") { + const conflictFileCount = pr.mergeability.conflictFiles?.length ?? 0; + return conflictFileCount > 0 ? conflictFileCount : mergeLinks(pr).length; + } + if (pr.mergeability.state === "blocked" || pr.mergeability.state === "unstable") { + return pr.mergeability.reasons.length; + } + return 0; +} + function mergeOverflowNoun(pr: SessionPRSummary): string { return (pr.mergeability.conflictFiles ?? []).length > 0 ? "file" : "reason"; } +function reviewLinkTotal(pr: SessionPRSummary): number { + if (pr.state === "merged" || pr.state === "closed" || pr.state === "draft") { + return 0; + } + if (pr.review.decision !== "changes_requested" && !pr.review.hasUnresolvedHumanComments) { + return 0; + } + return pr.review.unresolvedBy.length > 0 ? pr.review.unresolvedBy.length : reviewLinks(pr).length; +} + function toCIState(value: string): SessionPRSummary["ci"]["state"] { return ciStates.has(value as SessionPRSummary["ci"]["state"]) ? (value as SessionPRSummary["ci"]["state"])