From 888854236e666936fe14da67403e2bc02c70062e Mon Sep 17 00:00:00 2001 From: Chris Tate Date: Sat, 25 Oct 2025 14:57:25 +0000 Subject: [PATCH 1/6] Add PR status overlay to bottom-right of sidebar task icon --- components/pr-check-status.tsx | 84 ++++++++++++++++++++++++++++++++ components/task-sidebar.tsx | 8 ++- components/tasks-list-client.tsx | 8 ++- 3 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 components/pr-check-status.tsx diff --git a/components/pr-check-status.tsx b/components/pr-check-status.tsx new file mode 100644 index 00000000..df4cccf7 --- /dev/null +++ b/components/pr-check-status.tsx @@ -0,0 +1,84 @@ +'use client' + +import { useEffect, useState } from 'react' +import { Check, Loader2, X } from 'lucide-react' + +interface CheckRun { + id: number + name: string + status: string + conclusion: string | null + html_url: string + started_at: string | null + completed_at: string | null +} + +interface PRCheckStatusProps { + taskId: string + className?: string +} + +export function PRCheckStatus({ taskId, className = '' }: PRCheckStatusProps) { + const [checkRuns, setCheckRuns] = useState([]) + const [isLoading, setIsLoading] = useState(true) + + useEffect(() => { + const fetchCheckRuns = async () => { + try { + const response = await fetch(`/api/tasks/${taskId}/check-runs`) + if (response.ok) { + const data = await response.json() + if (data.success && data.checkRuns) { + setCheckRuns(data.checkRuns) + } + } + } catch (error) { + console.error('Error fetching check runs:', error) + } finally { + setIsLoading(false) + } + } + + fetchCheckRuns() + // Refresh every 30 seconds for in-progress checks + const interval = setInterval(fetchCheckRuns, 30000) + return () => clearInterval(interval) + }, [taskId]) + + // Don't render anything if loading or no check runs + if (isLoading || checkRuns.length === 0) { + return null + } + + // Determine overall status + const hasInProgress = checkRuns.some((run) => run.status === 'in_progress' || run.status === 'queued') + const hasFailed = checkRuns.some((run) => run.conclusion === 'failure' || run.conclusion === 'cancelled') + const allPassed = checkRuns.every((run) => run.status === 'completed' && run.conclusion === 'success') + + // Render the appropriate indicator + if (hasInProgress) { + return ( +
+
+
+ ) + } + + if (hasFailed) { + return ( +
+
+
+ ) + } + + if (allPassed) { + return ( +
+ +
+ ) + } + + return null +} diff --git a/components/task-sidebar.tsx b/components/task-sidebar.tsx index 896a5311..ca3f234a 100644 --- a/components/task-sidebar.tsx +++ b/components/task-sidebar.tsx @@ -25,6 +25,7 @@ import { useTasks } from '@/components/app-layout' import { useAtomValue } from 'jotai' import { sessionAtom } from '@/lib/atoms/session' import { PRStatusIcon } from '@/components/pr-status-icon' +import { PRCheckStatus } from '@/components/pr-check-status' // Model mappings for human-friendly names const AGENT_MODELS = { @@ -397,7 +398,12 @@ export function TaskSidebar({ tasks, onTaskSelect, width = 288 }: TaskSidebarPro
{task.repoUrl && (
- {task.prStatus && } + {task.prStatus && ( +
+ + +
+ )} {(() => { try { diff --git a/components/tasks-list-client.tsx b/components/tasks-list-client.tsx index 71a3b8dc..832e388e 100644 --- a/components/tasks-list-client.tsx +++ b/components/tasks-list-client.tsx @@ -28,6 +28,7 @@ import type { Session } from '@/lib/session/types' import { VERCEL_DEPLOY_URL } from '@/lib/constants' import { Claude, Codex, Copilot, Cursor, Gemini, OpenCode } from '@/components/logos' import { PRStatusIcon } from '@/components/pr-status-icon' +import { PRCheckStatus } from '@/components/pr-check-status' interface TasksListClientProps { user: Session['user'] | null @@ -420,7 +421,12 @@ export function TasksListClient({ user, authProvider, initialStars = 1056 }: Tas
{task.repoUrl && (
- {task.prStatus && } + {task.prStatus && ( +
+ + +
+ )} {(() => { try { From 21d4d6c20190321f10332319b7b308fff159d316 Mon Sep 17 00:00:00 2001 From: Chris Tate Date: Sat, 25 Oct 2025 15:01:00 +0000 Subject: [PATCH 2/6] Reduce cursor dot size in agent user interface --- components/pr-check-status.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/pr-check-status.tsx b/components/pr-check-status.tsx index df4cccf7..d205d2a7 100644 --- a/components/pr-check-status.tsx +++ b/components/pr-check-status.tsx @@ -59,7 +59,7 @@ export function PRCheckStatus({ taskId, className = '' }: PRCheckStatusProps) { if (hasInProgress) { return (
-
+
) } @@ -67,7 +67,7 @@ export function PRCheckStatus({ taskId, className = '' }: PRCheckStatusProps) { if (hasFailed) { return (
-
+
) } @@ -75,7 +75,7 @@ export function PRCheckStatus({ taskId, className = '' }: PRCheckStatusProps) { if (allPassed) { return (
- +
) } From 4a71317516766cad4219a74b61d70be9c64128f0 Mon Sep 17 00:00:00 2001 From: Chris Tate Date: Sat, 25 Oct 2025 15:03:11 +0000 Subject: [PATCH 3/6] Fix circle border to match task bg for active/inactive state --- components/pr-check-status.tsx | 12 ++++++++---- components/task-sidebar.tsx | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/components/pr-check-status.tsx b/components/pr-check-status.tsx index d205d2a7..9dc37fc9 100644 --- a/components/pr-check-status.tsx +++ b/components/pr-check-status.tsx @@ -15,10 +15,11 @@ interface CheckRun { interface PRCheckStatusProps { taskId: string + isActive?: boolean className?: string } -export function PRCheckStatus({ taskId, className = '' }: PRCheckStatusProps) { +export function PRCheckStatus({ taskId, isActive = false, className = '' }: PRCheckStatusProps) { const [checkRuns, setCheckRuns] = useState([]) const [isLoading, setIsLoading] = useState(true) @@ -55,10 +56,13 @@ export function PRCheckStatus({ taskId, className = '' }: PRCheckStatusProps) { const hasFailed = checkRuns.some((run) => run.conclusion === 'failure' || run.conclusion === 'cancelled') const allPassed = checkRuns.every((run) => run.status === 'completed' && run.conclusion === 'success') + // Determine background color based on active state + const bgColor = isActive ? 'bg-accent' : 'bg-card' + // Render the appropriate indicator if (hasInProgress) { return ( -
+
) @@ -66,7 +70,7 @@ export function PRCheckStatus({ taskId, className = '' }: PRCheckStatusProps) { if (hasFailed) { return ( -
+
) @@ -74,7 +78,7 @@ export function PRCheckStatus({ taskId, className = '' }: PRCheckStatusProps) { if (allPassed) { return ( -
+
) diff --git a/components/task-sidebar.tsx b/components/task-sidebar.tsx index ca3f234a..0edf250f 100644 --- a/components/task-sidebar.tsx +++ b/components/task-sidebar.tsx @@ -401,7 +401,7 @@ export function TaskSidebar({ tasks, onTaskSelect, width = 288 }: TaskSidebarPro {task.prStatus && (
- +
)} From f7f249fca4a9dba653c7e9c91636e6d873416d53 Mon Sep 17 00:00:00 2001 From: Chris Tate Date: Sat, 25 Oct 2025 15:05:09 +0000 Subject: [PATCH 4/6] Shrink status dot and hide on merged prs; show only for open prs --- components/pr-check-status.tsx | 14 ++++++++++---- components/task-sidebar.tsx | 2 +- components/tasks-list-client.tsx | 2 +- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/components/pr-check-status.tsx b/components/pr-check-status.tsx index 9dc37fc9..f51b2358 100644 --- a/components/pr-check-status.tsx +++ b/components/pr-check-status.tsx @@ -15,11 +15,12 @@ interface CheckRun { interface PRCheckStatusProps { taskId: string + prStatus: 'open' | 'closed' | 'merged' isActive?: boolean className?: string } -export function PRCheckStatus({ taskId, isActive = false, className = '' }: PRCheckStatusProps) { +export function PRCheckStatus({ taskId, prStatus, isActive = false, className = '' }: PRCheckStatusProps) { const [checkRuns, setCheckRuns] = useState([]) const [isLoading, setIsLoading] = useState(true) @@ -46,6 +47,11 @@ export function PRCheckStatus({ taskId, isActive = false, className = '' }: PRCh return () => clearInterval(interval) }, [taskId]) + // Only show indicator for open PRs + if (prStatus !== 'open') { + return null + } + // Don't render anything if loading or no check runs if (isLoading || checkRuns.length === 0) { return null @@ -63,7 +69,7 @@ export function PRCheckStatus({ taskId, isActive = false, className = '' }: PRCh if (hasInProgress) { return (
-
+
) } @@ -71,7 +77,7 @@ export function PRCheckStatus({ taskId, isActive = false, className = '' }: PRCh if (hasFailed) { return (
-
+
) } @@ -79,7 +85,7 @@ export function PRCheckStatus({ taskId, isActive = false, className = '' }: PRCh if (allPassed) { return (
- +
) } diff --git a/components/task-sidebar.tsx b/components/task-sidebar.tsx index 0edf250f..18211fc7 100644 --- a/components/task-sidebar.tsx +++ b/components/task-sidebar.tsx @@ -401,7 +401,7 @@ export function TaskSidebar({ tasks, onTaskSelect, width = 288 }: TaskSidebarPro {task.prStatus && (
- +
)} diff --git a/components/tasks-list-client.tsx b/components/tasks-list-client.tsx index 832e388e..a840b45b 100644 --- a/components/tasks-list-client.tsx +++ b/components/tasks-list-client.tsx @@ -424,7 +424,7 @@ export function TasksListClient({ user, authProvider, initialStars = 1056 }: Tas {task.prStatus && (
- +
)} From 4bacaabd2c61c9629112d83b8501b2547fe42588 Mon Sep 17 00:00:00 2001 From: Chris Tate Date: Sat, 25 Oct 2025 15:13:55 +0000 Subject: [PATCH 5/6] Prioritize failed over in progress in PRCheckStatus --- components/pr-check-status.tsx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/components/pr-check-status.tsx b/components/pr-check-status.tsx index f51b2358..9fd0ac3d 100644 --- a/components/pr-check-status.tsx +++ b/components/pr-check-status.tsx @@ -66,18 +66,19 @@ export function PRCheckStatus({ taskId, prStatus, isActive = false, className = const bgColor = isActive ? 'bg-accent' : 'bg-card' // Render the appropriate indicator - if (hasInProgress) { + // Note: Check failed first to ensure failures are always visible, even if other checks are in progress + if (hasFailed) { return (
-
+
) } - if (hasFailed) { + if (hasInProgress) { return (
-
+
) } From d41d4674b0e06f57f7303dc884bf6af4906b8cce Mon Sep 17 00:00:00 2001 From: Chris Tate Date: Sat, 25 Oct 2025 15:15:35 +0000 Subject: [PATCH 6/6] Make the dot blue when the check is natural --- components/pr-check-status.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/components/pr-check-status.tsx b/components/pr-check-status.tsx index 9fd0ac3d..ba15d791 100644 --- a/components/pr-check-status.tsx +++ b/components/pr-check-status.tsx @@ -60,6 +60,7 @@ export function PRCheckStatus({ taskId, prStatus, isActive = false, className = // Determine overall status const hasInProgress = checkRuns.some((run) => run.status === 'in_progress' || run.status === 'queued') const hasFailed = checkRuns.some((run) => run.conclusion === 'failure' || run.conclusion === 'cancelled') + const hasNeutral = checkRuns.some((run) => run.conclusion === 'neutral') const allPassed = checkRuns.every((run) => run.status === 'completed' && run.conclusion === 'success') // Determine background color based on active state @@ -83,6 +84,14 @@ export function PRCheckStatus({ taskId, prStatus, isActive = false, className = ) } + if (hasNeutral) { + return ( +
+
+
+ ) + } + if (allPassed) { return (