-
Notifications
You must be signed in to change notification settings - Fork 54
feat(sidebar): show PR state for cloud runs with a provenance badge #3245
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
bfc1d3c
25c86b4
5db3e39
201ca1d
94f055a
891ff3c
61bf4e6
04e39d6
e51a417
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -184,61 +184,84 @@ function CloudStatusIcon({ | |
| ); | ||
| } | ||
|
|
||
| type PrStateMeta = { Icon: typeof GitMerge; color: string; label: string }; | ||
| const PR_STATE_META: Record<Exclude<SidebarPrState, null>, 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", | ||
| }; | ||
| const CLOUD_BADGE_META: OriginProductMeta = { Icon: CloudIcon, label: "Cloud" }; | ||
|
|
||
| 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 ( | ||
| <Tooltip content="PR merged" side="right"> | ||
| <span className="flex items-center justify-center"> | ||
| <GitMerge size={size} weight="bold" color="var(--purple-11)" /> | ||
| </span> | ||
| </Tooltip> | ||
| ); | ||
| } | ||
| if (prState === "open") { | ||
| return ( | ||
| <Tooltip content="PR open" side="right"> | ||
| <span className="flex items-center justify-center"> | ||
| <GitPullRequest size={size} weight="bold" color="var(--green-11)" /> | ||
| </span> | ||
| </Tooltip> | ||
| ); | ||
| } | ||
| if (prState === "draft") { | ||
| return ( | ||
| <Tooltip content="Draft PR" side="right"> | ||
| <span className="flex items-center justify-center"> | ||
| <GitPullRequest size={size} weight="bold" color="var(--gray-9)" /> | ||
| </span> | ||
| </Tooltip> | ||
| ); | ||
| } | ||
| if (prState === "closed") { | ||
| return ( | ||
| <Tooltip content="PR closed" side="right"> | ||
| <span className="flex items-center justify-center"> | ||
| <GitPullRequest size={size} weight="bold" color="var(--red-11)" /> | ||
| </span> | ||
| </Tooltip> | ||
| ); | ||
| } | ||
| if (hasDiff) { | ||
| const meta = prState ? PR_STATE_META[prState] : hasDiff ? DIFF_META : null; | ||
| if (!meta) return null; | ||
|
|
||
| if (!provenanceBadge) { | ||
| return ( | ||
| <Tooltip content="Has changes" side="right"> | ||
| <Tooltip content={meta.label} side="right"> | ||
| <span className="flex items-center justify-center"> | ||
| <GitBranch size={size} weight="bold" color="var(--amber-11)" /> | ||
| <meta.Icon size={size} weight="bold" color={meta.color} /> | ||
| </span> | ||
| </Tooltip> | ||
| ); | ||
| } | ||
| 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 mask = | ||
| "radial-gradient(ellipse 37% 37% at 76.5% 76.5%, transparent 97%, black 100%)"; | ||
| const icon = ( | ||
| <span className="relative flex items-center justify-center"> | ||
| <meta.Icon | ||
| size={size} | ||
| weight="bold" | ||
| color={meta.color} | ||
| style={{ maskImage: mask, WebkitMaskImage: mask }} | ||
| /> | ||
| <provenanceBadge.Icon | ||
| weight="fill" | ||
| color="var(--gray-10)" | ||
| className="absolute" | ||
| style={{ width: "65%", height: "65%", right: "-9%", bottom: "-9%" }} | ||
| /> | ||
| </span> | ||
| ); | ||
| return ( | ||
| <Tooltip content={`${meta.label} · ${provenanceBadge.label}`} side="right"> | ||
| {renderIconSpan({ | ||
| icon, | ||
| link: threadUrl, | ||
| ariaLabel: threadUrl | ||
| ? `Open ${provenanceBadge.label} thread` | ||
| : undefined, | ||
| })} | ||
| </Tooltip> | ||
| ); | ||
| } | ||
|
|
||
| export interface TaskIconProps { | ||
|
|
@@ -280,6 +303,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 +333,12 @@ export function TaskIcon({ | |
| </Tooltip> | ||
| ); | ||
| } | ||
| 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 ( | ||
| <CloudStatusIcon | ||
| taskRunStatus={taskRunStatus} | ||
|
|
@@ -328,7 +358,19 @@ export function TaskIcon({ | |
| ); | ||
| } | ||
| if (prState || hasDiff) { | ||
| return <PrStatusIcon prState={prState} hasDiff={hasDiff} size={size} />; | ||
| return ( | ||
| <PrStatusIcon | ||
| prState={prState} | ||
| hasDiff={hasDiff} | ||
| size={size} | ||
| provenanceBadge={ | ||
| isCloudTask ? (originProductMeta ?? CLOUD_BADGE_META) : undefined | ||
| } | ||
|
Comment on lines
+366
to
+368
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time! |
||
| threadUrl={ | ||
| isCloudTask && originProductMeta ? slackThreadUrl : undefined | ||
| } | ||
| /> | ||
| ); | ||
| } | ||
| if (isPinned) { | ||
| return <PushPin size={size} color="var(--accent-11)" />; | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.