diff --git a/apps/code/src/renderer/features/sidebar/components/TaskListView.tsx b/apps/code/src/renderer/features/sidebar/components/TaskListView.tsx index 7fea8db2c5..82487858cd 100644 --- a/apps/code/src/renderer/features/sidebar/components/TaskListView.tsx +++ b/apps/code/src/renderer/features/sidebar/components/TaskListView.tsx @@ -131,6 +131,7 @@ function TaskRow({ slackThreadUrl={task.slackThreadUrl} prState={prState} hasDiff={hasDiff} + prUrl={task.cloudPrUrl} timestamp={timestamp} onClick={onClick} onDoubleClick={onDoubleClick} diff --git a/apps/code/src/renderer/features/sidebar/components/items/TaskItem.tsx b/apps/code/src/renderer/features/sidebar/components/items/TaskItem.tsx index a5ee2a5b49..3c5b2c66db 100644 --- a/apps/code/src/renderer/features/sidebar/components/items/TaskItem.tsx +++ b/apps/code/src/renderer/features/sidebar/components/items/TaskItem.tsx @@ -1,13 +1,54 @@ import { Tooltip } from "@components/ui/Tooltip"; import type { SidebarPrState } from "@features/sidebar/hooks/useTaskPrStatus"; import type { WorkspaceMode } from "@main/services/workspace/schemas"; -import { Archive, PushPin } from "@phosphor-icons/react"; +import { Archive, GitPullRequest, PushPin } from "@phosphor-icons/react"; import type { TaskRunStatus } from "@shared/types"; +import { openUrlInBrowser } from "@utils/browser"; import { formatRelativeTimeShort } from "@utils/time"; import { useCallback, useEffect, useRef, useState } from "react"; import { SidebarItem } from "../SidebarItem"; import { TaskIcon } from "./TaskIcon"; +function extractPrNumber(url: string): string | null { + const match = url.match( + /\/(?:pull|pulls|merge_requests|pull-requests)\/(\d+)/, + ); + return match ? match[1] : null; +} + +function PrBadge({ url }: { url: string }) { + const number = extractPrNumber(url); + const open = () => { + void openUrlInBrowser(url); + }; + return ( + + {/* biome-ignore lint/a11y/useSemanticElements: nested clickable inside SidebarItem button */} + { + e.stopPropagation(); + open(); + }} + onDoubleClick={(e) => e.stopPropagation()} + onKeyDown={(e) => { + if (e.key === "Enter" || e.key === " ") { + e.preventDefault(); + e.stopPropagation(); + open(); + } + }} + > + + {number ? `#${number}` : "PR"} + + + ); +} + interface TaskItemProps { depth?: number; taskId: string; @@ -27,6 +68,7 @@ interface TaskItemProps { slackThreadUrl?: string; prState?: SidebarPrState; hasDiff?: boolean; + prUrl?: string | null; timestamp?: number; isEditing?: boolean; onClick: (e: React.MouseEvent) => void; @@ -123,6 +165,7 @@ export function TaskItem({ slackThreadUrl, prState, hasDiff, + prUrl, timestamp, isEditing = false, onClick, @@ -149,6 +192,8 @@ export function TaskItem({ /> ); + const prBadge = prUrl ? : null; + const timestampNode = timestamp ? ( {formatRelativeTimeShort(timestamp)} @@ -165,8 +210,9 @@ export function TaskItem({ ) : null; const endContent = - timestampNode || toolbar ? ( + prBadge || timestampNode || toolbar ? ( <> + {prBadge} {timestampNode} {toolbar}