From 832d10e133f7cf83e2d5321740f757c14601b2d4 Mon Sep 17 00:00:00 2001
From: webdevcody
Date: Sat, 17 Jan 2026 17:58:16 -0500
Subject: [PATCH] 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...