From bfc1d3ca8fd7844fb9419933b93d213216fbae66 Mon Sep 17 00:00:00 2001 From: Georges-Antoine Assi Date: Tue, 7 Jul 2026 15:01:54 -0400 Subject: [PATCH] feat(sidebar): show PR state for cloud runs with a provenance badge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cloud runs never surfaced their branch lifecycle (draft/open/merged/closed PR) in the sidebar: the terminal-cloud branch of TaskIcon's waterfall won before the PR-state branch, so a completed cloud run always showed the green cloud icon even when its PR state was already resolved via the run's pr_url. Now a cloud run that finished cleanly and has a known PR state renders the same PR-state glyphs as local runs (merged purple, open green, draft gray, closed red), with a small filled cloud — or the origin-product icon (Slack, Signals, …) — stacked on the glyph's bottom-right corner so cloud provenance stays visible at a glance. A radial-gradient mask punches a hole in the PR glyph under the badge, keeping both shapes legible on any row background without hardcoding a cutout ring color. Origin-branded tasks keep their clickable thread link on the stacked icon. Failed/cancelled cloud runs keep the red cloud icon: the failure remains the actionable signal there. Queued/running runs and completed runs without a PR are unchanged. Applies to every TaskIcon consumer (sidebar, tabs, command palette, command center). Generated-By: PostHog Code Task-Id: 3c36b344-ed49-424a-9218-703049ad89db --- .../sidebar/components/items/TaskIcon.tsx | 134 ++++++++++++------ 1 file changed, 92 insertions(+), 42 deletions(-) diff --git a/packages/ui/src/features/sidebar/components/items/TaskIcon.tsx b/packages/ui/src/features/sidebar/components/items/TaskIcon.tsx index fbe1adcfb5..5d586864ab 100644 --- a/packages/ui/src/features/sidebar/components/items/TaskIcon.tsx +++ b/packages/ui/src/features/sidebar/components/items/TaskIcon.tsx @@ -184,61 +184,90 @@ function CloudStatusIcon({ ); } +type PrStateMeta = { Icon: typeof GitMerge; color: string; label: string }; +const PR_STATE_META: Record, PrStateMeta> = { + merged: { Icon: GitMerge, color: "var(--purple-11)", label: "PR merged" }, + open: { Icon: GitPullRequest, color: "var(--green-11)", label: "PR open" }, + draft: { Icon: GitPullRequest, color: "var(--gray-9)", label: "Draft PR" }, + closed: { Icon: GitPullRequest, color: "var(--red-11)", label: "PR closed" }, +}; +const DIFF_META: PrStateMeta = { + Icon: GitBranch, + color: "var(--amber-11)", + label: "Has changes", +}; + function PrStatusIcon({ prState, hasDiff, size, + provenanceBadge, + threadUrl, }: { prState?: SidebarPrState; hasDiff?: boolean; size: number; + /** When set (cloud tasks), a small provenance glyph (cloud or origin + * product) is stacked on the icon's bottom-right corner so "where this ran" + * stays visible alongside the PR state. */ + provenanceBadge?: OriginProductMeta; + /** Originating thread URL; keeps the badge state clickable like + * `CloudStatusIcon` does for origin-branded tasks. */ + threadUrl?: string; }) { - if (prState === "merged") { - return ( - - - - - - ); - } - if (prState === "open") { - return ( - - - - - - ); - } - if (prState === "draft") { - return ( - - - - - - ); - } - if (prState === "closed") { - return ( - - - - - - ); - } - if (hasDiff) { + const meta = prState ? PR_STATE_META[prState] : hasDiff ? DIFF_META : null; + if (!meta) return null; + + if (!provenanceBadge) { return ( - + - + ); } - return null; + + // Stack the provenance glyph over the PR icon's bottom-right corner. A + // radial-gradient mask punches a hole in the PR glyph under the badge so + // both shapes stay legible on any row background (hover, selected, command + // palette highlight) without hardcoding a cutout ring color. + const badgeSize = Math.round(size * 0.66); + const overflow = 2; + const holeCenter = size + overflow - badgeSize / 2; + const holeRadius = badgeSize / 2 + 1; + const mask = `radial-gradient(circle at ${holeCenter}px ${holeCenter}px, transparent ${holeRadius}px, black ${holeRadius + 0.5}px)`; + const icon = ( + + + + + ); + return ( + + {renderIconSpan({ + icon, + link: threadUrl, + ariaLabel: threadUrl + ? `Open ${provenanceBadge.label} thread` + : undefined, + })} + + ); } export interface TaskIconProps { @@ -280,6 +309,8 @@ export function TaskIcon({ }: TaskIconProps) { const isCloudTask = workspaceMode === "cloud"; const isTerminalCloud = isCloudTask && isTerminalStatus(taskRunStatus); + const cloudRunFailed = + taskRunStatus === "failed" || taskRunStatus === "cancelled"; const originProductMeta = getOriginProductMeta(originProduct); if (needsPermission) { @@ -308,7 +339,12 @@ export function TaskIcon({ ); } - if (isTerminalCloud) { + // A failed/cancelled cloud run keeps the red cloud icon — the failure is + // the actionable signal there. A cloud run that finished cleanly and has a + // PR falls through to the PR-state icon (with a provenance badge), because + // once the run is done the branch's lifecycle is the state worth scanning + // for — the same one local runs show. + if (isTerminalCloud && (cloudRunFailed || !prState)) { return ( ; + return ( + + ); } if (isPinned) { return ;