From 832d10e133f7cf83e2d5321740f757c14601b2d4 Mon Sep 17 00:00:00 2001
From: webdevcody
Date: Sat, 17 Jan 2026 17:58:16 -0500
Subject: [PATCH 1/3] refactor: replace Loader2 with Spinner component across
the application
This update standardizes the loading indicators by replacing all instances of Loader2 with the new Spinner component. The Spinner component provides a consistent look and feel for loading states throughout the UI, enhancing the user experience.
Changes include:
- Updated loading indicators in various components such as popovers, modals, and views.
- Ensured that the Spinner component is used with appropriate sizes for different contexts.
No functional changes were made; this is purely a visual and structural improvement.
---
.../src/components/claude-usage-popover.tsx | 3 +-
.../ui/src/components/codex-usage-popover.tsx | 3 +-
.../dialogs/board-background-modal.tsx | 7 ++--
.../components/dialogs/new-project-modal.tsx | 14 ++-----
.../dialogs/workspace-picker-modal.tsx | 5 ++-
.../sidebar/components/sidebar-navigation.tsx | 7 ++--
apps/ui/src/components/session-manager.tsx | 4 +-
apps/ui/src/components/ui/button.tsx | 4 +-
.../ui/description-image-dropzone.tsx | 5 ++-
.../components/ui/feature-image-upload.tsx | 5 ++-
apps/ui/src/components/ui/git-diff-panel.tsx | 4 +-
apps/ui/src/components/ui/image-drop-zone.tsx | 5 ++-
apps/ui/src/components/ui/loading-state.tsx | 10 ++---
apps/ui/src/components/ui/log-viewer.tsx | 4 +-
apps/ui/src/components/ui/spinner.tsx | 32 +++++++++++++++
.../src/components/ui/task-progress-panel.tsx | 5 ++-
apps/ui/src/components/usage-popover.tsx | 5 ++-
.../src/components/views/agent-tools-view.tsx | 8 ++--
.../components/thinking-indicator.tsx | 16 +-------
.../ui/src/components/views/analysis-view.tsx | 10 ++---
apps/ui/src/components/views/board-view.tsx | 4 +-
.../views/board-view/board-search-bar.tsx | 5 ++-
.../kanban-card/agent-info-panel.tsx | 14 ++-----
.../components/kanban-card/card-header.tsx | 6 +--
.../board-view/dialogs/agent-output-modal.tsx | 9 +++--
.../dialogs/backlog-plan-dialog.tsx | 19 +++------
.../dialogs/commit-worktree-dialog.tsx | 5 ++-
.../dialogs/create-branch-dialog.tsx | 5 ++-
.../board-view/dialogs/create-pr-dialog.tsx | 5 ++-
.../dialogs/create-worktree-dialog.tsx | 5 ++-
.../dialogs/delete-worktree-dialog.tsx | 5 ++-
.../dialogs/merge-worktree-dialog.tsx | 5 ++-
.../dialogs/plan-approval-dialog.tsx | 7 ++--
.../board-view/init-script-indicator.tsx | 5 ++-
.../views/board-view/mobile-usage-bar.tsx | 9 +++--
.../board-view/shared/model-selector.tsx | 4 +-
.../shared/planning-mode-selector.tsx | 4 +-
.../components/branch-switch-dropdown.tsx | 5 ++-
.../components/dev-server-logs-panel.tsx | 8 ++--
.../components/worktree-mobile-dropdown.tsx | 7 ++--
.../components/worktree-tab.tsx | 11 ++---
.../worktree-panel/worktree-panel.tsx | 5 ++-
apps/ui/src/components/views/code-view.tsx | 5 ++-
apps/ui/src/components/views/context-view.tsx | 11 +++--
.../src/components/views/dashboard-view.tsx | 4 +-
.../components/issue-detail-panel.tsx | 10 ++---
.../components/issue-row.tsx | 4 +-
.../components/issues-list-header.tsx | 3 +-
.../src/components/views/github-prs-view.tsx | 7 ++--
.../src/components/views/graph-view-page.tsx | 4 +-
.../components/ideation-dashboard.tsx | 11 ++---
.../components/prompt-category-grid.tsx | 4 +-
.../ideation-view/components/prompt-list.tsx | 7 ++--
.../components/views/ideation-view/index.tsx | 9 ++---
.../src/components/views/interview-view.tsx | 7 ++--
apps/ui/src/components/views/login-view.tsx | 9 +++--
apps/ui/src/components/views/memory-view.tsx | 3 +-
.../components/views/notifications-view.tsx | 5 ++-
.../worktree-preferences-section.tsx | 16 ++------
.../components/views/running-agents-view.tsx | 11 +++--
.../settings-view/account/account-section.tsx | 3 +-
.../settings-view/api-keys/api-key-field.tsx | 5 ++-
.../api-keys/api-keys-section.tsx | 7 ++--
.../api-keys/claude-usage-section.tsx | 3 +-
.../cli-status/claude-cli-status.tsx | 3 +-
.../cli-status/cli-status-card.tsx | 3 +-
.../cli-status/codex-cli-status.tsx | 3 +-
.../cli-status/cursor-cli-status.tsx | 3 +-
.../cli-status/opencode-cli-status.tsx | 3 +-
.../codex/codex-usage-section.tsx | 3 +-
.../event-hooks/event-history-view.tsx | 7 +++-
.../components/mcp-server-card.tsx | 5 ++-
.../components/mcp-server-header.tsx | 3 +-
.../views/settings-view/mcp-servers/utils.tsx | 5 ++-
.../claude-settings-tab/subagents-section.tsx | 18 ++-------
.../providers/cursor-permissions-section.tsx | 3 +-
.../opencode-model-configuration.tsx | 5 ++-
.../components/cli-installation-card.tsx | 5 ++-
.../setup-view/components/status-badge.tsx | 5 ++-
.../setup-view/steps/claude-setup-step.tsx | 22 +++++-----
.../views/setup-view/steps/cli-setup-step.tsx | 22 +++++-----
.../setup-view/steps/cursor-setup-step.tsx | 8 ++--
.../setup-view/steps/github-setup-step.tsx | 6 +--
.../setup-view/steps/opencode-setup-step.tsx | 8 ++--
.../setup-view/steps/providers-setup-step.tsx | 40 +++++++++----------
apps/ui/src/components/views/spec-view.tsx | 4 +-
.../spec-view/components/spec-empty-state.tsx | 7 ++--
.../spec-view/components/spec-header.tsx | 9 +++--
.../spec-view/dialogs/create-spec-dialog.tsx | 5 ++-
.../dialogs/regenerate-spec-dialog.tsx | 5 ++-
.../ui/src/components/views/terminal-view.tsx | 6 +--
.../views/terminal-view/terminal-panel.tsx | 6 +--
apps/ui/src/components/views/welcome-view.tsx | 6 +--
93 files changed, 351 insertions(+), 333 deletions(-)
create mode 100644 apps/ui/src/components/ui/spinner.tsx
diff --git a/apps/ui/src/components/claude-usage-popover.tsx b/apps/ui/src/components/claude-usage-popover.tsx
index d51e316c..fa3d5c94 100644
--- a/apps/ui/src/components/claude-usage-popover.tsx
+++ b/apps/ui/src/components/claude-usage-popover.tsx
@@ -2,6 +2,7 @@ import { useState, useEffect, useMemo, useCallback } from 'react';
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
import { Button } from '@/components/ui/button';
import { RefreshCw, AlertTriangle, CheckCircle, XCircle, Clock, ExternalLink } from 'lucide-react';
+import { Spinner } from '@/components/ui/spinner';
import { cn } from '@/lib/utils';
import { getElectronAPI } from '@/lib/electron';
import { useAppStore } from '@/store/app-store';
@@ -279,7 +280,7 @@ export function ClaudeUsagePopover() {
) : !claudeUsage ? (
// Loading state
-
+
Loading usage data...
) : (
diff --git a/apps/ui/src/components/codex-usage-popover.tsx b/apps/ui/src/components/codex-usage-popover.tsx
index f6005b6a..0fee4226 100644
--- a/apps/ui/src/components/codex-usage-popover.tsx
+++ b/apps/ui/src/components/codex-usage-popover.tsx
@@ -2,6 +2,7 @@ import { useState, useEffect, useMemo, useCallback } from 'react';
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
import { Button } from '@/components/ui/button';
import { RefreshCw, AlertTriangle, CheckCircle, XCircle, Clock, ExternalLink } from 'lucide-react';
+import { Spinner } from '@/components/ui/spinner';
import { cn } from '@/lib/utils';
import { getElectronAPI } from '@/lib/electron';
import { useAppStore } from '@/store/app-store';
@@ -333,7 +334,7 @@ export function CodexUsagePopover() {
) : !codexUsage ? (
// Loading state
-
+
Loading usage data...
) : codexUsage.rateLimits ? (
diff --git a/apps/ui/src/components/dialogs/board-background-modal.tsx b/apps/ui/src/components/dialogs/board-background-modal.tsx
index 89ab44da..e381c366 100644
--- a/apps/ui/src/components/dialogs/board-background-modal.tsx
+++ b/apps/ui/src/components/dialogs/board-background-modal.tsx
@@ -1,6 +1,7 @@
import { useState, useRef, useCallback, useEffect } from 'react';
import { createLogger } from '@automaker/utils/logger';
-import { ImageIcon, Upload, Loader2, Trash2 } from 'lucide-react';
+import { ImageIcon, Upload, Trash2 } from 'lucide-react';
+import { Spinner } from '@/components/ui/spinner';
const logger = createLogger('BoardBackgroundModal');
import {
@@ -313,7 +314,7 @@ export function BoardBackgroundModal({ open, onOpenChange }: BoardBackgroundModa
/>
{isProcessing && (
-
+
)}
@@ -353,7 +354,7 @@ export function BoardBackgroundModal({ open, onOpenChange }: BoardBackgroundModa
)}
>
{isProcessing ? (
-
+
) : (
)}
diff --git a/apps/ui/src/components/dialogs/new-project-modal.tsx b/apps/ui/src/components/dialogs/new-project-modal.tsx
index dd114bf9..55df0a1c 100644
--- a/apps/ui/src/components/dialogs/new-project-modal.tsx
+++ b/apps/ui/src/components/dialogs/new-project-modal.tsx
@@ -14,16 +14,8 @@ import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Badge } from '@/components/ui/badge';
-import {
- FolderPlus,
- FolderOpen,
- Rocket,
- ExternalLink,
- Check,
- Loader2,
- Link,
- Folder,
-} from 'lucide-react';
+import { FolderPlus, FolderOpen, Rocket, ExternalLink, Check, Link, Folder } from 'lucide-react';
+import { Spinner } from '@/components/ui/spinner';
import { starterTemplates, type StarterTemplate } from '@/lib/templates';
import { getElectronAPI } from '@/lib/electron';
import { cn } from '@/lib/utils';
@@ -451,7 +443,7 @@ export function NewProjectModal({
>
{isCreating ? (
<>
-
+
{activeTab === 'template' ? 'Cloning...' : 'Creating...'}
>
) : (
diff --git a/apps/ui/src/components/dialogs/workspace-picker-modal.tsx b/apps/ui/src/components/dialogs/workspace-picker-modal.tsx
index 4f287465..84e723fc 100644
--- a/apps/ui/src/components/dialogs/workspace-picker-modal.tsx
+++ b/apps/ui/src/components/dialogs/workspace-picker-modal.tsx
@@ -8,7 +8,8 @@ import {
DialogTitle,
} from '@/components/ui/dialog';
import { Button } from '@/components/ui/button';
-import { Folder, Loader2, FolderOpen, AlertCircle } from 'lucide-react';
+import { Folder, FolderOpen, AlertCircle } from 'lucide-react';
+import { Spinner } from '@/components/ui/spinner';
import { getHttpApiClient } from '@/lib/http-api-client';
interface WorkspaceDirectory {
@@ -74,7 +75,7 @@ export function WorkspacePickerModal({ open, onOpenChange, onSelect }: Workspace
{isLoading && (
)}
diff --git a/apps/ui/src/components/layout/sidebar/components/sidebar-navigation.tsx b/apps/ui/src/components/layout/sidebar/components/sidebar-navigation.tsx
index 3cda8229..c4956159 100644
--- a/apps/ui/src/components/layout/sidebar/components/sidebar-navigation.tsx
+++ b/apps/ui/src/components/layout/sidebar/components/sidebar-navigation.tsx
@@ -1,9 +1,9 @@
import type { NavigateOptions } from '@tanstack/react-router';
-import { Loader2 } from 'lucide-react';
import { cn } from '@/lib/utils';
import { formatShortcut } from '@/store/app-store';
import type { NavSection } from '../types';
import type { Project } from '@/lib/electron';
+import { Spinner } from '@/components/ui/spinner';
interface SidebarNavigationProps {
currentProject: Project | null;
@@ -93,9 +93,10 @@ export function SidebarNavigation({
>
{item.isLoading ? (
-
diff --git a/apps/ui/src/components/session-manager.tsx b/apps/ui/src/components/session-manager.tsx
index 88c31acc..f0fa9a45 100644
--- a/apps/ui/src/components/session-manager.tsx
+++ b/apps/ui/src/components/session-manager.tsx
@@ -16,8 +16,8 @@ import {
Check,
X,
ArchiveRestore,
- Loader2,
} from 'lucide-react';
+import { Spinner } from '@/components/ui/spinner';
import { cn } from '@/lib/utils';
import type { SessionListItem } from '@/types/electron';
import { useKeyboardShortcutsConfig } from '@/hooks/use-keyboard-shortcuts';
@@ -466,7 +466,7 @@ export function SessionManager({
{/* Show loading indicator if this session is running (either current session thinking or any session in runningSessions) */}
{(currentSessionId === session.id && isCurrentSessionThinking) ||
runningSessions.has(session.id) ? (
-
+
) : (
)}
diff --git a/apps/ui/src/components/ui/button.tsx b/apps/ui/src/components/ui/button.tsx
index fa970a52..a7163ed3 100644
--- a/apps/ui/src/components/ui/button.tsx
+++ b/apps/ui/src/components/ui/button.tsx
@@ -1,9 +1,9 @@
import * as React from 'react';
import { Slot } from '@radix-ui/react-slot';
import { cva, type VariantProps } from 'class-variance-authority';
-import { Loader2 } from 'lucide-react';
import { cn } from '@/lib/utils';
+import { Spinner } from '@/components/ui/spinner';
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all duration-200 cursor-pointer disabled:pointer-events-none disabled:opacity-50 disabled:cursor-not-allowed [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive active:scale-[0.98]",
@@ -39,7 +39,7 @@ const buttonVariants = cva(
// Loading spinner component
function ButtonSpinner({ className }: { className?: string }) {
- return
;
+ return
;
}
function Button({
diff --git a/apps/ui/src/components/ui/description-image-dropzone.tsx b/apps/ui/src/components/ui/description-image-dropzone.tsx
index 42b2d588..7b67fd9b 100644
--- a/apps/ui/src/components/ui/description-image-dropzone.tsx
+++ b/apps/ui/src/components/ui/description-image-dropzone.tsx
@@ -3,7 +3,8 @@ import { createLogger } from '@automaker/utils/logger';
import { cn } from '@/lib/utils';
const logger = createLogger('DescriptionImageDropZone');
-import { ImageIcon, X, Loader2, FileText } from 'lucide-react';
+import { ImageIcon, X, FileText } from 'lucide-react';
+import { Spinner } from '@/components/ui/spinner';
import { Textarea } from '@/components/ui/textarea';
import { getElectronAPI } from '@/lib/electron';
import { getAuthenticatedImageUrl } from '@/lib/api-fetch';
@@ -431,7 +432,7 @@ export function DescriptionImageDropZone({
{/* Processing indicator */}
{isProcessing && (
-
+
Processing files...
)}
diff --git a/apps/ui/src/components/ui/feature-image-upload.tsx b/apps/ui/src/components/ui/feature-image-upload.tsx
index ec4ef205..23837cc1 100644
--- a/apps/ui/src/components/ui/feature-image-upload.tsx
+++ b/apps/ui/src/components/ui/feature-image-upload.tsx
@@ -3,7 +3,8 @@ import { createLogger } from '@automaker/utils/logger';
import { cn } from '@/lib/utils';
const logger = createLogger('FeatureImageUpload');
-import { ImageIcon, X, Upload } from 'lucide-react';
+import { ImageIcon, X } from 'lucide-react';
+import { Spinner } from '@/components/ui/spinner';
import {
fileToBase64,
generateImageId,
@@ -196,7 +197,7 @@ export function FeatureImageUpload({
)}
>
{isProcessing ? (
-
+
) : (
)}
diff --git a/apps/ui/src/components/ui/git-diff-panel.tsx b/apps/ui/src/components/ui/git-diff-panel.tsx
index 803ff46c..e0e51ea0 100644
--- a/apps/ui/src/components/ui/git-diff-panel.tsx
+++ b/apps/ui/src/components/ui/git-diff-panel.tsx
@@ -9,11 +9,11 @@ import {
FilePen,
ChevronDown,
ChevronRight,
- Loader2,
RefreshCw,
GitBranch,
AlertCircle,
} from 'lucide-react';
+import { Spinner } from '@/components/ui/spinner';
import { Button } from './button';
import type { FileStatus } from '@/types/electron';
@@ -484,7 +484,7 @@ export function GitDiffPanel({
{isLoading ? (
-
+
Loading changes...
) : error ? (
diff --git a/apps/ui/src/components/ui/image-drop-zone.tsx b/apps/ui/src/components/ui/image-drop-zone.tsx
index cdd7b396..dcaf892d 100644
--- a/apps/ui/src/components/ui/image-drop-zone.tsx
+++ b/apps/ui/src/components/ui/image-drop-zone.tsx
@@ -3,7 +3,8 @@ import { createLogger } from '@automaker/utils/logger';
import { cn } from '@/lib/utils';
const logger = createLogger('ImageDropZone');
-import { ImageIcon, X, Upload } from 'lucide-react';
+import { ImageIcon, X } from 'lucide-react';
+import { Spinner } from '@/components/ui/spinner';
import type { ImageAttachment } from '@/store/app-store';
import {
fileToBase64,
@@ -204,7 +205,7 @@ export function ImageDropZone({
)}
>
{isProcessing ? (
-
+
) : (
)}
diff --git a/apps/ui/src/components/ui/loading-state.tsx b/apps/ui/src/components/ui/loading-state.tsx
index 9ae6ff3b..60695e4c 100644
--- a/apps/ui/src/components/ui/loading-state.tsx
+++ b/apps/ui/src/components/ui/loading-state.tsx
@@ -1,17 +1,15 @@
-import { Loader2 } from 'lucide-react';
+import { Spinner } from '@/components/ui/spinner';
interface LoadingStateProps {
/** Optional custom message to display below the spinner */
message?: string;
- /** Optional custom size class for the spinner (default: h-8 w-8) */
- size?: string;
}
-export function LoadingState({ message, size = 'h-8 w-8' }: LoadingStateProps) {
+export function LoadingState({ message }: LoadingStateProps) {
return (
-
- {message &&
{message}
}
+
+ {message &&
{message}
}
);
}
diff --git a/apps/ui/src/components/ui/log-viewer.tsx b/apps/ui/src/components/ui/log-viewer.tsx
index 1d14a14e..65426f8b 100644
--- a/apps/ui/src/components/ui/log-viewer.tsx
+++ b/apps/ui/src/components/ui/log-viewer.tsx
@@ -22,8 +22,8 @@ import {
Filter,
Circle,
Play,
- Loader2,
} from 'lucide-react';
+import { Spinner } from '@/components/ui/spinner';
import { cn } from '@/lib/utils';
import {
parseLogOutput,
@@ -148,7 +148,7 @@ function TodoListRenderer({ todos }: { todos: TodoItem[] }) {
case 'completed':
return
;
case 'in_progress':
- return
;
+ return
;
case 'pending':
return
;
default:
diff --git a/apps/ui/src/components/ui/spinner.tsx b/apps/ui/src/components/ui/spinner.tsx
new file mode 100644
index 00000000..c66b7684
--- /dev/null
+++ b/apps/ui/src/components/ui/spinner.tsx
@@ -0,0 +1,32 @@
+import { Loader2 } from 'lucide-react';
+import { cn } from '@/lib/utils';
+
+type SpinnerSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
+
+const sizeClasses: Record
= {
+ xs: 'h-3 w-3',
+ sm: 'h-4 w-4',
+ md: 'h-5 w-5',
+ lg: 'h-6 w-6',
+ xl: 'h-8 w-8',
+};
+
+interface SpinnerProps {
+ /** Size of the spinner */
+ size?: SpinnerSize;
+ /** Additional class names */
+ className?: string;
+}
+
+/**
+ * Themed spinner component using the primary brand color.
+ * Use this for all loading indicators throughout the app for consistency.
+ */
+export function Spinner({ size = 'md', className }: SpinnerProps) {
+ return (
+
+ );
+}
diff --git a/apps/ui/src/components/ui/task-progress-panel.tsx b/apps/ui/src/components/ui/task-progress-panel.tsx
index 414be1e7..4fecefbc 100644
--- a/apps/ui/src/components/ui/task-progress-panel.tsx
+++ b/apps/ui/src/components/ui/task-progress-panel.tsx
@@ -5,7 +5,8 @@ import { createLogger } from '@automaker/utils/logger';
import { cn } from '@/lib/utils';
const logger = createLogger('TaskProgressPanel');
-import { Check, Loader2, Circle, ChevronDown, ChevronRight, FileCode } from 'lucide-react';
+import { Check, Circle, ChevronDown, ChevronRight, FileCode } from 'lucide-react';
+import { Spinner } from '@/components/ui/spinner';
import { getElectronAPI } from '@/lib/electron';
import type { AutoModeEvent } from '@/types/electron';
import { Badge } from '@/components/ui/badge';
@@ -260,7 +261,7 @@ export function TaskProgressPanel({
)}
>
{isCompleted && }
- {isActive && }
+ {isActive && }
{isPending && }
diff --git a/apps/ui/src/components/usage-popover.tsx b/apps/ui/src/components/usage-popover.tsx
index ac15a519..26c100ce 100644
--- a/apps/ui/src/components/usage-popover.tsx
+++ b/apps/ui/src/components/usage-popover.tsx
@@ -3,6 +3,7 @@ import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover
import { Button } from '@/components/ui/button';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { RefreshCw, AlertTriangle, CheckCircle, XCircle, Clock, ExternalLink } from 'lucide-react';
+import { Spinner } from '@/components/ui/spinner';
import { cn } from '@/lib/utils';
import { getElectronAPI } from '@/lib/electron';
import { useAppStore } from '@/store/app-store';
@@ -449,7 +450,7 @@ export function UsagePopover() {
) : !claudeUsage ? (
-
+
Loading usage data...
) : (
@@ -568,7 +569,7 @@ export function UsagePopover() {
) : !codexUsage ? (
-
+
Loading usage data...
) : codexUsage.rateLimits ? (
diff --git a/apps/ui/src/components/views/agent-tools-view.tsx b/apps/ui/src/components/views/agent-tools-view.tsx
index 4485f165..48c3f92d 100644
--- a/apps/ui/src/components/views/agent-tools-view.tsx
+++ b/apps/ui/src/components/views/agent-tools-view.tsx
@@ -11,12 +11,12 @@ import {
Terminal,
CheckCircle,
XCircle,
- Loader2,
Play,
File,
Pencil,
Wrench,
} from 'lucide-react';
+import { Spinner } from '@/components/ui/spinner';
import { cn } from '@/lib/utils';
import { getElectronAPI } from '@/lib/electron';
@@ -236,7 +236,7 @@ export function AgentToolsView() {
>
{isReadingFile ? (
<>
-
+
Reading...
>
) : (
@@ -315,7 +315,7 @@ export function AgentToolsView() {
>
{isWritingFile ? (
<>
-
+
Writing...
>
) : (
@@ -383,7 +383,7 @@ export function AgentToolsView() {
>
{isRunningCommand ? (
<>
-
+
Running...
>
) : (
diff --git a/apps/ui/src/components/views/agent-view/components/thinking-indicator.tsx b/apps/ui/src/components/views/agent-view/components/thinking-indicator.tsx
index facd4fc5..ff2965d5 100644
--- a/apps/ui/src/components/views/agent-view/components/thinking-indicator.tsx
+++ b/apps/ui/src/components/views/agent-view/components/thinking-indicator.tsx
@@ -1,4 +1,5 @@
import { Bot } from 'lucide-react';
+import { Spinner } from '@/components/ui/spinner';
export function ThinkingIndicator() {
return (
@@ -8,20 +9,7 @@ export function ThinkingIndicator() {
diff --git a/apps/ui/src/components/views/analysis-view.tsx b/apps/ui/src/components/views/analysis-view.tsx
index e235a9e9..2143d390 100644
--- a/apps/ui/src/components/views/analysis-view.tsx
+++ b/apps/ui/src/components/views/analysis-view.tsx
@@ -14,12 +14,12 @@ import {
RefreshCw,
BarChart3,
FileCode,
- Loader2,
FileText,
CheckCircle,
AlertCircle,
ListChecks,
} from 'lucide-react';
+import { Spinner } from '@/components/ui/spinner';
import { cn, generateUUID } from '@/lib/utils';
const logger = createLogger('AnalysisView');
@@ -742,7 +742,7 @@ ${Object.entries(projectAnalysis.filesByExtension)
{description}
diff --git a/apps/ui/src/components/views/settings-view/cli-status/codex-cli-status.tsx b/apps/ui/src/components/views/settings-view/cli-status/codex-cli-status.tsx
index 86635264..3e0d8b53 100644
--- a/apps/ui/src/components/views/settings-view/cli-status/codex-cli-status.tsx
+++ b/apps/ui/src/components/views/settings-view/cli-status/codex-cli-status.tsx
@@ -1,5 +1,6 @@
import { useState, useCallback } from 'react';
import { Button } from '@/components/ui/button';
+import { Spinner } from '@/components/ui/spinner';
import { CheckCircle2, AlertCircle, RefreshCw, XCircle } from 'lucide-react';
import { cn } from '@/lib/utils';
import type { CliStatus } from '../shared/types';
@@ -165,7 +166,7 @@ export function CodexCliStatus({ status, authStatus, isChecking, onRefresh }: Cl
'transition-all duration-200'
)}
>
-
+ {isChecking ? : }
diff --git a/apps/ui/src/components/views/settings-view/cli-status/cursor-cli-status.tsx b/apps/ui/src/components/views/settings-view/cli-status/cursor-cli-status.tsx
index bc49270c..68c052fb 100644
--- a/apps/ui/src/components/views/settings-view/cli-status/cursor-cli-status.tsx
+++ b/apps/ui/src/components/views/settings-view/cli-status/cursor-cli-status.tsx
@@ -1,5 +1,6 @@
import { useState, useCallback } from 'react';
import { Button } from '@/components/ui/button';
+import { Spinner } from '@/components/ui/spinner';
import { CheckCircle2, AlertCircle, RefreshCw, XCircle } from 'lucide-react';
import { cn } from '@/lib/utils';
import { CursorIcon } from '@/components/ui/provider-icon';
@@ -290,7 +291,7 @@ export function CursorCliStatus({ status, isChecking, onRefresh }: CursorCliStat
'transition-all duration-200'
)}
>
-
+ {isChecking ? : }
diff --git a/apps/ui/src/components/views/settings-view/cli-status/opencode-cli-status.tsx b/apps/ui/src/components/views/settings-view/cli-status/opencode-cli-status.tsx
index bfd9efe6..7d7577c5 100644
--- a/apps/ui/src/components/views/settings-view/cli-status/opencode-cli-status.tsx
+++ b/apps/ui/src/components/views/settings-view/cli-status/opencode-cli-status.tsx
@@ -1,5 +1,6 @@
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
+import { Spinner } from '@/components/ui/spinner';
import { CheckCircle2, AlertCircle, RefreshCw, Bot, Cloud } from 'lucide-react';
import { cn } from '@/lib/utils';
import type { CliStatus } from '../shared/types';
@@ -221,7 +222,7 @@ export function OpencodeCliStatus({
'transition-all duration-200'
)}
>
-
+ {isChecking ? : }
diff --git a/apps/ui/src/components/views/settings-view/codex/codex-usage-section.tsx b/apps/ui/src/components/views/settings-view/codex/codex-usage-section.tsx
index b879df4a..9012047d 100644
--- a/apps/ui/src/components/views/settings-view/codex/codex-usage-section.tsx
+++ b/apps/ui/src/components/views/settings-view/codex/codex-usage-section.tsx
@@ -1,6 +1,7 @@
// @ts-nocheck
import { useCallback, useEffect, useState } from 'react';
import { Button } from '@/components/ui/button';
+import { Spinner } from '@/components/ui/spinner';
import { RefreshCw, AlertCircle } from 'lucide-react';
import { OpenAIIcon } from '@/components/ui/provider-icon';
import { cn } from '@/lib/utils';
@@ -168,7 +169,7 @@ export function CodexUsageSection() {
data-testid="refresh-codex-usage"
title={CODEX_REFRESH_LABEL}
>
-
+ {isLoading ? : }
{CODEX_USAGE_SUBTITLE}
diff --git a/apps/ui/src/components/views/settings-view/event-hooks/event-history-view.tsx b/apps/ui/src/components/views/settings-view/event-hooks/event-history-view.tsx
index 780f5f98..e9c5a071 100644
--- a/apps/ui/src/components/views/settings-view/event-hooks/event-history-view.tsx
+++ b/apps/ui/src/components/views/settings-view/event-hooks/event-history-view.tsx
@@ -1,5 +1,6 @@
import { useState, useEffect, useCallback } from 'react';
import { Button } from '@/components/ui/button';
+import { Spinner } from '@/components/ui/spinner';
import { cn } from '@/lib/utils';
import {
History,
@@ -184,7 +185,11 @@ export function EventHistoryView() {
-
+ {loading ? (
+
+ ) : (
+
+ )}
Refresh
{events.length > 0 && (
diff --git a/apps/ui/src/components/views/settings-view/mcp-servers/components/mcp-server-card.tsx b/apps/ui/src/components/views/settings-view/mcp-servers/components/mcp-server-card.tsx
index babf4bda..752b06e7 100644
--- a/apps/ui/src/components/views/settings-view/mcp-servers/components/mcp-server-card.tsx
+++ b/apps/ui/src/components/views/settings-view/mcp-servers/components/mcp-server-card.tsx
@@ -1,4 +1,5 @@
-import { ChevronDown, ChevronRight, Code, Pencil, Trash2, PlayCircle, Loader2 } from 'lucide-react';
+import { ChevronDown, ChevronRight, Code, Pencil, Trash2, PlayCircle } from 'lucide-react';
+import { Spinner } from '@/components/ui/spinner';
import { Button } from '@/components/ui/button';
import { Switch } from '@/components/ui/switch';
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible';
@@ -111,7 +112,7 @@ export function MCPServerCard({
className="h-8 px-2"
>
{testState?.status === 'testing' ? (
-
+
) : (
)}
diff --git a/apps/ui/src/components/views/settings-view/mcp-servers/components/mcp-server-header.tsx b/apps/ui/src/components/views/settings-view/mcp-servers/components/mcp-server-header.tsx
index a85fc305..8caf3bca 100644
--- a/apps/ui/src/components/views/settings-view/mcp-servers/components/mcp-server-header.tsx
+++ b/apps/ui/src/components/views/settings-view/mcp-servers/components/mcp-server-header.tsx
@@ -1,5 +1,6 @@
import { Plug, RefreshCw, Download, Code, FileJson, Plus } from 'lucide-react';
import { Button } from '@/components/ui/button';
+import { Spinner } from '@/components/ui/spinner';
import { cn } from '@/lib/utils';
interface MCPServerHeaderProps {
@@ -43,7 +44,7 @@ export function MCPServerHeader({
disabled={isRefreshing}
data-testid="refresh-mcp-servers-button"
>
-
+ {isRefreshing ?
:
}
{hasServers && (
<>
diff --git a/apps/ui/src/components/views/settings-view/mcp-servers/utils.tsx b/apps/ui/src/components/views/settings-view/mcp-servers/utils.tsx
index 25102025..83687556 100644
--- a/apps/ui/src/components/views/settings-view/mcp-servers/utils.tsx
+++ b/apps/ui/src/components/views/settings-view/mcp-servers/utils.tsx
@@ -1,4 +1,5 @@
-import { Terminal, Globe, Loader2, CheckCircle2, XCircle } from 'lucide-react';
+import { Terminal, Globe, CheckCircle2, XCircle } from 'lucide-react';
+import { Spinner } from '@/components/ui/spinner';
import type { ServerType, ServerTestState } from './types';
import { SENSITIVE_PARAM_PATTERNS } from './constants';
@@ -40,7 +41,7 @@ export function getServerIcon(type: ServerType = 'stdio') {
export function getTestStatusIcon(status: ServerTestState['status']) {
switch (status) {
case 'testing':
- return
;
+ return
;
case 'success':
return
;
case 'error':
diff --git a/apps/ui/src/components/views/settings-view/providers/claude-settings-tab/subagents-section.tsx b/apps/ui/src/components/views/settings-view/providers/claude-settings-tab/subagents-section.tsx
index 08800331..d1f1bf76 100644
--- a/apps/ui/src/components/views/settings-view/providers/claude-settings-tab/subagents-section.tsx
+++ b/apps/ui/src/components/views/settings-view/providers/claude-settings-tab/subagents-section.tsx
@@ -14,16 +14,8 @@ import { Label } from '@/components/ui/label';
import { Switch } from '@/components/ui/switch';
import { Checkbox } from '@/components/ui/checkbox';
import { cn } from '@/lib/utils';
-import {
- Bot,
- RefreshCw,
- Loader2,
- Users,
- ExternalLink,
- Globe,
- FolderOpen,
- Sparkles,
-} from 'lucide-react';
+import { Bot, RefreshCw, Users, ExternalLink, Globe, FolderOpen, Sparkles } from 'lucide-react';
+import { Spinner } from '@/components/ui/spinner';
import { useSubagents } from './hooks/use-subagents';
import { useSubagentsSettings } from './hooks/use-subagents-settings';
import { SubagentCard } from './subagent-card';
@@ -178,11 +170,7 @@ export function SubagentsSection() {
title="Refresh agents from disk"
className="gap-1.5 h-7 px-2 text-xs"
>
- {isLoadingAgents ? (
-
- ) : (
-
- )}
+ {isLoadingAgents ?
:
}
Refresh
diff --git a/apps/ui/src/components/views/settings-view/providers/cursor-permissions-section.tsx b/apps/ui/src/components/views/settings-view/providers/cursor-permissions-section.tsx
index 29be25b3..133913b9 100644
--- a/apps/ui/src/components/views/settings-view/providers/cursor-permissions-section.tsx
+++ b/apps/ui/src/components/views/settings-view/providers/cursor-permissions-section.tsx
@@ -4,6 +4,7 @@ import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible';
import { Shield, ShieldCheck, ShieldAlert, ChevronDown, Copy, Check } from 'lucide-react';
+import { Spinner } from '@/components/ui/spinner';
import { cn } from '@/lib/utils';
import type { CursorStatus } from '../hooks/use-cursor-status';
import type { PermissionsData } from '../hooks/use-cursor-permissions';
@@ -118,7 +119,7 @@ export function CursorPermissionsSection({
{isLoadingPermissions ? (
) : (
<>
diff --git a/apps/ui/src/components/views/settings-view/providers/opencode-model-configuration.tsx b/apps/ui/src/components/views/settings-view/providers/opencode-model-configuration.tsx
index 3d2d0fb6..6ecce79c 100644
--- a/apps/ui/src/components/views/settings-view/providers/opencode-model-configuration.tsx
+++ b/apps/ui/src/components/views/settings-view/providers/opencode-model-configuration.tsx
@@ -9,7 +9,8 @@ import {
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
-import { Terminal, Cloud, Cpu, Brain, Github, Loader2, KeyRound, ShieldCheck } from 'lucide-react';
+import { Terminal, Cloud, Cpu, Brain, Github, KeyRound, ShieldCheck } from 'lucide-react';
+import { Spinner } from '@/components/ui/spinner';
import { cn } from '@/lib/utils';
import { Input } from '@/components/ui/input';
import type {
@@ -500,7 +501,7 @@ export function OpencodeModelConfiguration({
{isLoadingDynamicModels && (
-
+
Discovering...
)}
diff --git a/apps/ui/src/components/views/setup-view/components/cli-installation-card.tsx b/apps/ui/src/components/views/setup-view/components/cli-installation-card.tsx
index ee32f231..4932ef29 100644
--- a/apps/ui/src/components/views/setup-view/components/cli-installation-card.tsx
+++ b/apps/ui/src/components/views/setup-view/components/cli-installation-card.tsx
@@ -1,6 +1,7 @@
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
-import { Download, Loader2, AlertCircle } from 'lucide-react';
+import { Download, AlertCircle } from 'lucide-react';
+import { Spinner } from '@/components/ui/spinner';
import { CopyableCommandField } from './copyable-command-field';
import { TerminalOutput } from './terminal-output';
@@ -59,7 +60,7 @@ export function CliInstallationCard({
>
{isInstalling ? (
<>
-
+
Installing...
>
) : (
diff --git a/apps/ui/src/components/views/setup-view/components/status-badge.tsx b/apps/ui/src/components/views/setup-view/components/status-badge.tsx
index 38692a0b..53869d07 100644
--- a/apps/ui/src/components/views/setup-view/components/status-badge.tsx
+++ b/apps/ui/src/components/views/setup-view/components/status-badge.tsx
@@ -1,4 +1,5 @@
-import { CheckCircle2, XCircle, Loader2, AlertCircle } from 'lucide-react';
+import { CheckCircle2, XCircle, AlertCircle } from 'lucide-react';
+import { Spinner } from '@/components/ui/spinner';
interface StatusBadgeProps {
status:
@@ -34,7 +35,7 @@ export function StatusBadge({ status, label }: StatusBadgeProps) {
};
case 'checking':
return {
- icon: ,
+ icon: ,
className: 'bg-yellow-500/10 text-yellow-500 border-yellow-500/20',
};
case 'unverified':
diff --git a/apps/ui/src/components/views/setup-view/steps/claude-setup-step.tsx b/apps/ui/src/components/views/setup-view/steps/claude-setup-step.tsx
index 8b56f49c..87bf6f77 100644
--- a/apps/ui/src/components/views/setup-view/steps/claude-setup-step.tsx
+++ b/apps/ui/src/components/views/setup-view/steps/claude-setup-step.tsx
@@ -14,7 +14,6 @@ import { useAppStore } from '@/store/app-store';
import { getElectronAPI } from '@/lib/electron';
import {
CheckCircle2,
- Loader2,
Key,
ArrowRight,
ArrowLeft,
@@ -27,6 +26,7 @@ import {
XCircle,
Trash2,
} from 'lucide-react';
+import { Spinner } from '@/components/ui/spinner';
import { toast } from 'sonner';
import { StatusBadge, TerminalOutput } from '../components';
import { useCliStatus, useCliInstallation, useTokenSave } from '../hooks';
@@ -330,7 +330,7 @@ export function ClaudeSetupStep({ onNext, onBack, onSkip }: ClaudeSetupStepProps
Authentication Methods
-
+ {isChecking ? : }
@@ -412,7 +412,7 @@ export function ClaudeSetupStep({ onNext, onBack, onSkip }: ClaudeSetupStepProps
>
{isInstalling ? (
<>
-
+
Installing...
>
) : (
@@ -435,7 +435,7 @@ export function ClaudeSetupStep({ onNext, onBack, onSkip }: ClaudeSetupStepProps
{/* CLI Verification Status */}
{cliVerificationStatus === 'verifying' && (
-
+
Verifying CLI authentication...
Running a test query
@@ -494,7 +494,7 @@ export function ClaudeSetupStep({ onNext, onBack, onSkip }: ClaudeSetupStepProps
>
{cliVerificationStatus === 'verifying' ? (
<>
-
+
Verifying...
>
) : cliVerificationStatus === 'error' ? (
@@ -574,7 +574,7 @@ export function ClaudeSetupStep({ onNext, onBack, onSkip }: ClaudeSetupStepProps
>
{isSavingApiKey ? (
<>
-
+
Saving...
>
) : (
@@ -589,11 +589,7 @@ export function ClaudeSetupStep({ onNext, onBack, onSkip }: ClaudeSetupStepProps
className="border-red-500/50 text-red-500 hover:bg-red-500/10 hover:text-red-400"
data-testid="delete-anthropic-key-button"
>
- {isDeletingApiKey ? (
-
- ) : (
-
- )}
+ {isDeletingApiKey ?
:
}
)}
@@ -602,7 +598,7 @@ export function ClaudeSetupStep({ onNext, onBack, onSkip }: ClaudeSetupStepProps
{/* API Key Verification Status */}
{apiKeyVerificationStatus === 'verifying' && (
-
+
Verifying API key...
Running a test query
@@ -642,7 +638,7 @@ export function ClaudeSetupStep({ onNext, onBack, onSkip }: ClaudeSetupStepProps
>
{apiKeyVerificationStatus === 'verifying' ? (
<>
-
+
Verifying...
>
) : apiKeyVerificationStatus === 'error' ? (
diff --git a/apps/ui/src/components/views/setup-view/steps/cli-setup-step.tsx b/apps/ui/src/components/views/setup-view/steps/cli-setup-step.tsx
index cf581f8c..031d6815 100644
--- a/apps/ui/src/components/views/setup-view/steps/cli-setup-step.tsx
+++ b/apps/ui/src/components/views/setup-view/steps/cli-setup-step.tsx
@@ -14,7 +14,6 @@ import { useAppStore } from '@/store/app-store';
import { getElectronAPI } from '@/lib/electron';
import {
CheckCircle2,
- Loader2,
Key,
ArrowRight,
ArrowLeft,
@@ -27,6 +26,7 @@ import {
XCircle,
Trash2,
} from 'lucide-react';
+import { Spinner } from '@/components/ui/spinner';
import { toast } from 'sonner';
import { StatusBadge, TerminalOutput } from '../components';
import { useCliStatus, useCliInstallation, useTokenSave } from '../hooks';
@@ -332,7 +332,7 @@ export function CliSetupStep({ config, state, onNext, onBack, onSkip }: CliSetup
Authentication Methods
-
+ {isChecking ? : }
Choose one of the following methods to authenticate:
@@ -408,7 +408,7 @@ export function CliSetupStep({ config, state, onNext, onBack, onSkip }: CliSetup
>
{isInstalling ? (
<>
-
+
Installing...
>
) : (
@@ -427,7 +427,7 @@ export function CliSetupStep({ config, state, onNext, onBack, onSkip }: CliSetup
{cliVerificationStatus === 'verifying' && (
-
+
Verifying CLI authentication...
Running a test query
@@ -605,7 +605,7 @@ export function CliSetupStep({ config, state, onNext, onBack, onSkip }: CliSetup
>
{cliVerificationStatus === 'verifying' ? (
<>
-
+
Verifying...
>
) : cliVerificationStatus === 'error' ? (
@@ -681,7 +681,7 @@ export function CliSetupStep({ config, state, onNext, onBack, onSkip }: CliSetup
>
{isSavingApiKey ? (
<>
-
+
Saving...
>
) : (
@@ -696,11 +696,7 @@ export function CliSetupStep({ config, state, onNext, onBack, onSkip }: CliSetup
className="border-red-500/50 text-red-500 hover:bg-red-500/10 hover:text-red-400"
data-testid={config.testIds.deleteApiKeyButton}
>
- {isDeletingApiKey ? (
-
- ) : (
-
- )}
+ {isDeletingApiKey ?
:
}
)}
@@ -708,7 +704,7 @@ export function CliSetupStep({ config, state, onNext, onBack, onSkip }: CliSetup
{apiKeyVerificationStatus === 'verifying' && (
-
+
Verifying API key...
Running a test query
@@ -767,7 +763,7 @@ export function CliSetupStep({ config, state, onNext, onBack, onSkip }: CliSetup
>
{apiKeyVerificationStatus === 'verifying' ? (
<>
-
+
Verifying...
>
) : apiKeyVerificationStatus === 'error' ? (
diff --git a/apps/ui/src/components/views/setup-view/steps/cursor-setup-step.tsx b/apps/ui/src/components/views/setup-view/steps/cursor-setup-step.tsx
index ff591f1a..e48057c4 100644
--- a/apps/ui/src/components/views/setup-view/steps/cursor-setup-step.tsx
+++ b/apps/ui/src/components/views/setup-view/steps/cursor-setup-step.tsx
@@ -7,7 +7,6 @@ import { useSetupStore } from '@/store/setup-store';
import { getElectronAPI } from '@/lib/electron';
import {
CheckCircle2,
- Loader2,
ArrowRight,
ArrowLeft,
ExternalLink,
@@ -16,6 +15,7 @@ import {
AlertTriangle,
XCircle,
} from 'lucide-react';
+import { Spinner } from '@/components/ui/spinner';
import { toast } from 'sonner';
import { StatusBadge } from '../components';
import { CursorIcon } from '@/components/ui/provider-icon';
@@ -204,7 +204,7 @@ export function CursorSetupStep({ onNext, onBack, onSkip }: CursorSetupStepProps
{getStatusBadge()}
-
+ {isChecking ? : }
@@ -318,7 +318,7 @@ export function CursorSetupStep({ onNext, onBack, onSkip }: CursorSetupStepProps
>
{isLoggingIn ? (
<>
-
+
Waiting for login...
>
) : (
@@ -332,7 +332,7 @@ export function CursorSetupStep({ onNext, onBack, onSkip }: CursorSetupStepProps
{/* Loading State */}
{isChecking && (
-
+
Checking Cursor CLI status...
diff --git a/apps/ui/src/components/views/setup-view/steps/github-setup-step.tsx b/apps/ui/src/components/views/setup-view/steps/github-setup-step.tsx
index fcccb618..3a20ee24 100644
--- a/apps/ui/src/components/views/setup-view/steps/github-setup-step.tsx
+++ b/apps/ui/src/components/views/setup-view/steps/github-setup-step.tsx
@@ -6,7 +6,6 @@ import { useSetupStore } from '@/store/setup-store';
import { getElectronAPI } from '@/lib/electron';
import {
CheckCircle2,
- Loader2,
ArrowRight,
ArrowLeft,
ExternalLink,
@@ -16,6 +15,7 @@ import {
Github,
XCircle,
} from 'lucide-react';
+import { Spinner } from '@/components/ui/spinner';
import { toast } from 'sonner';
import { StatusBadge } from '../components';
@@ -116,7 +116,7 @@ export function GitHubSetupStep({ onNext, onBack, onSkip }: GitHubSetupStepProps
{getStatusBadge()}
-
+ {isChecking ? : }
@@ -252,7 +252,7 @@ export function GitHubSetupStep({ onNext, onBack, onSkip }: GitHubSetupStepProps
{/* Loading State */}
{isChecking && (
-
+
Checking GitHub CLI status...
diff --git a/apps/ui/src/components/views/setup-view/steps/opencode-setup-step.tsx b/apps/ui/src/components/views/setup-view/steps/opencode-setup-step.tsx
index 5e7e29c0..58337851 100644
--- a/apps/ui/src/components/views/setup-view/steps/opencode-setup-step.tsx
+++ b/apps/ui/src/components/views/setup-view/steps/opencode-setup-step.tsx
@@ -7,7 +7,6 @@ import { useSetupStore } from '@/store/setup-store';
import { getElectronAPI } from '@/lib/electron';
import {
CheckCircle2,
- Loader2,
ArrowRight,
ArrowLeft,
ExternalLink,
@@ -17,6 +16,7 @@ import {
XCircle,
Terminal,
} from 'lucide-react';
+import { Spinner } from '@/components/ui/spinner';
import { toast } from 'sonner';
import { StatusBadge } from '../components';
@@ -204,7 +204,7 @@ export function OpencodeSetupStep({ onNext, onBack, onSkip }: OpencodeSetupStepP
{getStatusBadge()}
-
+ {isChecking ? : }
@@ -316,7 +316,7 @@ export function OpencodeSetupStep({ onNext, onBack, onSkip }: OpencodeSetupStepP
>
{isLoggingIn ? (
<>
-
+
Waiting for login...
>
) : (
@@ -330,7 +330,7 @@ export function OpencodeSetupStep({ onNext, onBack, onSkip }: OpencodeSetupStepP
{/* Loading State */}
{isChecking && (
-
+
Checking OpenCode CLI status...
diff --git a/apps/ui/src/components/views/setup-view/steps/providers-setup-step.tsx b/apps/ui/src/components/views/setup-view/steps/providers-setup-step.tsx
index b9ad3263..53b3ca0b 100644
--- a/apps/ui/src/components/views/setup-view/steps/providers-setup-step.tsx
+++ b/apps/ui/src/components/views/setup-view/steps/providers-setup-step.tsx
@@ -17,7 +17,6 @@ import {
ArrowRight,
ArrowLeft,
CheckCircle2,
- Loader2,
Key,
ExternalLink,
Copy,
@@ -29,6 +28,7 @@ import {
Terminal,
AlertCircle,
} from 'lucide-react';
+import { Spinner } from '@/components/ui/spinner';
import { toast } from 'sonner';
import { cn } from '@/lib/utils';
import { AnthropicIcon, CursorIcon, OpenAIIcon, OpenCodeIcon } from '@/components/ui/provider-icon';
@@ -240,7 +240,7 @@ function ClaudeContent() {
onClick={checkStatus}
disabled={isChecking || isVerifying}
>
-
+ {isChecking || isVerifying ?
:
}
@@ -278,7 +278,7 @@ function ClaudeContent() {
{/* Checking/Verifying State */}
{(isChecking || isVerifying) && (
-
+
{isChecking ? 'Checking Claude CLI status...' : 'Verifying authentication...'}
@@ -322,7 +322,7 @@ function ClaudeContent() {
>
{isInstalling ? (
<>
-
+
Installing...
>
) : (
@@ -417,11 +417,7 @@ function ClaudeContent() {
disabled={isSavingApiKey || !apiKey.trim()}
className="flex-1 bg-brand-500 hover:bg-brand-600 text-white"
>
- {isSavingApiKey ? (
-
- ) : (
- 'Save API Key'
- )}
+ {isSavingApiKey ?
: 'Save API Key'}
{hasApiKey && (
{isDeletingApiKey ? (
-
+
) : (
)}
@@ -553,7 +549,7 @@ function CursorContent() {
Cursor CLI Status
-
+ {isChecking ? : }
@@ -658,7 +654,7 @@ function CursorContent() {
>
{isLoggingIn ? (
<>
-
+
Waiting for login...
>
) : (
@@ -671,7 +667,7 @@ function CursorContent() {
{isChecking && (
-
+
Checking Cursor CLI status...
)}
@@ -807,7 +803,7 @@ function CodexContent() {
Codex CLI Status
-
+ {isChecking ? : }
@@ -915,7 +911,7 @@ function CodexContent() {
>
{isLoggingIn ? (
<>
-
+
Waiting for login...
>
) : (
@@ -958,7 +954,7 @@ function CodexContent() {
disabled={isSaving || !apiKey.trim()}
className="w-full bg-brand-500 hover:bg-brand-600 text-white"
>
- {isSaving ? : 'Save API Key'}
+ {isSaving ? : 'Save API Key'}
@@ -968,7 +964,7 @@ function CodexContent() {
{isChecking && (
-
+
Checking Codex CLI status...
)}
@@ -1082,7 +1078,7 @@ function OpencodeContent() {
OpenCode CLI Status
-
+ {isChecking ? : }
@@ -1191,7 +1187,7 @@ function OpencodeContent() {
>
{isLoggingIn ? (
<>
-
+
Waiting for login...
>
) : (
@@ -1204,7 +1200,7 @@ function OpencodeContent() {
{isChecking && (
-
+
Checking OpenCode CLI status...
)}
@@ -1416,7 +1412,7 @@ export function ProvidersSetupStep({ onNext, onBack }: ProvidersSetupStepProps)
);
case 'verifying':
return (
-
+
);
case 'installed_not_auth':
return (
@@ -1436,7 +1432,7 @@ export function ProvidersSetupStep({ onNext, onBack }: ProvidersSetupStepProps)
{isInitialChecking && (
-
+
Checking provider status...
)}
diff --git a/apps/ui/src/components/views/spec-view.tsx b/apps/ui/src/components/views/spec-view.tsx
index 616dc4dd..88fec94b 100644
--- a/apps/ui/src/components/views/spec-view.tsx
+++ b/apps/ui/src/components/views/spec-view.tsx
@@ -1,6 +1,6 @@
import { useState } from 'react';
-import { RefreshCw } from 'lucide-react';
import { useAppStore } from '@/store/app-store';
+import { Spinner } from '@/components/ui/spinner';
// Extracted hooks
import { useSpecLoading, useSpecSave, useSpecGeneration } from './spec-view/hooks';
@@ -86,7 +86,7 @@ export function SpecView() {
if (isLoading) {
return (
-
+
);
}
diff --git a/apps/ui/src/components/views/spec-view/components/spec-empty-state.tsx b/apps/ui/src/components/views/spec-view/components/spec-empty-state.tsx
index fa1792b1..ce7c1667 100644
--- a/apps/ui/src/components/views/spec-view/components/spec-empty-state.tsx
+++ b/apps/ui/src/components/views/spec-view/components/spec-empty-state.tsx
@@ -1,5 +1,6 @@
import { Button } from '@/components/ui/button';
-import { FileText, FilePlus2, Loader2 } from 'lucide-react';
+import { FileText, FilePlus2 } from 'lucide-react';
+import { Spinner } from '@/components/ui/spinner';
import { PHASE_LABELS } from '../constants';
interface SpecEmptyStateProps {
@@ -36,7 +37,7 @@ export function SpecEmptyState({
{isProcessing && (
@@ -64,7 +65,7 @@ export function SpecEmptyState({
{isCreating ? (
-
+
) : (
)}
diff --git a/apps/ui/src/components/views/spec-view/components/spec-header.tsx b/apps/ui/src/components/views/spec-view/components/spec-header.tsx
index b38a6579..72d879dd 100644
--- a/apps/ui/src/components/views/spec-view/components/spec-header.tsx
+++ b/apps/ui/src/components/views/spec-view/components/spec-header.tsx
@@ -3,7 +3,8 @@ import {
HeaderActionsPanel,
HeaderActionsPanelTrigger,
} from '@/components/ui/header-actions-panel';
-import { Save, Sparkles, Loader2, FileText, AlertCircle, ListPlus, RefreshCcw } from 'lucide-react';
+import { Save, Sparkles, FileText, AlertCircle, ListPlus, RefreshCcw } from 'lucide-react';
+import { Spinner } from '@/components/ui/spinner';
import { PHASE_LABELS } from '../constants';
interface SpecHeaderProps {
@@ -59,7 +60,7 @@ export function SpecHeader({
{isProcessing && (
@@ -83,7 +84,7 @@ export function SpecHeader({
{/* Mobile processing indicator */}
{isProcessing && (
-
+
Processing...
)}
@@ -157,7 +158,7 @@ export function SpecHeader({
{/* Status messages in panel */}
{isProcessing && (
-
+
{isSyncing
diff --git a/apps/ui/src/components/views/spec-view/dialogs/create-spec-dialog.tsx b/apps/ui/src/components/views/spec-view/dialogs/create-spec-dialog.tsx
index 73389f78..f77b08ca 100644
--- a/apps/ui/src/components/views/spec-view/dialogs/create-spec-dialog.tsx
+++ b/apps/ui/src/components/views/spec-view/dialogs/create-spec-dialog.tsx
@@ -1,4 +1,5 @@
-import { Sparkles, Clock, Loader2 } from 'lucide-react';
+import { Sparkles, Clock } from 'lucide-react';
+import { Spinner } from '@/components/ui/spinner';
import {
Dialog,
DialogContent,
@@ -163,7 +164,7 @@ export function CreateSpecDialog({
>
{isCreatingSpec ? (
<>
-
+
Generating...
>
) : (
diff --git a/apps/ui/src/components/views/spec-view/dialogs/regenerate-spec-dialog.tsx b/apps/ui/src/components/views/spec-view/dialogs/regenerate-spec-dialog.tsx
index fd534a58..c911fc94 100644
--- a/apps/ui/src/components/views/spec-view/dialogs/regenerate-spec-dialog.tsx
+++ b/apps/ui/src/components/views/spec-view/dialogs/regenerate-spec-dialog.tsx
@@ -1,4 +1,5 @@
-import { Sparkles, Clock, Loader2 } from 'lucide-react';
+import { Sparkles, Clock } from 'lucide-react';
+import { Spinner } from '@/components/ui/spinner';
import {
Dialog,
DialogContent,
@@ -158,7 +159,7 @@ export function RegenerateSpecDialog({
>
{isRegenerating ? (
<>
-
+
Regenerating...
>
) : (
diff --git a/apps/ui/src/components/views/terminal-view.tsx b/apps/ui/src/components/views/terminal-view.tsx
index 328afc21..0287ca68 100644
--- a/apps/ui/src/components/views/terminal-view.tsx
+++ b/apps/ui/src/components/views/terminal-view.tsx
@@ -7,13 +7,13 @@ import {
Unlock,
SplitSquareHorizontal,
SplitSquareVertical,
- Loader2,
AlertCircle,
RefreshCw,
X,
SquarePlus,
Settings,
} from 'lucide-react';
+import { Spinner } from '@/components/ui/spinner';
import { getServerUrlSync } from '@/lib/http-api-client';
import {
useAppStore,
@@ -1279,7 +1279,7 @@ export function TerminalView() {
if (loading) {
return (
-
+
);
}
@@ -1342,7 +1342,7 @@ export function TerminalView() {
{authError && {authError}
}
{authLoading ? (
-
+
) : (
)}
diff --git a/apps/ui/src/components/views/terminal-view/terminal-panel.tsx b/apps/ui/src/components/views/terminal-view/terminal-panel.tsx
index 481ee6b4..88f02591 100644
--- a/apps/ui/src/components/views/terminal-view/terminal-panel.tsx
+++ b/apps/ui/src/components/views/terminal-view/terminal-panel.tsx
@@ -13,7 +13,6 @@ import {
CheckSquare,
Trash2,
ImageIcon,
- Loader2,
Settings,
RotateCcw,
Search,
@@ -24,6 +23,7 @@ import {
ArrowDown,
} from 'lucide-react';
import { Button } from '@/components/ui/button';
+import { Spinner } from '@/components/ui/spinner';
import { cn } from '@/lib/utils';
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
import { Slider } from '@/components/ui/slider';
@@ -1743,7 +1743,7 @@ export function TerminalPanel({
{isProcessingImage ? (
<>
-
+
Processing...
>
) : (
@@ -1791,7 +1791,7 @@ export function TerminalPanel({
)}
{connectionStatus === 'reconnecting' && (
-
+
Reconnecting...
)}
diff --git a/apps/ui/src/components/views/welcome-view.tsx b/apps/ui/src/components/views/welcome-view.tsx
index b07c5188..46e4d88c 100644
--- a/apps/ui/src/components/views/welcome-view.tsx
+++ b/apps/ui/src/components/views/welcome-view.tsx
@@ -20,8 +20,8 @@ import {
Sparkles,
MessageSquare,
ChevronDown,
- Loader2,
} from 'lucide-react';
+import { Spinner } from '@/components/ui/spinner';
import {
DropdownMenu,
DropdownMenuContent,
@@ -758,7 +758,7 @@ export function WelcomeView() {
{isAnalyzing ? (
-
+
AI agent is analyzing your project structure...
@@ -802,7 +802,7 @@ export function WelcomeView() {
data-testid="project-opening-overlay"
>
-
+
Initializing project...
From 5b1e0105f4b6441d0e6e2d9e4b846c4583228a73 Mon Sep 17 00:00:00 2001
From: Shirone
Date: Sat, 17 Jan 2026 23:58:19 +0100
Subject: [PATCH 2/3] refactor: standardize PR state representation across the
application
Updated the PR state handling to use a consistent uppercase format ('OPEN', 'MERGED', 'CLOSED') throughout the codebase. This includes changes to the worktree metadata interface, PR creation logic, and related tests to ensure uniformity and prevent potential mismatches in state representation.
Additionally, modified the GitHub PR fetching logic to retrieve all PR states, allowing for better detection of state changes.
This refactor enhances clarity and consistency in how PR states are managed and displayed.
---
apps/server/src/lib/worktree-metadata.ts | 6 ++-
.../src/routes/worktree/routes/create-pr.ts | 9 ++--
.../server/src/routes/worktree/routes/list.ts | 54 +++++++++++++------
.../tests/unit/lib/worktree-metadata.test.ts | 14 ++---
.../views/board-view/worktree-panel/types.ts | 9 +++-
5 files changed, 63 insertions(+), 29 deletions(-)
diff --git a/apps/server/src/lib/worktree-metadata.ts b/apps/server/src/lib/worktree-metadata.ts
index 3f7ea60d..ab0ba67c 100644
--- a/apps/server/src/lib/worktree-metadata.ts
+++ b/apps/server/src/lib/worktree-metadata.ts
@@ -9,11 +9,15 @@ import * as path from 'path';
/** Maximum length for sanitized branch names in filesystem paths */
const MAX_SANITIZED_BRANCH_PATH_LENGTH = 200;
+/** GitHub PR states as returned by the GitHub API */
+export type PRState = 'OPEN' | 'MERGED' | 'CLOSED';
+
export interface WorktreePRInfo {
number: number;
url: string;
title: string;
- state: string;
+ /** PR state: OPEN, MERGED, or CLOSED */
+ state: PRState;
createdAt: string;
}
diff --git a/apps/server/src/routes/worktree/routes/create-pr.ts b/apps/server/src/routes/worktree/routes/create-pr.ts
index 1bde9448..25211854 100644
--- a/apps/server/src/routes/worktree/routes/create-pr.ts
+++ b/apps/server/src/routes/worktree/routes/create-pr.ts
@@ -268,11 +268,12 @@ export function createCreatePRHandler() {
prAlreadyExisted = true;
// Store the existing PR info in metadata
+ // GitHub CLI returns uppercase states: OPEN, MERGED, CLOSED
await updateWorktreePRInfo(effectiveProjectPath, branchName, {
number: existingPr.number,
url: existingPr.url,
title: existingPr.title || title,
- state: existingPr.state || 'open',
+ state: existingPr.state || 'OPEN',
createdAt: new Date().toISOString(),
});
logger.debug(
@@ -319,11 +320,12 @@ export function createCreatePRHandler() {
if (prNumber) {
try {
+ // Note: GitHub doesn't have a 'DRAFT' state - drafts still show as 'OPEN'
await updateWorktreePRInfo(effectiveProjectPath, branchName, {
number: prNumber,
url: prUrl,
title,
- state: draft ? 'draft' : 'open',
+ state: 'OPEN',
createdAt: new Date().toISOString(),
});
logger.debug(`Stored PR info for branch ${branchName}: PR #${prNumber}`);
@@ -352,11 +354,12 @@ export function createCreatePRHandler() {
prNumber = existingPr.number;
prAlreadyExisted = true;
+ // GitHub CLI returns uppercase states: OPEN, MERGED, CLOSED
await updateWorktreePRInfo(effectiveProjectPath, branchName, {
number: existingPr.number,
url: existingPr.url,
title: existingPr.title || title,
- state: existingPr.state || 'open',
+ state: existingPr.state || 'OPEN',
createdAt: new Date().toISOString(),
});
logger.debug(`Fetched and stored existing PR: #${existingPr.number}`);
diff --git a/apps/server/src/routes/worktree/routes/list.ts b/apps/server/src/routes/worktree/routes/list.ts
index 96782d64..a911fce7 100644
--- a/apps/server/src/routes/worktree/routes/list.ts
+++ b/apps/server/src/routes/worktree/routes/list.ts
@@ -14,7 +14,12 @@ import path from 'path';
import * as secureFs from '../../../lib/secure-fs.js';
import { isGitRepo } from '@automaker/git-utils';
import { getErrorMessage, logError, normalizePath, execEnv, isGhCliAvailable } from '../common.js';
-import { readAllWorktreeMetadata, type WorktreePRInfo } from '../../../lib/worktree-metadata.js';
+import {
+ readAllWorktreeMetadata,
+ updateWorktreePRInfo,
+ type WorktreePRInfo,
+ type PRState,
+} from '../../../lib/worktree-metadata.js';
import { createLogger } from '@automaker/utils';
import {
checkGitHubRemote,
@@ -168,8 +173,11 @@ async function getGitHubRemoteStatus(projectPath: string): Promise();
for (const worktree of worktrees) {
const metadata = allMetadata.get(worktree.branch);
- if (metadata?.pr) {
- // Use stored metadata (more complete info)
- worktree.pr = metadata.pr;
- } else if (includeDetails) {
- // Fall back to GitHub PR detection only when includeDetails is requested
- const githubPR = githubPRs.get(worktree.branch);
- if (githubPR) {
- worktree.pr = githubPR;
+ const githubPR = githubPRs.get(worktree.branch);
+
+ if (githubPR) {
+ // Prefer fresh GitHub data (it has the current state)
+ worktree.pr = githubPR;
+
+ // Sync metadata with GitHub state when:
+ // 1. No metadata exists for this PR (PR created externally)
+ // 2. State has changed (e.g., merged/closed on GitHub)
+ const needsSync = !metadata?.pr || metadata.pr.state !== githubPR.state;
+ if (needsSync) {
+ // Fire and forget - don't block the response
+ updateWorktreePRInfo(projectPath, worktree.branch, githubPR).catch((err) => {
+ logger.warn(
+ `Failed to update PR info for ${worktree.branch}: ${getErrorMessage(err)}`
+ );
+ });
}
+ } else if (metadata?.pr) {
+ // Fall back to stored metadata (for PRs not in recent GitHub response)
+ worktree.pr = metadata.pr;
}
}
diff --git a/apps/server/tests/unit/lib/worktree-metadata.test.ts b/apps/server/tests/unit/lib/worktree-metadata.test.ts
index ab7967f3..2f84af88 100644
--- a/apps/server/tests/unit/lib/worktree-metadata.test.ts
+++ b/apps/server/tests/unit/lib/worktree-metadata.test.ts
@@ -121,7 +121,7 @@ describe('worktree-metadata.ts', () => {
number: 123,
url: 'https://github.com/owner/repo/pull/123',
title: 'Test PR',
- state: 'open',
+ state: 'OPEN',
createdAt: new Date().toISOString(),
},
};
@@ -158,7 +158,7 @@ describe('worktree-metadata.ts', () => {
number: 456,
url: 'https://github.com/owner/repo/pull/456',
title: 'Updated PR',
- state: 'closed',
+ state: 'CLOSED',
createdAt: new Date().toISOString(),
},
};
@@ -177,7 +177,7 @@ describe('worktree-metadata.ts', () => {
number: 789,
url: 'https://github.com/owner/repo/pull/789',
title: 'New PR',
- state: 'open',
+ state: 'OPEN',
createdAt: new Date().toISOString(),
};
@@ -201,7 +201,7 @@ describe('worktree-metadata.ts', () => {
number: 999,
url: 'https://github.com/owner/repo/pull/999',
title: 'Updated PR',
- state: 'merged',
+ state: 'MERGED',
createdAt: new Date().toISOString(),
};
@@ -224,7 +224,7 @@ describe('worktree-metadata.ts', () => {
number: 111,
url: 'https://github.com/owner/repo/pull/111',
title: 'PR',
- state: 'open',
+ state: 'OPEN',
createdAt: new Date().toISOString(),
};
@@ -259,7 +259,7 @@ describe('worktree-metadata.ts', () => {
number: 222,
url: 'https://github.com/owner/repo/pull/222',
title: 'Has PR',
- state: 'open',
+ state: 'OPEN',
createdAt: new Date().toISOString(),
};
@@ -297,7 +297,7 @@ describe('worktree-metadata.ts', () => {
number: 333,
url: 'https://github.com/owner/repo/pull/333',
title: 'PR 3',
- state: 'open',
+ state: 'OPEN',
createdAt: new Date().toISOString(),
},
};
diff --git a/apps/ui/src/components/views/board-view/worktree-panel/types.ts b/apps/ui/src/components/views/board-view/worktree-panel/types.ts
index d2040048..36aa2da7 100644
--- a/apps/ui/src/components/views/board-view/worktree-panel/types.ts
+++ b/apps/ui/src/components/views/board-view/worktree-panel/types.ts
@@ -1,8 +1,12 @@
+/** GitHub PR states as returned by the GitHub API */
+export type PRState = 'OPEN' | 'MERGED' | 'CLOSED';
+
export interface WorktreePRInfo {
number: number;
url: string;
title: string;
- state: string;
+ /** PR state: OPEN, MERGED, or CLOSED */
+ state: PRState;
createdAt: string;
}
@@ -43,7 +47,8 @@ export interface PRInfo {
number: number;
title: string;
url: string;
- state: string;
+ /** PR state: OPEN, MERGED, or CLOSED */
+ state: PRState;
author: string;
body: string;
comments: Array<{
From 44e665f1bf04bfc3dbbc4cfff7a02785eef80ada Mon Sep 17 00:00:00 2001
From: Shirone
Date: Sun, 18 Jan 2026 00:22:27 +0100
Subject: [PATCH 3/3] fix: adress pr comments
---
apps/server/src/lib/worktree-metadata.ts | 16 +++-------
.../src/routes/worktree/routes/create-pr.ts | 5 +--
.../server/src/routes/worktree/routes/list.ts | 4 +--
.../views/board-view/worktree-panel/types.ts | 14 ++------
libs/types/src/index.ts | 4 +++
libs/types/src/worktree.ts | 32 +++++++++++++++++++
6 files changed, 48 insertions(+), 27 deletions(-)
create mode 100644 libs/types/src/worktree.ts
diff --git a/apps/server/src/lib/worktree-metadata.ts b/apps/server/src/lib/worktree-metadata.ts
index ab0ba67c..4742a5b0 100644
--- a/apps/server/src/lib/worktree-metadata.ts
+++ b/apps/server/src/lib/worktree-metadata.ts
@@ -5,22 +5,14 @@
import * as secureFs from './secure-fs.js';
import * as path from 'path';
+import type { PRState, WorktreePRInfo } from '@automaker/types';
+
+// Re-export types for backwards compatibility
+export type { PRState, WorktreePRInfo };
/** Maximum length for sanitized branch names in filesystem paths */
const MAX_SANITIZED_BRANCH_PATH_LENGTH = 200;
-/** GitHub PR states as returned by the GitHub API */
-export type PRState = 'OPEN' | 'MERGED' | 'CLOSED';
-
-export interface WorktreePRInfo {
- number: number;
- url: string;
- title: string;
- /** PR state: OPEN, MERGED, or CLOSED */
- state: PRState;
- createdAt: string;
-}
-
export interface WorktreeMetadata {
branch: string;
createdAt: string;
diff --git a/apps/server/src/routes/worktree/routes/create-pr.ts b/apps/server/src/routes/worktree/routes/create-pr.ts
index 25211854..87777c69 100644
--- a/apps/server/src/routes/worktree/routes/create-pr.ts
+++ b/apps/server/src/routes/worktree/routes/create-pr.ts
@@ -13,6 +13,7 @@ import {
} from '../common.js';
import { updateWorktreePRInfo } from '../../../lib/worktree-metadata.js';
import { createLogger } from '@automaker/utils';
+import { validatePRState } from '@automaker/types';
const logger = createLogger('CreatePR');
@@ -273,7 +274,7 @@ export function createCreatePRHandler() {
number: existingPr.number,
url: existingPr.url,
title: existingPr.title || title,
- state: existingPr.state || 'OPEN',
+ state: validatePRState(existingPr.state),
createdAt: new Date().toISOString(),
});
logger.debug(
@@ -359,7 +360,7 @@ export function createCreatePRHandler() {
number: existingPr.number,
url: existingPr.url,
title: existingPr.title || title,
- state: existingPr.state || 'OPEN',
+ state: validatePRState(existingPr.state),
createdAt: new Date().toISOString(),
});
logger.debug(`Fetched and stored existing PR: #${existingPr.number}`);
diff --git a/apps/server/src/routes/worktree/routes/list.ts b/apps/server/src/routes/worktree/routes/list.ts
index a911fce7..e82e5c14 100644
--- a/apps/server/src/routes/worktree/routes/list.ts
+++ b/apps/server/src/routes/worktree/routes/list.ts
@@ -18,9 +18,9 @@ import {
readAllWorktreeMetadata,
updateWorktreePRInfo,
type WorktreePRInfo,
- type PRState,
} from '../../../lib/worktree-metadata.js';
import { createLogger } from '@automaker/utils';
+import { validatePRState } from '@automaker/types';
import {
checkGitHubRemote,
type GitHubRemoteStatus,
@@ -221,7 +221,7 @@ async function fetchGitHubPRs(projectPath: string): Promise