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.
This commit is contained in:
webdevcody
2026-01-17 17:58:16 -05:00
parent 044c3d50d1
commit 832d10e133
93 changed files with 351 additions and 333 deletions

View File

@@ -11,6 +11,7 @@ import {
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
import { toast } from 'sonner';
import { LogOut, User, Code2, RefreshCw } from 'lucide-react';
import { Spinner } from '@/components/ui/spinner';
import { cn } from '@/lib/utils';
import { logout } from '@/lib/http-api-client';
import { useAuthStore } from '@/store/auth-store';
@@ -143,7 +144,7 @@ export function AccountSection() {
disabled={isRefreshing || isLoadingEditors}
className="shrink-0 h-9 w-9"
>
<RefreshCw className={cn('w-4 h-4', isRefreshing && 'animate-spin')} />
{isRefreshing ? <Spinner size="sm" /> : <RefreshCw className="w-4 h-4" />}
</Button>
</TooltipTrigger>
<TooltipContent>

View File

@@ -1,7 +1,8 @@
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { AlertCircle, CheckCircle2, Eye, EyeOff, Loader2, Zap } from 'lucide-react';
import { AlertCircle, CheckCircle2, Eye, EyeOff, Zap } from 'lucide-react';
import { Spinner } from '@/components/ui/spinner';
import type { ProviderConfig } from '@/config/api-providers';
interface ApiKeyFieldProps {
@@ -70,7 +71,7 @@ export function ApiKeyField({ config }: ApiKeyFieldProps) {
>
{testButton.loading ? (
<>
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
<Spinner size="sm" className="mr-2" />
Testing...
</>
) : (

View File

@@ -1,7 +1,8 @@
import { useAppStore } from '@/store/app-store';
import { useSetupStore } from '@/store/setup-store';
import { Button } from '@/components/ui/button';
import { Key, CheckCircle2, Trash2, Loader2 } from 'lucide-react';
import { Key, CheckCircle2, Trash2 } from 'lucide-react';
import { Spinner } from '@/components/ui/spinner';
import { ApiKeyField } from './api-key-field';
import { buildProviderConfigs } from '@/config/api-providers';
import { SecurityNotice } from './security-notice';
@@ -142,7 +143,7 @@ export function ApiKeysSection() {
data-testid="delete-anthropic-key"
>
{isDeletingAnthropicKey ? (
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
<Spinner size="sm" className="mr-2" />
) : (
<Trash2 className="w-4 h-4 mr-2" />
)}
@@ -159,7 +160,7 @@ export function ApiKeysSection() {
data-testid="delete-openai-key"
>
{isDeletingOpenaiKey ? (
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
<Spinner size="sm" className="mr-2" />
) : (
<Trash2 className="w-4 h-4 mr-2" />
)}

View File

@@ -4,6 +4,7 @@ import { getElectronAPI } from '@/lib/electron';
import { useSetupStore } from '@/store/setup-store';
import { useAppStore } from '@/store/app-store';
import { Button } from '@/components/ui/button';
import { Spinner } from '@/components/ui/spinner';
import { RefreshCw, AlertCircle } from 'lucide-react';
const ERROR_NO_API = 'Claude usage API not available';
@@ -178,7 +179,7 @@ export function ClaudeUsageSection() {
data-testid="refresh-claude-usage"
title={CLAUDE_REFRESH_LABEL}
>
<RefreshCw className={cn('w-4 h-4', isLoading && 'animate-spin')} />
{isLoading ? <Spinner size="sm" /> : <RefreshCw className="w-4 h-4" />}
</Button>
</div>
<p className="text-sm text-muted-foreground/80 ml-12">{CLAUDE_USAGE_SUBTITLE}</p>

View File

@@ -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';
@@ -172,7 +173,7 @@ export function ClaudeCliStatus({ status, authStatus, isChecking, onRefresh }: C
'transition-all duration-200'
)}
>
<RefreshCw className={cn('w-4 h-4', isChecking && 'animate-spin')} />
{isChecking ? <Spinner size="sm" /> : <RefreshCw className="w-4 h-4" />}
</Button>
</div>
<p className="text-sm text-muted-foreground/80 ml-12">

View File

@@ -1,5 +1,6 @@
import { Button } from '@/components/ui/button';
import { CheckCircle2, AlertCircle, RefreshCw } from 'lucide-react';
import { Spinner } from '@/components/ui/spinner';
import { cn } from '@/lib/utils';
import type { CliStatus } from '../shared/types';
@@ -56,7 +57,7 @@ export function CliStatusCard({
'transition-all duration-200'
)}
>
<RefreshCw className={cn('w-4 h-4', isChecking && 'animate-spin')} />
{isChecking ? <Spinner size="sm" /> : <RefreshCw className="w-4 h-4" />}
</Button>
</div>
<p className="text-sm text-muted-foreground/80 ml-12">{description}</p>

View File

@@ -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'
)}
>
<RefreshCw className={cn('w-4 h-4', isChecking && 'animate-spin')} />
{isChecking ? <Spinner size="sm" /> : <RefreshCw className="w-4 h-4" />}
</Button>
</div>
<p className="text-sm text-muted-foreground/80 ml-12">

View File

@@ -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'
)}
>
<RefreshCw className={cn('w-4 h-4', isChecking && 'animate-spin')} />
{isChecking ? <Spinner size="sm" /> : <RefreshCw className="w-4 h-4" />}
</Button>
</div>
<p className="text-sm text-muted-foreground/80 ml-12">

View File

@@ -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'
)}
>
<RefreshCw className={cn('w-4 h-4', isChecking && 'animate-spin')} />
{isChecking ? <Spinner size="sm" /> : <RefreshCw className="w-4 h-4" />}
</Button>
</div>
<p className="text-sm text-muted-foreground/80 ml-12">

View File

@@ -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}
>
<RefreshCw className={cn('w-4 h-4', isLoading && 'animate-spin')} />
{isLoading ? <Spinner size="sm" /> : <RefreshCw className="w-4 h-4" />}
</Button>
</div>
<p className="text-sm text-muted-foreground/80 ml-12">{CODEX_USAGE_SUBTITLE}</p>

View File

@@ -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() {
</p>
<div className="flex items-center gap-2">
<Button variant="outline" size="sm" onClick={loadEvents} disabled={loading}>
<RefreshCw className={cn('w-4 h-4 mr-2', loading && 'animate-spin')} />
{loading ? (
<Spinner size="sm" className="mr-2" />
) : (
<RefreshCw className="w-4 h-4 mr-2" />
)}
Refresh
</Button>
{events.length > 0 && (

View File

@@ -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' ? (
<Loader2 className="w-4 h-4 animate-spin" />
<Spinner size="sm" />
) : (
<PlayCircle className="w-4 h-4" />
)}

View File

@@ -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"
>
<RefreshCw className={cn('w-4 h-4', isRefreshing && 'animate-spin')} />
{isRefreshing ? <Spinner size="sm" /> : <RefreshCw className="w-4 h-4" />}
</Button>
{hasServers && (
<>

View File

@@ -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 <Loader2 className="w-4 h-4 animate-spin text-brand-500" />;
return <Spinner size="sm" />;
case 'success':
return <CheckCircle2 className="w-4 h-4 text-green-500" />;
case 'error':

View File

@@ -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 ? (
<Loader2 className="h-3.5 w-3.5 animate-spin" />
) : (
<RefreshCw className="h-3.5 w-3.5" />
)}
{isLoadingAgents ? <Spinner size="xs" /> : <RefreshCw className="h-3.5 w-3.5" />}
Refresh
</Button>
</div>

View File

@@ -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 ? (
<div className="flex items-center justify-center py-8">
<div className="animate-spin w-6 h-6 border-2 border-brand-500 border-t-transparent rounded-full" />
<Spinner size="lg" />
</div>
) : (
<>

View File

@@ -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({
</p>
{isLoadingDynamicModels && (
<div className="flex items-center gap-1.5 text-xs text-muted-foreground">
<Loader2 className="w-3 h-3 animate-spin" />
<Spinner size="xs" />
<span>Discovering...</span>
</div>
)}