From a9f60911ba2f3c3471fee31aa463522b2aebde01 Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Fri, 24 Jul 2026 14:02:42 +0200 Subject: [PATCH] Color settled PR labels on hover Co-authored-by: codex --- apps/web/src/components/SidebarV2.tsx | 9 +++++-- .../components/ThreadStatusIndicators.test.ts | 25 ++++++++++++++++++- .../src/components/ThreadStatusIndicators.tsx | 13 +++++++++- 3 files changed, 43 insertions(+), 4 deletions(-) diff --git a/apps/web/src/components/SidebarV2.tsx b/apps/web/src/components/SidebarV2.tsx index e018491348e..fd59cb63adc 100644 --- a/apps/web/src/components/SidebarV2.tsx +++ b/apps/web/src/components/SidebarV2.tsx @@ -117,7 +117,11 @@ import { sortThreadsForSidebarV2, } from "./Sidebar.logic"; import { resolveLocalCheckoutBranchMismatch } from "./BranchToolbar.logic"; -import { prStatusIndicator, resolveThreadPr } from "./ThreadStatusIndicators"; +import { + prStatusIndicator, + resolveThreadPr, + settledPrHoverColorClass, +} from "./ThreadStatusIndicators"; import { resolveSnoozePresets, snoozeWakeDescription, @@ -498,6 +502,7 @@ const SidebarV2Row = memo(function SidebarV2Row(props: { hasDedicatedWorktree: thread.worktreePath !== null, }); const prStatus = prStatusIndicator(pr, gitStatus.data?.sourceControlProvider); + const settledPrHoverClass = pr ? settledPrHoverColorClass(pr.state) : undefined; // Report the PR state up: the parent partitions rows with effectiveSettled, // and a merged/closed PR auto-settles a thread — data only rows have. const prState = pr?.state ?? null; @@ -714,7 +719,7 @@ const SidebarV2Row = memo(function SidebarV2Row(props: { variant === "slim" && variantAction === "unsettle" ? props.isActive ? "text-muted-foreground/70" - : "text-muted-foreground/35 transition-colors group-hover/v2-row:text-muted-foreground/65" + : cn("text-muted-foreground/35 transition-colors", settledPrHoverClass) : prStatus.colorClass, )} aria-label={prStatus.tooltip} diff --git a/apps/web/src/components/ThreadStatusIndicators.test.ts b/apps/web/src/components/ThreadStatusIndicators.test.ts index 24ccc6ef33f..9fb4535f266 100644 --- a/apps/web/src/components/ThreadStatusIndicators.test.ts +++ b/apps/web/src/components/ThreadStatusIndicators.test.ts @@ -1,7 +1,11 @@ import type { VcsStatusResult } from "@t3tools/contracts"; import { describe, expect, it } from "vite-plus/test"; -import { prStatusIndicator, resolveThreadPr } from "./ThreadStatusIndicators"; +import { + prStatusIndicator, + resolveThreadPr, + settledPrHoverColorClass, +} from "./ThreadStatusIndicators"; function status(overrides: Partial = {}): VcsStatusResult { return { @@ -70,4 +74,23 @@ describe("prStatusIndicator", () => { tooltipTitle: "PR branch", }); }); + + it("uses red for closed pull requests", () => { + const closedPr = status().pr; + if (!closedPr) throw new Error("Expected pull request fixture"); + + expect(prStatusIndicator({ ...closedPr, state: "closed" }, undefined)?.colorClass).toContain( + "text-red-600", + ); + }); +}); + +describe("settledPrHoverColorClass", () => { + it.each([ + ["open", "text-emerald-600"], + ["merged", "text-violet-600"], + ["closed", "text-red-600"], + ] as const)("restores the %s pull request color on row hover", (state, colorClass) => { + expect(settledPrHoverColorClass(state)).toContain(`group-hover/v2-row:${colorClass}`); + }); }); diff --git a/apps/web/src/components/ThreadStatusIndicators.tsx b/apps/web/src/components/ThreadStatusIndicators.tsx index 3c58d6ea989..0d3291a9e28 100644 --- a/apps/web/src/components/ThreadStatusIndicators.tsx +++ b/apps/web/src/components/ThreadStatusIndicators.tsx @@ -35,6 +35,17 @@ export interface TerminalStatusIndicator { export type ThreadPr = VcsStatusResult["pr"]; +export function settledPrHoverColorClass(state: NonNullable["state"]): string { + switch (state) { + case "open": + return "group-hover/v2-row:text-emerald-600 dark:group-hover/v2-row:text-emerald-300/90"; + case "merged": + return "group-hover/v2-row:text-violet-600 dark:group-hover/v2-row:text-violet-300/90"; + case "closed": + return "group-hover/v2-row:text-red-600 dark:group-hover/v2-row:text-red-300/90"; + } +} + export function prStatusIndicator( pr: ThreadPr, provider: VcsStatusResult["sourceControlProvider"] | null | undefined, @@ -65,7 +76,7 @@ export function prStatusIndicator( if (pr.state === "closed") { return { label: `${presentation.shortName} closed`, - colorClass: "text-zinc-500 dark:text-zinc-400/80", + colorClass: "text-red-600 dark:text-red-300/90", tooltip, tooltipLead, tooltipTitle: pr.title,