mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-04 09:13:08 +00:00
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:
@@ -2,6 +2,7 @@ import { useState, useEffect, useMemo, useCallback } from 'react';
|
|||||||
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
|
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { RefreshCw, AlertTriangle, CheckCircle, XCircle, Clock, ExternalLink } from 'lucide-react';
|
import { RefreshCw, AlertTriangle, CheckCircle, XCircle, Clock, ExternalLink } from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
import { useAppStore } from '@/store/app-store';
|
import { useAppStore } from '@/store/app-store';
|
||||||
@@ -279,7 +280,7 @@ export function ClaudeUsagePopover() {
|
|||||||
) : !claudeUsage ? (
|
) : !claudeUsage ? (
|
||||||
// Loading state
|
// Loading state
|
||||||
<div className="flex flex-col items-center justify-center py-8 space-y-2">
|
<div className="flex flex-col items-center justify-center py-8 space-y-2">
|
||||||
<RefreshCw className="w-6 h-6 animate-spin text-muted-foreground/50" />
|
<Spinner size="lg" />
|
||||||
<p className="text-xs text-muted-foreground">Loading usage data...</p>
|
<p className="text-xs text-muted-foreground">Loading usage data...</p>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { useState, useEffect, useMemo, useCallback } from 'react';
|
|||||||
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
|
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { RefreshCw, AlertTriangle, CheckCircle, XCircle, Clock, ExternalLink } from 'lucide-react';
|
import { RefreshCw, AlertTriangle, CheckCircle, XCircle, Clock, ExternalLink } from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
import { useAppStore } from '@/store/app-store';
|
import { useAppStore } from '@/store/app-store';
|
||||||
@@ -333,7 +334,7 @@ export function CodexUsagePopover() {
|
|||||||
) : !codexUsage ? (
|
) : !codexUsage ? (
|
||||||
// Loading state
|
// Loading state
|
||||||
<div className="flex flex-col items-center justify-center py-8 space-y-2">
|
<div className="flex flex-col items-center justify-center py-8 space-y-2">
|
||||||
<RefreshCw className="w-6 h-6 animate-spin text-muted-foreground/50" />
|
<Spinner size="lg" />
|
||||||
<p className="text-xs text-muted-foreground">Loading usage data...</p>
|
<p className="text-xs text-muted-foreground">Loading usage data...</p>
|
||||||
</div>
|
</div>
|
||||||
) : codexUsage.rateLimits ? (
|
) : codexUsage.rateLimits ? (
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useState, useRef, useCallback, useEffect } from 'react';
|
import { useState, useRef, useCallback, useEffect } from 'react';
|
||||||
import { createLogger } from '@automaker/utils/logger';
|
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');
|
const logger = createLogger('BoardBackgroundModal');
|
||||||
import {
|
import {
|
||||||
@@ -313,7 +314,7 @@ export function BoardBackgroundModal({ open, onOpenChange }: BoardBackgroundModa
|
|||||||
/>
|
/>
|
||||||
{isProcessing && (
|
{isProcessing && (
|
||||||
<div className="absolute inset-0 flex items-center justify-center bg-background/80">
|
<div className="absolute inset-0 flex items-center justify-center bg-background/80">
|
||||||
<Loader2 className="w-6 h-6 animate-spin text-brand-500" />
|
<Spinner size="lg" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -353,7 +354,7 @@ export function BoardBackgroundModal({ open, onOpenChange }: BoardBackgroundModa
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{isProcessing ? (
|
{isProcessing ? (
|
||||||
<Upload className="h-6 w-6 animate-spin text-muted-foreground" />
|
<Spinner size="lg" />
|
||||||
) : (
|
) : (
|
||||||
<ImageIcon className="h-6 w-6 text-muted-foreground" />
|
<ImageIcon className="h-6 w-6 text-muted-foreground" />
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -14,16 +14,8 @@ import { Input } from '@/components/ui/input';
|
|||||||
import { Label } from '@/components/ui/label';
|
import { Label } from '@/components/ui/label';
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||||
import { Badge } from '@/components/ui/badge';
|
import { Badge } from '@/components/ui/badge';
|
||||||
import {
|
import { FolderPlus, FolderOpen, Rocket, ExternalLink, Check, Link, Folder } from 'lucide-react';
|
||||||
FolderPlus,
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
FolderOpen,
|
|
||||||
Rocket,
|
|
||||||
ExternalLink,
|
|
||||||
Check,
|
|
||||||
Loader2,
|
|
||||||
Link,
|
|
||||||
Folder,
|
|
||||||
} from 'lucide-react';
|
|
||||||
import { starterTemplates, type StarterTemplate } from '@/lib/templates';
|
import { starterTemplates, type StarterTemplate } from '@/lib/templates';
|
||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
@@ -451,7 +443,7 @@ export function NewProjectModal({
|
|||||||
>
|
>
|
||||||
{isCreating ? (
|
{isCreating ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
<Spinner size="sm" className="mr-2" />
|
||||||
{activeTab === 'template' ? 'Cloning...' : 'Creating...'}
|
{activeTab === 'template' ? 'Cloning...' : 'Creating...'}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ import {
|
|||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from '@/components/ui/dialog';
|
} from '@/components/ui/dialog';
|
||||||
import { Button } from '@/components/ui/button';
|
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';
|
import { getHttpApiClient } from '@/lib/http-api-client';
|
||||||
|
|
||||||
interface WorkspaceDirectory {
|
interface WorkspaceDirectory {
|
||||||
@@ -74,7 +75,7 @@ export function WorkspacePickerModal({ open, onOpenChange, onSelect }: Workspace
|
|||||||
<div className="flex-1 overflow-y-auto py-4 min-h-[200px]">
|
<div className="flex-1 overflow-y-auto py-4 min-h-[200px]">
|
||||||
{isLoading && (
|
{isLoading && (
|
||||||
<div className="flex flex-col items-center justify-center h-full gap-3">
|
<div className="flex flex-col items-center justify-center h-full gap-3">
|
||||||
<Loader2 className="w-8 h-8 text-brand-500 animate-spin" />
|
<Spinner size="xl" />
|
||||||
<p className="text-sm text-muted-foreground">Loading projects...</p>
|
<p className="text-sm text-muted-foreground">Loading projects...</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import type { NavigateOptions } from '@tanstack/react-router';
|
import type { NavigateOptions } from '@tanstack/react-router';
|
||||||
import { Loader2 } from 'lucide-react';
|
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { formatShortcut } from '@/store/app-store';
|
import { formatShortcut } from '@/store/app-store';
|
||||||
import type { NavSection } from '../types';
|
import type { NavSection } from '../types';
|
||||||
import type { Project } from '@/lib/electron';
|
import type { Project } from '@/lib/electron';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
|
|
||||||
interface SidebarNavigationProps {
|
interface SidebarNavigationProps {
|
||||||
currentProject: Project | null;
|
currentProject: Project | null;
|
||||||
@@ -93,9 +93,10 @@ export function SidebarNavigation({
|
|||||||
>
|
>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
{item.isLoading ? (
|
{item.isLoading ? (
|
||||||
<Loader2
|
<Spinner
|
||||||
|
size="md"
|
||||||
className={cn(
|
className={cn(
|
||||||
'w-[18px] h-[18px] shrink-0 animate-spin',
|
'shrink-0',
|
||||||
isActive ? 'text-brand-500' : 'text-muted-foreground'
|
isActive ? 'text-brand-500' : 'text-muted-foreground'
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ import {
|
|||||||
Check,
|
Check,
|
||||||
X,
|
X,
|
||||||
ArchiveRestore,
|
ArchiveRestore,
|
||||||
Loader2,
|
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import type { SessionListItem } from '@/types/electron';
|
import type { SessionListItem } from '@/types/electron';
|
||||||
import { useKeyboardShortcutsConfig } from '@/hooks/use-keyboard-shortcuts';
|
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) */}
|
{/* Show loading indicator if this session is running (either current session thinking or any session in runningSessions) */}
|
||||||
{(currentSessionId === session.id && isCurrentSessionThinking) ||
|
{(currentSessionId === session.id && isCurrentSessionThinking) ||
|
||||||
runningSessions.has(session.id) ? (
|
runningSessions.has(session.id) ? (
|
||||||
<Loader2 className="w-4 h-4 text-primary animate-spin shrink-0" />
|
<Spinner size="sm" className="shrink-0" />
|
||||||
) : (
|
) : (
|
||||||
<MessageSquare className="w-4 h-4 text-muted-foreground shrink-0" />
|
<MessageSquare className="w-4 h-4 text-muted-foreground shrink-0" />
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Slot } from '@radix-ui/react-slot';
|
import { Slot } from '@radix-ui/react-slot';
|
||||||
import { cva, type VariantProps } from 'class-variance-authority';
|
import { cva, type VariantProps } from 'class-variance-authority';
|
||||||
import { Loader2 } from 'lucide-react';
|
|
||||||
|
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
|
|
||||||
const buttonVariants = cva(
|
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]",
|
"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
|
// Loading spinner component
|
||||||
function ButtonSpinner({ className }: { className?: string }) {
|
function ButtonSpinner({ className }: { className?: string }) {
|
||||||
return <Loader2 className={cn('size-4 animate-spin', className)} aria-hidden="true" />;
|
return <Spinner size="sm" className={className} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
function Button({
|
function Button({
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ import { createLogger } from '@automaker/utils/logger';
|
|||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
const logger = createLogger('DescriptionImageDropZone');
|
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 { Textarea } from '@/components/ui/textarea';
|
||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
import { getAuthenticatedImageUrl } from '@/lib/api-fetch';
|
import { getAuthenticatedImageUrl } from '@/lib/api-fetch';
|
||||||
@@ -431,7 +432,7 @@ export function DescriptionImageDropZone({
|
|||||||
{/* Processing indicator */}
|
{/* Processing indicator */}
|
||||||
{isProcessing && (
|
{isProcessing && (
|
||||||
<div className="flex items-center gap-2 mt-2 text-sm text-muted-foreground">
|
<div className="flex items-center gap-2 mt-2 text-sm text-muted-foreground">
|
||||||
<Loader2 className="w-4 h-4 animate-spin" />
|
<Spinner size="sm" />
|
||||||
<span>Processing files...</span>
|
<span>Processing files...</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ import { createLogger } from '@automaker/utils/logger';
|
|||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
const logger = createLogger('FeatureImageUpload');
|
const logger = createLogger('FeatureImageUpload');
|
||||||
import { ImageIcon, X, Upload } from 'lucide-react';
|
import { ImageIcon, X } from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import {
|
import {
|
||||||
fileToBase64,
|
fileToBase64,
|
||||||
generateImageId,
|
generateImageId,
|
||||||
@@ -196,7 +197,7 @@ export function FeatureImageUpload({
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{isProcessing ? (
|
{isProcessing ? (
|
||||||
<Upload className="h-5 w-5 animate-spin text-muted-foreground" />
|
<Spinner size="md" />
|
||||||
) : (
|
) : (
|
||||||
<ImageIcon className="h-5 w-5 text-muted-foreground" />
|
<ImageIcon className="h-5 w-5 text-muted-foreground" />
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -9,11 +9,11 @@ import {
|
|||||||
FilePen,
|
FilePen,
|
||||||
ChevronDown,
|
ChevronDown,
|
||||||
ChevronRight,
|
ChevronRight,
|
||||||
Loader2,
|
|
||||||
RefreshCw,
|
RefreshCw,
|
||||||
GitBranch,
|
GitBranch,
|
||||||
AlertCircle,
|
AlertCircle,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { Button } from './button';
|
import { Button } from './button';
|
||||||
import type { FileStatus } from '@/types/electron';
|
import type { FileStatus } from '@/types/electron';
|
||||||
|
|
||||||
@@ -484,7 +484,7 @@ export function GitDiffPanel({
|
|||||||
<div className="border-t border-border">
|
<div className="border-t border-border">
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<div className="flex items-center justify-center gap-2 py-8 text-muted-foreground">
|
<div className="flex items-center justify-center gap-2 py-8 text-muted-foreground">
|
||||||
<Loader2 className="w-5 h-5 animate-spin" />
|
<Spinner size="md" />
|
||||||
<span className="text-sm">Loading changes...</span>
|
<span className="text-sm">Loading changes...</span>
|
||||||
</div>
|
</div>
|
||||||
) : error ? (
|
) : error ? (
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ import { createLogger } from '@automaker/utils/logger';
|
|||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
const logger = createLogger('ImageDropZone');
|
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 type { ImageAttachment } from '@/store/app-store';
|
||||||
import {
|
import {
|
||||||
fileToBase64,
|
fileToBase64,
|
||||||
@@ -204,7 +205,7 @@ export function ImageDropZone({
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{isProcessing ? (
|
{isProcessing ? (
|
||||||
<Upload className="h-6 w-6 animate-spin text-muted-foreground" />
|
<Spinner size="lg" />
|
||||||
) : (
|
) : (
|
||||||
<ImageIcon className="h-6 w-6 text-muted-foreground" />
|
<ImageIcon className="h-6 w-6 text-muted-foreground" />
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,17 +1,15 @@
|
|||||||
import { Loader2 } from 'lucide-react';
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
|
|
||||||
interface LoadingStateProps {
|
interface LoadingStateProps {
|
||||||
/** Optional custom message to display below the spinner */
|
/** Optional custom message to display below the spinner */
|
||||||
message?: string;
|
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 (
|
return (
|
||||||
<div className="flex-1 flex flex-col items-center justify-center">
|
<div className="flex-1 flex flex-col items-center justify-center">
|
||||||
<Loader2 className={`${size} animate-spin text-muted-foreground`} />
|
<Spinner size="xl" />
|
||||||
{message && <p className="mt-4 text-sm text-muted-foreground">{message}</p>}
|
{message && <p className="mt-4 text-sm font-medium text-primary">{message}</p>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ import {
|
|||||||
Filter,
|
Filter,
|
||||||
Circle,
|
Circle,
|
||||||
Play,
|
Play,
|
||||||
Loader2,
|
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import {
|
import {
|
||||||
parseLogOutput,
|
parseLogOutput,
|
||||||
@@ -148,7 +148,7 @@ function TodoListRenderer({ todos }: { todos: TodoItem[] }) {
|
|||||||
case 'completed':
|
case 'completed':
|
||||||
return <CheckCircle2 className="w-4 h-4 text-emerald-400" />;
|
return <CheckCircle2 className="w-4 h-4 text-emerald-400" />;
|
||||||
case 'in_progress':
|
case 'in_progress':
|
||||||
return <Loader2 className="w-4 h-4 text-amber-400 animate-spin" />;
|
return <Spinner size="sm" />;
|
||||||
case 'pending':
|
case 'pending':
|
||||||
return <Circle className="w-4 h-4 text-muted-foreground/70" />;
|
return <Circle className="w-4 h-4 text-muted-foreground/70" />;
|
||||||
default:
|
default:
|
||||||
|
|||||||
32
apps/ui/src/components/ui/spinner.tsx
Normal file
32
apps/ui/src/components/ui/spinner.tsx
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import { Loader2 } from 'lucide-react';
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
type SpinnerSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
||||||
|
|
||||||
|
const sizeClasses: Record<SpinnerSize, string> = {
|
||||||
|
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 (
|
||||||
|
<Loader2
|
||||||
|
className={cn(sizeClasses[size], 'animate-spin text-primary', className)}
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -5,7 +5,8 @@ import { createLogger } from '@automaker/utils/logger';
|
|||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
const logger = createLogger('TaskProgressPanel');
|
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 { getElectronAPI } from '@/lib/electron';
|
||||||
import type { AutoModeEvent } from '@/types/electron';
|
import type { AutoModeEvent } from '@/types/electron';
|
||||||
import { Badge } from '@/components/ui/badge';
|
import { Badge } from '@/components/ui/badge';
|
||||||
@@ -260,7 +261,7 @@ export function TaskProgressPanel({
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{isCompleted && <Check className="h-3.5 w-3.5" />}
|
{isCompleted && <Check className="h-3.5 w-3.5" />}
|
||||||
{isActive && <Loader2 className="h-3.5 w-3.5 animate-spin" />}
|
{isActive && <Spinner size="xs" />}
|
||||||
{isPending && <Circle className="h-2 w-2 fill-current opacity-50" />}
|
{isPending && <Circle className="h-2 w-2 fill-current opacity-50" />}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover
|
|||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||||
import { RefreshCw, AlertTriangle, CheckCircle, XCircle, Clock, ExternalLink } from 'lucide-react';
|
import { RefreshCw, AlertTriangle, CheckCircle, XCircle, Clock, ExternalLink } from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
import { useAppStore } from '@/store/app-store';
|
import { useAppStore } from '@/store/app-store';
|
||||||
@@ -449,7 +450,7 @@ export function UsagePopover() {
|
|||||||
</div>
|
</div>
|
||||||
) : !claudeUsage ? (
|
) : !claudeUsage ? (
|
||||||
<div className="flex flex-col items-center justify-center py-8 space-y-2">
|
<div className="flex flex-col items-center justify-center py-8 space-y-2">
|
||||||
<RefreshCw className="w-6 h-6 animate-spin text-muted-foreground/50" />
|
<Spinner size="lg" />
|
||||||
<p className="text-xs text-muted-foreground">Loading usage data...</p>
|
<p className="text-xs text-muted-foreground">Loading usage data...</p>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
@@ -568,7 +569,7 @@ export function UsagePopover() {
|
|||||||
</div>
|
</div>
|
||||||
) : !codexUsage ? (
|
) : !codexUsage ? (
|
||||||
<div className="flex flex-col items-center justify-center py-8 space-y-2">
|
<div className="flex flex-col items-center justify-center py-8 space-y-2">
|
||||||
<RefreshCw className="w-6 h-6 animate-spin text-muted-foreground/50" />
|
<Spinner size="lg" />
|
||||||
<p className="text-xs text-muted-foreground">Loading usage data...</p>
|
<p className="text-xs text-muted-foreground">Loading usage data...</p>
|
||||||
</div>
|
</div>
|
||||||
) : codexUsage.rateLimits ? (
|
) : codexUsage.rateLimits ? (
|
||||||
|
|||||||
@@ -11,12 +11,12 @@ import {
|
|||||||
Terminal,
|
Terminal,
|
||||||
CheckCircle,
|
CheckCircle,
|
||||||
XCircle,
|
XCircle,
|
||||||
Loader2,
|
|
||||||
Play,
|
Play,
|
||||||
File,
|
File,
|
||||||
Pencil,
|
Pencil,
|
||||||
Wrench,
|
Wrench,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
|
|
||||||
@@ -236,7 +236,7 @@ export function AgentToolsView() {
|
|||||||
>
|
>
|
||||||
{isReadingFile ? (
|
{isReadingFile ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
<Spinner size="sm" className="mr-2" />
|
||||||
Reading...
|
Reading...
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
@@ -315,7 +315,7 @@ export function AgentToolsView() {
|
|||||||
>
|
>
|
||||||
{isWritingFile ? (
|
{isWritingFile ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
<Spinner size="sm" className="mr-2" />
|
||||||
Writing...
|
Writing...
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
@@ -383,7 +383,7 @@ export function AgentToolsView() {
|
|||||||
>
|
>
|
||||||
{isRunningCommand ? (
|
{isRunningCommand ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
<Spinner size="sm" className="mr-2" />
|
||||||
Running...
|
Running...
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Bot } from 'lucide-react';
|
import { Bot } from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
|
|
||||||
export function ThinkingIndicator() {
|
export function ThinkingIndicator() {
|
||||||
return (
|
return (
|
||||||
@@ -8,20 +9,7 @@ export function ThinkingIndicator() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="bg-card border border-border rounded-2xl px-4 py-3 shadow-sm">
|
<div className="bg-card border border-border rounded-2xl px-4 py-3 shadow-sm">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<div className="flex items-center gap-1">
|
<Spinner size="sm" />
|
||||||
<span
|
|
||||||
className="w-2 h-2 rounded-full bg-primary animate-pulse"
|
|
||||||
style={{ animationDelay: '0ms' }}
|
|
||||||
/>
|
|
||||||
<span
|
|
||||||
className="w-2 h-2 rounded-full bg-primary animate-pulse"
|
|
||||||
style={{ animationDelay: '150ms' }}
|
|
||||||
/>
|
|
||||||
<span
|
|
||||||
className="w-2 h-2 rounded-full bg-primary animate-pulse"
|
|
||||||
style={{ animationDelay: '300ms' }}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<span className="text-sm text-muted-foreground">Thinking...</span>
|
<span className="text-sm text-muted-foreground">Thinking...</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -14,12 +14,12 @@ import {
|
|||||||
RefreshCw,
|
RefreshCw,
|
||||||
BarChart3,
|
BarChart3,
|
||||||
FileCode,
|
FileCode,
|
||||||
Loader2,
|
|
||||||
FileText,
|
FileText,
|
||||||
CheckCircle,
|
CheckCircle,
|
||||||
AlertCircle,
|
AlertCircle,
|
||||||
ListChecks,
|
ListChecks,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { cn, generateUUID } from '@/lib/utils';
|
import { cn, generateUUID } from '@/lib/utils';
|
||||||
|
|
||||||
const logger = createLogger('AnalysisView');
|
const logger = createLogger('AnalysisView');
|
||||||
@@ -742,7 +742,7 @@ ${Object.entries(projectAnalysis.filesByExtension)
|
|||||||
<Button onClick={runAnalysis} disabled={isAnalyzing} data-testid="analyze-project-button">
|
<Button onClick={runAnalysis} disabled={isAnalyzing} data-testid="analyze-project-button">
|
||||||
{isAnalyzing ? (
|
{isAnalyzing ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
<Spinner size="sm" className="mr-2" />
|
||||||
Analyzing...
|
Analyzing...
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
@@ -771,7 +771,7 @@ ${Object.entries(projectAnalysis.filesByExtension)
|
|||||||
</div>
|
</div>
|
||||||
) : isAnalyzing ? (
|
) : isAnalyzing ? (
|
||||||
<div className="flex flex-col items-center justify-center h-full">
|
<div className="flex flex-col items-center justify-center h-full">
|
||||||
<Loader2 className="w-12 h-12 animate-spin text-primary mb-4" />
|
<Spinner size="xl" className="mb-4" />
|
||||||
<p className="text-muted-foreground">Scanning project files...</p>
|
<p className="text-muted-foreground">Scanning project files...</p>
|
||||||
</div>
|
</div>
|
||||||
) : projectAnalysis ? (
|
) : projectAnalysis ? (
|
||||||
@@ -850,7 +850,7 @@ ${Object.entries(projectAnalysis.filesByExtension)
|
|||||||
>
|
>
|
||||||
{isGeneratingSpec ? (
|
{isGeneratingSpec ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
<Spinner size="sm" className="mr-2" />
|
||||||
Generating...
|
Generating...
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
@@ -903,7 +903,7 @@ ${Object.entries(projectAnalysis.filesByExtension)
|
|||||||
>
|
>
|
||||||
{isGeneratingFeatureList ? (
|
{isGeneratingFeatureList ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
<Spinner size="sm" className="mr-2" />
|
||||||
Generating...
|
Generating...
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ import { pathsEqual } from '@/lib/utils';
|
|||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { getBlockingDependencies } from '@automaker/dependency-resolver';
|
import { getBlockingDependencies } from '@automaker/dependency-resolver';
|
||||||
import { BoardBackgroundModal } from '@/components/dialogs/board-background-modal';
|
import { BoardBackgroundModal } from '@/components/dialogs/board-background-modal';
|
||||||
import { RefreshCw } from 'lucide-react';
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { useAutoMode } from '@/hooks/use-auto-mode';
|
import { useAutoMode } from '@/hooks/use-auto-mode';
|
||||||
import { useKeyboardShortcutsConfig } from '@/hooks/use-keyboard-shortcuts';
|
import { useKeyboardShortcutsConfig } from '@/hooks/use-keyboard-shortcuts';
|
||||||
import { useWindowState } from '@/hooks/use-window-state';
|
import { useWindowState } from '@/hooks/use-window-state';
|
||||||
@@ -1384,7 +1384,7 @@ export function BoardView() {
|
|||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<div className="flex-1 flex items-center justify-center" data-testid="board-view-loading">
|
<div className="flex-1 flex items-center justify-center" data-testid="board-view-loading">
|
||||||
<RefreshCw className="w-6 h-6 animate-spin text-muted-foreground" />
|
<Spinner size="lg" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useRef, useEffect } from 'react';
|
import { useRef, useEffect } from 'react';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import { Search, X, Loader2 } from 'lucide-react';
|
import { Search, X } from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
|
|
||||||
interface BoardSearchBarProps {
|
interface BoardSearchBarProps {
|
||||||
searchQuery: string;
|
searchQuery: string;
|
||||||
@@ -75,7 +76,7 @@ export function BoardSearchBar({
|
|||||||
title="Creating App Specification"
|
title="Creating App Specification"
|
||||||
data-testid="spec-creation-badge"
|
data-testid="spec-creation-badge"
|
||||||
>
|
>
|
||||||
<Loader2 className="w-3 h-3 animate-spin text-brand-500 shrink-0" />
|
<Spinner size="xs" className="shrink-0" />
|
||||||
<span className="text-xs font-medium text-brand-500 whitespace-nowrap">
|
<span className="text-xs font-medium text-brand-500 whitespace-nowrap">
|
||||||
Creating spec
|
Creating spec
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -11,16 +11,8 @@ import {
|
|||||||
} from '@/lib/agent-context-parser';
|
} from '@/lib/agent-context-parser';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import type { AutoModeEvent } from '@/types/electron';
|
import type { AutoModeEvent } from '@/types/electron';
|
||||||
import {
|
import { Brain, ListTodo, Sparkles, Expand, CheckCircle2, Circle, Wrench } from 'lucide-react';
|
||||||
Brain,
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
ListTodo,
|
|
||||||
Sparkles,
|
|
||||||
Expand,
|
|
||||||
CheckCircle2,
|
|
||||||
Circle,
|
|
||||||
Loader2,
|
|
||||||
Wrench,
|
|
||||||
} from 'lucide-react';
|
|
||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
import { SummaryDialog } from './summary-dialog';
|
import { SummaryDialog } from './summary-dialog';
|
||||||
import { getProviderIconForModel } from '@/components/ui/provider-icon';
|
import { getProviderIconForModel } from '@/components/ui/provider-icon';
|
||||||
@@ -338,7 +330,7 @@ export function AgentInfoPanel({
|
|||||||
{todo.status === 'completed' ? (
|
{todo.status === 'completed' ? (
|
||||||
<CheckCircle2 className="w-2.5 h-2.5 text-[var(--status-success)] shrink-0" />
|
<CheckCircle2 className="w-2.5 h-2.5 text-[var(--status-success)] shrink-0" />
|
||||||
) : todo.status === 'in_progress' ? (
|
) : todo.status === 'in_progress' ? (
|
||||||
<Loader2 className="w-2.5 h-2.5 text-[var(--status-warning)] animate-spin shrink-0" />
|
<Spinner size="xs" className="w-2.5 h-2.5 shrink-0" />
|
||||||
) : (
|
) : (
|
||||||
<Circle className="w-2.5 h-2.5 text-muted-foreground/50 shrink-0" />
|
<Circle className="w-2.5 h-2.5 text-muted-foreground/50 shrink-0" />
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import {
|
|||||||
import {
|
import {
|
||||||
GripVertical,
|
GripVertical,
|
||||||
Edit,
|
Edit,
|
||||||
Loader2,
|
|
||||||
Trash2,
|
Trash2,
|
||||||
FileText,
|
FileText,
|
||||||
MoreVertical,
|
MoreVertical,
|
||||||
@@ -21,6 +20,7 @@ import {
|
|||||||
ChevronUp,
|
ChevronUp,
|
||||||
GitFork,
|
GitFork,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { CountUpTimer } from '@/components/ui/count-up-timer';
|
import { CountUpTimer } from '@/components/ui/count-up-timer';
|
||||||
import { formatModelName, DEFAULT_MODEL } from '@/lib/agent-context-parser';
|
import { formatModelName, DEFAULT_MODEL } from '@/lib/agent-context-parser';
|
||||||
import { DeleteConfirmDialog } from '@/components/ui/delete-confirm-dialog';
|
import { DeleteConfirmDialog } from '@/components/ui/delete-confirm-dialog';
|
||||||
@@ -65,7 +65,7 @@ export function CardHeaderSection({
|
|||||||
{isCurrentAutoTask && !isSelectionMode && (
|
{isCurrentAutoTask && !isSelectionMode && (
|
||||||
<div className="absolute top-2 right-2 flex items-center gap-1">
|
<div className="absolute top-2 right-2 flex items-center gap-1">
|
||||||
<div className="flex items-center justify-center gap-2 bg-[var(--status-in-progress)]/15 border border-[var(--status-in-progress)]/50 rounded-md px-2 py-0.5">
|
<div className="flex items-center justify-center gap-2 bg-[var(--status-in-progress)]/15 border border-[var(--status-in-progress)]/50 rounded-md px-2 py-0.5">
|
||||||
<Loader2 className="w-3.5 h-3.5 text-[var(--status-in-progress)] animate-spin" />
|
<Spinner size="xs" />
|
||||||
{feature.startedAt && (
|
{feature.startedAt && (
|
||||||
<CountUpTimer
|
<CountUpTimer
|
||||||
startedAt={feature.startedAt}
|
startedAt={feature.startedAt}
|
||||||
@@ -324,7 +324,7 @@ export function CardHeaderSection({
|
|||||||
<div className="flex-1 min-w-0 overflow-hidden">
|
<div className="flex-1 min-w-0 overflow-hidden">
|
||||||
{feature.titleGenerating ? (
|
{feature.titleGenerating ? (
|
||||||
<div className="flex items-center gap-1.5 mb-1">
|
<div className="flex items-center gap-1.5 mb-1">
|
||||||
<Loader2 className="w-3 h-3 animate-spin text-muted-foreground" />
|
<Spinner size="xs" />
|
||||||
<span className="text-xs text-muted-foreground italic">Generating title...</span>
|
<span className="text-xs text-muted-foreground italic">Generating title...</span>
|
||||||
</div>
|
</div>
|
||||||
) : feature.title ? (
|
) : feature.title ? (
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ import {
|
|||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from '@/components/ui/dialog';
|
} from '@/components/ui/dialog';
|
||||||
import { Loader2, List, FileText, GitBranch, ClipboardList } from 'lucide-react';
|
import { List, FileText, GitBranch, ClipboardList } from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
import { LogViewer } from '@/components/ui/log-viewer';
|
import { LogViewer } from '@/components/ui/log-viewer';
|
||||||
import { GitDiffPanel } from '@/components/ui/git-diff-panel';
|
import { GitDiffPanel } from '@/components/ui/git-diff-panel';
|
||||||
@@ -353,7 +354,7 @@ export function AgentOutputModal({
|
|||||||
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2 pr-8">
|
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2 pr-8">
|
||||||
<DialogTitle className="flex items-center gap-2">
|
<DialogTitle className="flex items-center gap-2">
|
||||||
{featureStatus !== 'verified' && featureStatus !== 'waiting_approval' && (
|
{featureStatus !== 'verified' && featureStatus !== 'waiting_approval' && (
|
||||||
<Loader2 className="w-5 h-5 text-primary animate-spin" />
|
<Spinner size="md" />
|
||||||
)}
|
)}
|
||||||
Agent Output
|
Agent Output
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
@@ -439,7 +440,7 @@ export function AgentOutputModal({
|
|||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex items-center justify-center h-full text-muted-foreground">
|
<div className="flex items-center justify-center h-full text-muted-foreground">
|
||||||
<Loader2 className="w-6 h-6 animate-spin mr-2" />
|
<Spinner size="lg" className="mr-2" />
|
||||||
Loading...
|
Loading...
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -457,7 +458,7 @@ export function AgentOutputModal({
|
|||||||
>
|
>
|
||||||
{isLoading && !output ? (
|
{isLoading && !output ? (
|
||||||
<div className="flex items-center justify-center h-full text-muted-foreground">
|
<div className="flex items-center justify-center h-full text-muted-foreground">
|
||||||
<Loader2 className="w-6 h-6 animate-spin mr-2" />
|
<Spinner size="lg" className="mr-2" />
|
||||||
Loading output...
|
Loading output...
|
||||||
</div>
|
</div>
|
||||||
) : !output ? (
|
) : !output ? (
|
||||||
|
|||||||
@@ -11,16 +11,8 @@ import {
|
|||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Textarea } from '@/components/ui/textarea';
|
import { Textarea } from '@/components/ui/textarea';
|
||||||
import { Checkbox } from '@/components/ui/checkbox';
|
import { Checkbox } from '@/components/ui/checkbox';
|
||||||
import {
|
import { Wand2, Check, Plus, Pencil, Trash2, ChevronDown, ChevronRight } from 'lucide-react';
|
||||||
Loader2,
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
Wand2,
|
|
||||||
Check,
|
|
||||||
Plus,
|
|
||||||
Pencil,
|
|
||||||
Trash2,
|
|
||||||
ChevronDown,
|
|
||||||
ChevronRight,
|
|
||||||
} from 'lucide-react';
|
|
||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
@@ -287,8 +279,7 @@ export function BacklogPlanDialog({
|
|||||||
</div>
|
</div>
|
||||||
{isGeneratingPlan && (
|
{isGeneratingPlan && (
|
||||||
<div className="flex items-center gap-2 text-sm text-muted-foreground bg-muted/50 rounded-lg p-3">
|
<div className="flex items-center gap-2 text-sm text-muted-foreground bg-muted/50 rounded-lg p-3">
|
||||||
<Loader2 className="w-4 h-4 animate-spin" />A plan is currently being generated in
|
<Spinner size="sm" />A plan is currently being generated in the background...
|
||||||
the background...
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -405,7 +396,7 @@ export function BacklogPlanDialog({
|
|||||||
case 'applying':
|
case 'applying':
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col items-center justify-center py-12">
|
<div className="flex flex-col items-center justify-center py-12">
|
||||||
<Loader2 className="w-8 h-8 animate-spin text-primary mb-4" />
|
<Spinner size="xl" className="mb-4" />
|
||||||
<p className="text-muted-foreground">Applying changes...</p>
|
<p className="text-muted-foreground">Applying changes...</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -452,7 +443,7 @@ export function BacklogPlanDialog({
|
|||||||
<Button onClick={handleGenerate} disabled={!prompt.trim() || isGeneratingPlan}>
|
<Button onClick={handleGenerate} disabled={!prompt.trim() || isGeneratingPlan}>
|
||||||
{isGeneratingPlan ? (
|
{isGeneratingPlan ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
<Spinner size="sm" className="mr-2" />
|
||||||
Generating...
|
Generating...
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ import {
|
|||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Textarea } from '@/components/ui/textarea';
|
import { Textarea } from '@/components/ui/textarea';
|
||||||
import { Label } from '@/components/ui/label';
|
import { Label } from '@/components/ui/label';
|
||||||
import { GitCommit, Loader2, Sparkles } from 'lucide-react';
|
import { GitCommit, Sparkles } from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { useAppStore } from '@/store/app-store';
|
import { useAppStore } from '@/store/app-store';
|
||||||
@@ -209,7 +210,7 @@ export function CommitWorktreeDialog({
|
|||||||
<Button onClick={handleCommit} disabled={isLoading || isGenerating || !message.trim()}>
|
<Button onClick={handleCommit} disabled={isLoading || isGenerating || !message.trim()}>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
<Spinner size="sm" className="mr-2" />
|
||||||
Committing...
|
Committing...
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ import { Input } from '@/components/ui/input';
|
|||||||
import { Label } from '@/components/ui/label';
|
import { Label } from '@/components/ui/label';
|
||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { GitBranchPlus, Loader2 } from 'lucide-react';
|
import { GitBranchPlus } from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
|
|
||||||
interface WorktreeInfo {
|
interface WorktreeInfo {
|
||||||
path: string;
|
path: string;
|
||||||
@@ -133,7 +134,7 @@ export function CreateBranchDialog({
|
|||||||
<Button onClick={handleCreate} disabled={!branchName.trim() || isCreating}>
|
<Button onClick={handleCreate} disabled={!branchName.trim() || isCreating}>
|
||||||
{isCreating ? (
|
{isCreating ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
<Spinner size="sm" className="mr-2" />
|
||||||
Creating...
|
Creating...
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ import { Textarea } from '@/components/ui/textarea';
|
|||||||
import { Label } from '@/components/ui/label';
|
import { Label } from '@/components/ui/label';
|
||||||
import { Checkbox } from '@/components/ui/checkbox';
|
import { Checkbox } from '@/components/ui/checkbox';
|
||||||
import { BranchAutocomplete } from '@/components/ui/branch-autocomplete';
|
import { BranchAutocomplete } from '@/components/ui/branch-autocomplete';
|
||||||
import { GitPullRequest, Loader2, ExternalLink } from 'lucide-react';
|
import { GitPullRequest, ExternalLink } from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
|
|
||||||
@@ -405,7 +406,7 @@ export function CreatePRDialog({
|
|||||||
<Button onClick={handleCreate} disabled={isLoading}>
|
<Button onClick={handleCreate} disabled={isLoading}>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
<Spinner size="sm" className="mr-2" />
|
||||||
Creating...
|
Creating...
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ import {
|
|||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import { Label } from '@/components/ui/label';
|
import { Label } from '@/components/ui/label';
|
||||||
import { GitBranch, Loader2, AlertCircle } from 'lucide-react';
|
import { GitBranch, AlertCircle } from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
|
|
||||||
@@ -216,7 +217,7 @@ export function CreateWorktreeDialog({
|
|||||||
<Button onClick={handleCreate} disabled={isLoading || !branchName.trim()}>
|
<Button onClick={handleCreate} disabled={isLoading || !branchName.trim()}>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
<Spinner size="sm" className="mr-2" />
|
||||||
Creating...
|
Creating...
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ import {
|
|||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Checkbox } from '@/components/ui/checkbox';
|
import { Checkbox } from '@/components/ui/checkbox';
|
||||||
import { Label } from '@/components/ui/label';
|
import { Label } from '@/components/ui/label';
|
||||||
import { Loader2, Trash2, AlertTriangle, FileWarning } from 'lucide-react';
|
import { Trash2, AlertTriangle, FileWarning } from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
|
|
||||||
@@ -147,7 +148,7 @@ export function DeleteWorktreeDialog({
|
|||||||
<Button variant="destructive" onClick={handleDelete} disabled={isLoading}>
|
<Button variant="destructive" onClick={handleDelete} disabled={isLoading}>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
<Spinner size="sm" className="mr-2" />
|
||||||
Deleting...
|
Deleting...
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ import {
|
|||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import { Label } from '@/components/ui/label';
|
import { Label } from '@/components/ui/label';
|
||||||
import { Loader2, GitMerge, AlertTriangle, CheckCircle2 } from 'lucide-react';
|
import { GitMerge, AlertTriangle, CheckCircle2 } from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
|
|
||||||
@@ -217,7 +218,7 @@ export function MergeWorktreeDialog({
|
|||||||
>
|
>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
<Spinner size="sm" className="mr-2" />
|
||||||
Merging...
|
Merging...
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ import { Textarea } from '@/components/ui/textarea';
|
|||||||
import { Markdown } from '@/components/ui/markdown';
|
import { Markdown } from '@/components/ui/markdown';
|
||||||
import { Label } from '@/components/ui/label';
|
import { Label } from '@/components/ui/label';
|
||||||
import { Feature } from '@/store/app-store';
|
import { Feature } from '@/store/app-store';
|
||||||
import { Check, RefreshCw, Edit2, Eye, Loader2 } from 'lucide-react';
|
import { Check, RefreshCw, Edit2, Eye } from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
|
|
||||||
interface PlanApprovalDialogProps {
|
interface PlanApprovalDialogProps {
|
||||||
open: boolean;
|
open: boolean;
|
||||||
@@ -171,7 +172,7 @@ export function PlanApprovalDialog({
|
|||||||
</Button>
|
</Button>
|
||||||
<Button variant="secondary" onClick={handleReject} disabled={isLoading}>
|
<Button variant="secondary" onClick={handleReject} disabled={isLoading}>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
<Spinner size="sm" className="mr-2" />
|
||||||
) : (
|
) : (
|
||||||
<RefreshCw className="w-4 h-4 mr-2" />
|
<RefreshCw className="w-4 h-4 mr-2" />
|
||||||
)}
|
)}
|
||||||
@@ -190,7 +191,7 @@ export function PlanApprovalDialog({
|
|||||||
className="bg-green-600 hover:bg-green-700 text-white"
|
className="bg-green-600 hover:bg-green-700 text-white"
|
||||||
>
|
>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
<Spinner size="sm" className="mr-2" />
|
||||||
) : (
|
) : (
|
||||||
<Check className="w-4 h-4 mr-2" />
|
<Check className="w-4 h-4 mr-2" />
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useState, useRef, useEffect, useCallback } from 'react';
|
import { useState, useRef, useEffect, useCallback } from 'react';
|
||||||
import { Terminal, Check, X, Loader2, ChevronDown, ChevronUp } from 'lucide-react';
|
import { Terminal, Check, X, ChevronDown, ChevronUp } from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { useAppStore, type InitScriptState } from '@/store/app-store';
|
import { useAppStore, type InitScriptState } from '@/store/app-store';
|
||||||
import { AnsiOutput } from '@/components/ui/ansi-output';
|
import { AnsiOutput } from '@/components/ui/ansi-output';
|
||||||
@@ -65,7 +66,7 @@ function SingleIndicator({
|
|||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="flex items-center justify-between p-3 border-b border-border/50">
|
<div className="flex items-center justify-between p-3 border-b border-border/50">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{status === 'running' && <Loader2 className="w-4 h-4 animate-spin text-blue-500" />}
|
{status === 'running' && <Spinner size="sm" />}
|
||||||
{status === 'success' && <Check className="w-4 h-4 text-green-500" />}
|
{status === 'success' && <Check className="w-4 h-4 text-green-500" />}
|
||||||
{status === 'failed' && <X className="w-4 h-4 text-red-500" />}
|
{status === 'failed' && <X className="w-4 h-4 text-red-500" />}
|
||||||
<span className="font-medium text-sm">
|
<span className="font-medium text-sm">
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useEffect, useCallback, useState, type ComponentType, type ReactNode } from 'react';
|
import { useEffect, useCallback, useState, type ComponentType, type ReactNode } from 'react';
|
||||||
import { RefreshCw } from 'lucide-react';
|
import { RefreshCw } from 'lucide-react';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
import { useAppStore } from '@/store/app-store';
|
import { useAppStore } from '@/store/app-store';
|
||||||
import { AnthropicIcon, OpenAIIcon } from '@/components/ui/provider-icon';
|
import { AnthropicIcon, OpenAIIcon } from '@/components/ui/provider-icon';
|
||||||
@@ -90,9 +91,11 @@ function UsageItem({
|
|||||||
className="p-1 rounded hover:bg-accent/50 transition-colors"
|
className="p-1 rounded hover:bg-accent/50 transition-colors"
|
||||||
title="Refresh usage"
|
title="Refresh usage"
|
||||||
>
|
>
|
||||||
<RefreshCw
|
{isLoading ? (
|
||||||
className={cn('w-3.5 h-3.5 text-muted-foreground', isLoading && 'animate-spin')}
|
<Spinner size="xs" />
|
||||||
/>
|
) : (
|
||||||
|
<RefreshCw className="w-3.5 h-3.5 text-muted-foreground" />
|
||||||
|
)}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="pl-6 space-y-2">{children}</div>
|
<div className="pl-6 space-y-2">{children}</div>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { getModelProvider, PROVIDER_PREFIXES, stripProviderPrefix } from '@autom
|
|||||||
import type { ModelProvider } from '@automaker/types';
|
import type { ModelProvider } from '@automaker/types';
|
||||||
import { CLAUDE_MODELS, CURSOR_MODELS, ModelOption } from './model-constants';
|
import { CLAUDE_MODELS, CURSOR_MODELS, ModelOption } from './model-constants';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { RefreshCw } from 'lucide-react';
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
|
|
||||||
interface ModelSelectorProps {
|
interface ModelSelectorProps {
|
||||||
selectedModel: string; // Can be ModelAlias or "cursor-{id}"
|
selectedModel: string; // Can be ModelAlias or "cursor-{id}"
|
||||||
@@ -294,7 +294,7 @@ export function ModelSelector({
|
|||||||
{/* Loading state */}
|
{/* Loading state */}
|
||||||
{codexModelsLoading && dynamicCodexModels.length === 0 && (
|
{codexModelsLoading && dynamicCodexModels.length === 0 && (
|
||||||
<div className="flex items-center justify-center gap-2 p-6 text-sm text-muted-foreground">
|
<div className="flex items-center justify-center gap-2 p-6 text-sm text-muted-foreground">
|
||||||
<RefreshCw className="w-4 h-4 animate-spin" />
|
<Spinner size="sm" />
|
||||||
Loading models...
|
Loading models...
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -6,12 +6,12 @@ import {
|
|||||||
ClipboardList,
|
ClipboardList,
|
||||||
FileText,
|
FileText,
|
||||||
ScrollText,
|
ScrollText,
|
||||||
Loader2,
|
|
||||||
Check,
|
Check,
|
||||||
Eye,
|
Eye,
|
||||||
RefreshCw,
|
RefreshCw,
|
||||||
Sparkles,
|
Sparkles,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { Label } from '@/components/ui/label';
|
import { Label } from '@/components/ui/label';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Checkbox } from '@/components/ui/checkbox';
|
import { Checkbox } from '@/components/ui/checkbox';
|
||||||
@@ -236,7 +236,7 @@ export function PlanningModeSelector({
|
|||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{isGenerating ? (
|
{isGenerating ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="h-4 w-4 animate-spin text-primary" />
|
<Spinner size="sm" />
|
||||||
<span className="text-sm text-muted-foreground">
|
<span className="text-sm text-muted-foreground">
|
||||||
Generating {mode === 'full' ? 'comprehensive spec' : 'spec'}...
|
Generating {mode === 'full' ? 'comprehensive spec' : 'spec'}...
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ import {
|
|||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
DropdownMenuLabel,
|
DropdownMenuLabel,
|
||||||
} from '@/components/ui/dropdown-menu';
|
} from '@/components/ui/dropdown-menu';
|
||||||
import { GitBranch, RefreshCw, GitBranchPlus, Check, Search } from 'lucide-react';
|
import { GitBranch, GitBranchPlus, Check, Search } from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import type { WorktreeInfo, BranchInfo } from '../types';
|
import type { WorktreeInfo, BranchInfo } from '../types';
|
||||||
|
|
||||||
@@ -81,7 +82,7 @@ export function BranchSwitchDropdown({
|
|||||||
<div className="max-h-[250px] overflow-y-auto">
|
<div className="max-h-[250px] overflow-y-auto">
|
||||||
{isLoadingBranches ? (
|
{isLoadingBranches ? (
|
||||||
<DropdownMenuItem disabled className="text-xs">
|
<DropdownMenuItem disabled className="text-xs">
|
||||||
<RefreshCw className="w-3.5 h-3.5 mr-2 animate-spin" />
|
<Spinner size="xs" className="mr-2" />
|
||||||
Loading branches...
|
Loading branches...
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
) : filteredBranches.length === 0 ? (
|
) : filteredBranches.length === 0 ? (
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { useEffect, useRef, useCallback, useState } from 'react';
|
|||||||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
|
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import {
|
import {
|
||||||
Loader2,
|
|
||||||
Terminal,
|
Terminal,
|
||||||
ArrowDown,
|
ArrowDown,
|
||||||
ExternalLink,
|
ExternalLink,
|
||||||
@@ -12,6 +11,7 @@ import {
|
|||||||
Clock,
|
Clock,
|
||||||
GitBranch,
|
GitBranch,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { XtermLogViewer, type XtermLogViewerRef } from '@/components/ui/xterm-log-viewer';
|
import { XtermLogViewer, type XtermLogViewerRef } from '@/components/ui/xterm-log-viewer';
|
||||||
import { useDevServerLogs } from '../hooks/use-dev-server-logs';
|
import { useDevServerLogs } from '../hooks/use-dev-server-logs';
|
||||||
@@ -183,7 +183,7 @@ export function DevServerLogsPanel({
|
|||||||
onClick={() => fetchLogs()}
|
onClick={() => fetchLogs()}
|
||||||
title="Refresh logs"
|
title="Refresh logs"
|
||||||
>
|
>
|
||||||
<RefreshCw className={cn('w-3.5 h-3.5', isLoading && 'animate-spin')} />
|
{isLoading ? <Spinner size="xs" /> : <RefreshCw className="w-3.5 h-3.5" />}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -234,7 +234,7 @@ export function DevServerLogsPanel({
|
|||||||
>
|
>
|
||||||
{isLoading && !logs ? (
|
{isLoading && !logs ? (
|
||||||
<div className="flex items-center justify-center h-full min-h-[300px] text-muted-foreground">
|
<div className="flex items-center justify-center h-full min-h-[300px] text-muted-foreground">
|
||||||
<Loader2 className="w-5 h-5 animate-spin mr-2" />
|
<Spinner size="md" className="mr-2" />
|
||||||
<span className="text-sm">Loading logs...</span>
|
<span className="text-sm">Loading logs...</span>
|
||||||
</div>
|
</div>
|
||||||
) : !logs && !isRunning ? (
|
) : !logs && !isRunning ? (
|
||||||
@@ -245,7 +245,7 @@ export function DevServerLogsPanel({
|
|||||||
</div>
|
</div>
|
||||||
) : !logs ? (
|
) : !logs ? (
|
||||||
<div className="flex flex-col items-center justify-center h-full min-h-[300px] text-muted-foreground p-8">
|
<div className="flex flex-col items-center justify-center h-full min-h-[300px] text-muted-foreground p-8">
|
||||||
<div className="w-8 h-8 mb-3 rounded-full border-2 border-muted-foreground/20 border-t-muted-foreground/60 animate-spin" />
|
<Spinner size="xl" className="mb-3" />
|
||||||
<p className="text-sm">Waiting for output...</p>
|
<p className="text-sm">Waiting for output...</p>
|
||||||
<p className="text-xs mt-1 opacity-60">
|
<p className="text-xs mt-1 opacity-60">
|
||||||
Logs will appear as the server generates output
|
Logs will appear as the server generates output
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ import {
|
|||||||
DropdownMenuSeparator,
|
DropdownMenuSeparator,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from '@/components/ui/dropdown-menu';
|
} from '@/components/ui/dropdown-menu';
|
||||||
import { GitBranch, ChevronDown, Loader2, CircleDot, Check } from 'lucide-react';
|
import { GitBranch, ChevronDown, CircleDot, Check } from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import type { WorktreeInfo } from '../types';
|
import type { WorktreeInfo } from '../types';
|
||||||
|
|
||||||
@@ -44,7 +45,7 @@ export function WorktreeMobileDropdown({
|
|||||||
<GitBranch className="w-3.5 h-3.5 shrink-0" />
|
<GitBranch className="w-3.5 h-3.5 shrink-0" />
|
||||||
<span className="truncate">{displayBranch}</span>
|
<span className="truncate">{displayBranch}</span>
|
||||||
{isActivating ? (
|
{isActivating ? (
|
||||||
<Loader2 className="w-3 h-3 animate-spin shrink-0" />
|
<Spinner size="xs" className="shrink-0" />
|
||||||
) : (
|
) : (
|
||||||
<ChevronDown className="w-3 h-3 shrink-0 ml-auto" />
|
<ChevronDown className="w-3 h-3 shrink-0 ml-auto" />
|
||||||
)}
|
)}
|
||||||
@@ -74,7 +75,7 @@ export function WorktreeMobileDropdown({
|
|||||||
) : (
|
) : (
|
||||||
<div className="w-3.5 h-3.5 shrink-0" />
|
<div className="w-3.5 h-3.5 shrink-0" />
|
||||||
)}
|
)}
|
||||||
{isRunning && <Loader2 className="w-3 h-3 animate-spin shrink-0" />}
|
{isRunning && <Spinner size="xs" className="shrink-0" />}
|
||||||
<span className={cn('font-mono text-xs truncate', isSelected && 'font-medium')}>
|
<span className={cn('font-mono text-xs truncate', isSelected && 'font-medium')}>
|
||||||
{worktree.branch}
|
{worktree.branch}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import type { JSX } from 'react';
|
import type { JSX } from 'react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { RefreshCw, Globe, Loader2, CircleDot, GitPullRequest } from 'lucide-react';
|
import { Globe, CircleDot, GitPullRequest } from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
|
||||||
import type { WorktreeInfo, BranchInfo, DevServerInfo, PRInfo, GitRepoStatus } from '../types';
|
import type { WorktreeInfo, BranchInfo, DevServerInfo, PRInfo, GitRepoStatus } from '../types';
|
||||||
@@ -197,8 +198,8 @@ export function WorktreeTab({
|
|||||||
aria-label={worktree.branch}
|
aria-label={worktree.branch}
|
||||||
data-testid={`worktree-branch-${worktree.branch}`}
|
data-testid={`worktree-branch-${worktree.branch}`}
|
||||||
>
|
>
|
||||||
{isRunning && <Loader2 className="w-3 h-3 animate-spin" />}
|
{isRunning && <Spinner size="xs" />}
|
||||||
{isActivating && !isRunning && <RefreshCw className="w-3 h-3 animate-spin" />}
|
{isActivating && !isRunning && <Spinner size="xs" />}
|
||||||
{worktree.branch}
|
{worktree.branch}
|
||||||
{cardCount !== undefined && cardCount > 0 && (
|
{cardCount !== undefined && cardCount > 0 && (
|
||||||
<span className="inline-flex items-center justify-center h-4 min-w-[1rem] px-1 text-[10px] font-medium rounded bg-background/80 text-foreground border border-border">
|
<span className="inline-flex items-center justify-center h-4 min-w-[1rem] px-1 text-[10px] font-medium rounded bg-background/80 text-foreground border border-border">
|
||||||
@@ -264,8 +265,8 @@ export function WorktreeTab({
|
|||||||
: 'Click to switch to this branch'
|
: 'Click to switch to this branch'
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{isRunning && <Loader2 className="w-3 h-3 animate-spin" />}
|
{isRunning && <Spinner size="xs" />}
|
||||||
{isActivating && !isRunning && <RefreshCw className="w-3 h-3 animate-spin" />}
|
{isActivating && !isRunning && <Spinner size="xs" />}
|
||||||
{worktree.branch}
|
{worktree.branch}
|
||||||
{cardCount !== undefined && cardCount > 0 && (
|
{cardCount !== undefined && cardCount > 0 && (
|
||||||
<span className="inline-flex items-center justify-center h-4 min-w-[1rem] px-1 text-[10px] font-medium rounded bg-background/80 text-foreground border border-border">
|
<span className="inline-flex items-center justify-center h-4 min-w-[1rem] px-1 text-[10px] font-medium rounded bg-background/80 text-foreground border border-border">
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useEffect, useRef, useCallback, useState } from 'react';
|
import { useEffect, useRef, useCallback, useState } from 'react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { GitBranch, Plus, RefreshCw } from 'lucide-react';
|
import { GitBranch, Plus, RefreshCw } from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { cn, pathsEqual } from '@/lib/utils';
|
import { cn, pathsEqual } from '@/lib/utils';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { getHttpApiClient } from '@/lib/http-api-client';
|
import { getHttpApiClient } from '@/lib/http-api-client';
|
||||||
@@ -285,7 +286,7 @@ export function WorktreePanel({
|
|||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
title="Refresh worktrees"
|
title="Refresh worktrees"
|
||||||
>
|
>
|
||||||
<RefreshCw className={cn('w-3.5 h-3.5', isLoading && 'animate-spin')} />
|
{isLoading ? <Spinner size="xs" /> : <RefreshCw className="w-3.5 h-3.5" />}
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
@@ -429,7 +430,7 @@ export function WorktreePanel({
|
|||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
title="Refresh worktrees"
|
title="Refresh worktrees"
|
||||||
>
|
>
|
||||||
<RefreshCw className={cn('w-3.5 h-3.5', isLoading && 'animate-spin')} />
|
{isLoading ? <Spinner size="xs" /> : <RefreshCw className="w-3.5 h-3.5" />}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ import { useAppStore } from '@/store/app-store';
|
|||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
import { Card, CardContent } from '@/components/ui/card';
|
import { Card, CardContent } from '@/components/ui/card';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { File, Folder, FolderOpen, ChevronRight, ChevronDown, RefreshCw, Code } from 'lucide-react';
|
import { File, Folder, FolderOpen, ChevronRight, ChevronDown, Code } from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
const logger = createLogger('CodeView');
|
const logger = createLogger('CodeView');
|
||||||
@@ -206,7 +207,7 @@ export function CodeView() {
|
|||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<div className="flex-1 flex items-center justify-center" data-testid="code-view-loading">
|
<div className="flex-1 flex items-center justify-center" data-testid="code-view-loading">
|
||||||
<RefreshCw className="w-6 h-6 animate-spin text-muted-foreground" />
|
<Spinner size="lg" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import {
|
|||||||
HeaderActionsPanelTrigger,
|
HeaderActionsPanelTrigger,
|
||||||
} from '@/components/ui/header-actions-panel';
|
} from '@/components/ui/header-actions-panel';
|
||||||
import {
|
import {
|
||||||
RefreshCw,
|
|
||||||
FileText,
|
FileText,
|
||||||
Image as ImageIcon,
|
Image as ImageIcon,
|
||||||
Trash2,
|
Trash2,
|
||||||
@@ -24,9 +23,9 @@ import {
|
|||||||
Pencil,
|
Pencil,
|
||||||
FilePlus,
|
FilePlus,
|
||||||
FileUp,
|
FileUp,
|
||||||
Loader2,
|
|
||||||
MoreVertical,
|
MoreVertical,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import {
|
import {
|
||||||
useKeyboardShortcuts,
|
useKeyboardShortcuts,
|
||||||
useKeyboardShortcutsConfig,
|
useKeyboardShortcutsConfig,
|
||||||
@@ -670,7 +669,7 @@ export function ContextView() {
|
|||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<div className="flex-1 flex items-center justify-center" data-testid="context-view-loading">
|
<div className="flex-1 flex items-center justify-center" data-testid="context-view-loading">
|
||||||
<RefreshCw className="w-6 h-6 animate-spin text-muted-foreground" />
|
<Spinner size="lg" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -790,7 +789,7 @@ export function ContextView() {
|
|||||||
{isUploading && (
|
{isUploading && (
|
||||||
<div className="absolute inset-0 bg-background/80 z-50 flex items-center justify-center">
|
<div className="absolute inset-0 bg-background/80 z-50 flex items-center justify-center">
|
||||||
<div className="flex flex-col items-center">
|
<div className="flex flex-col items-center">
|
||||||
<Loader2 className="w-8 h-8 animate-spin text-primary mb-2" />
|
<Spinner size="xl" className="mb-2" />
|
||||||
<span className="text-sm font-medium">Uploading {uploadingFileName}...</span>
|
<span className="text-sm font-medium">Uploading {uploadingFileName}...</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -838,7 +837,7 @@ export function ContextView() {
|
|||||||
<span className="truncate text-sm block">{file.name}</span>
|
<span className="truncate text-sm block">{file.name}</span>
|
||||||
{isGenerating ? (
|
{isGenerating ? (
|
||||||
<span className="flex items-center gap-1 text-xs text-muted-foreground">
|
<span className="flex items-center gap-1 text-xs text-muted-foreground">
|
||||||
<Loader2 className="w-3 h-3 animate-spin" />
|
<Spinner size="xs" />
|
||||||
Generating description...
|
Generating description...
|
||||||
</span>
|
</span>
|
||||||
) : file.description ? (
|
) : file.description ? (
|
||||||
@@ -955,7 +954,7 @@ export function ContextView() {
|
|||||||
</span>
|
</span>
|
||||||
{generatingDescriptions.has(selectedFile.name) ? (
|
{generatingDescriptions.has(selectedFile.name) ? (
|
||||||
<div className="flex items-center gap-2 mt-1 text-sm text-muted-foreground">
|
<div className="flex items-center gap-2 mt-1 text-sm text-muted-foreground">
|
||||||
<Loader2 className="w-4 h-4 animate-spin" />
|
<Spinner size="sm" />
|
||||||
<span>Generating description with AI...</span>
|
<span>Generating description with AI...</span>
|
||||||
</div>
|
</div>
|
||||||
) : selectedFile.description ? (
|
) : selectedFile.description ? (
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import {
|
|||||||
Folder,
|
Folder,
|
||||||
Star,
|
Star,
|
||||||
Clock,
|
Clock,
|
||||||
Loader2,
|
|
||||||
ChevronDown,
|
ChevronDown,
|
||||||
MessageSquare,
|
MessageSquare,
|
||||||
MoreVertical,
|
MoreVertical,
|
||||||
@@ -28,6 +27,7 @@ import {
|
|||||||
type LucideIcon,
|
type LucideIcon,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import * as LucideIcons from 'lucide-react';
|
import * as LucideIcons from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import { getAuthenticatedImageUrl } from '@/lib/api-fetch';
|
import { getAuthenticatedImageUrl } from '@/lib/api-fetch';
|
||||||
import {
|
import {
|
||||||
@@ -992,7 +992,7 @@ export function DashboardView() {
|
|||||||
data-testid="project-opening-overlay"
|
data-testid="project-opening-overlay"
|
||||||
>
|
>
|
||||||
<div className="flex flex-col items-center gap-4 p-8 rounded-2xl bg-card border border-border shadow-2xl">
|
<div className="flex flex-col items-center gap-4 p-8 rounded-2xl bg-card border border-border shadow-2xl">
|
||||||
<Loader2 className="w-10 h-10 text-brand-500 animate-spin" />
|
<Spinner size="xl" />
|
||||||
<p className="text-foreground font-medium">Opening project...</p>
|
<p className="text-foreground font-medium">Opening project...</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import {
|
|||||||
X,
|
X,
|
||||||
Wand2,
|
Wand2,
|
||||||
ExternalLink,
|
ExternalLink,
|
||||||
Loader2,
|
|
||||||
CheckCircle,
|
CheckCircle,
|
||||||
Clock,
|
Clock,
|
||||||
GitPullRequest,
|
GitPullRequest,
|
||||||
@@ -14,6 +13,7 @@ import {
|
|||||||
ChevronDown,
|
ChevronDown,
|
||||||
ChevronUp,
|
ChevronUp,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Checkbox } from '@/components/ui/checkbox';
|
import { Checkbox } from '@/components/ui/checkbox';
|
||||||
@@ -87,7 +87,7 @@ export function IssueDetailPanel({
|
|||||||
if (isValidating) {
|
if (isValidating) {
|
||||||
return (
|
return (
|
||||||
<Button variant="default" size="sm" disabled>
|
<Button variant="default" size="sm" disabled>
|
||||||
<Loader2 className="h-4 w-4 mr-1 animate-spin" />
|
<Spinner size="sm" className="mr-1" />
|
||||||
Validating...
|
Validating...
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
@@ -297,9 +297,7 @@ export function IssueDetailPanel({
|
|||||||
<span className="text-sm font-medium">
|
<span className="text-sm font-medium">
|
||||||
Comments {totalCount > 0 && `(${totalCount})`}
|
Comments {totalCount > 0 && `(${totalCount})`}
|
||||||
</span>
|
</span>
|
||||||
{commentsLoading && (
|
{commentsLoading && <Spinner size="xs" />}
|
||||||
<Loader2 className="h-3 w-3 animate-spin text-muted-foreground" />
|
|
||||||
)}
|
|
||||||
{commentsExpanded ? (
|
{commentsExpanded ? (
|
||||||
<ChevronUp className="h-4 w-4 text-muted-foreground" />
|
<ChevronUp className="h-4 w-4 text-muted-foreground" />
|
||||||
) : (
|
) : (
|
||||||
@@ -340,7 +338,7 @@ export function IssueDetailPanel({
|
|||||||
>
|
>
|
||||||
{loadingMore ? (
|
{loadingMore ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="h-4 w-4 mr-2 animate-spin" />
|
<Spinner size="sm" className="mr-2" />
|
||||||
Loading...
|
Loading...
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ import {
|
|||||||
Circle,
|
Circle,
|
||||||
CheckCircle2,
|
CheckCircle2,
|
||||||
ExternalLink,
|
ExternalLink,
|
||||||
Loader2,
|
|
||||||
CheckCircle,
|
CheckCircle,
|
||||||
Sparkles,
|
Sparkles,
|
||||||
GitPullRequest,
|
GitPullRequest,
|
||||||
User,
|
User,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import type { IssueRowProps } from '../types';
|
import type { IssueRowProps } from '../types';
|
||||||
@@ -97,7 +97,7 @@ export function IssueRow({
|
|||||||
{/* Validating indicator */}
|
{/* Validating indicator */}
|
||||||
{isValidating && (
|
{isValidating && (
|
||||||
<span className="inline-flex items-center gap-1 px-1.5 py-0.5 text-[10px] font-medium rounded-full bg-primary/10 text-primary border border-primary/20 animate-in fade-in duration-200">
|
<span className="inline-flex items-center gap-1 px-1.5 py-0.5 text-[10px] font-medium rounded-full bg-primary/10 text-primary border border-primary/20 animate-in fade-in duration-200">
|
||||||
<Loader2 className="h-3 w-3 animate-spin" />
|
<Spinner size="xs" />
|
||||||
Analyzing...
|
Analyzing...
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { CircleDot, RefreshCw } from 'lucide-react';
|
import { CircleDot, RefreshCw } from 'lucide-react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import type { IssuesStateFilter } from '../types';
|
import type { IssuesStateFilter } from '../types';
|
||||||
import { IssuesFilterControls } from './issues-filter-controls';
|
import { IssuesFilterControls } from './issues-filter-controls';
|
||||||
@@ -77,7 +78,7 @@ export function IssuesListHeader({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Button variant="outline" size="sm" onClick={onRefresh} disabled={refreshing}>
|
<Button variant="outline" size="sm" onClick={onRefresh} disabled={refreshing}>
|
||||||
<RefreshCw className={cn('h-4 w-4', refreshing && 'animate-spin')} />
|
{refreshing ? <Spinner size="sm" /> : <RefreshCw className="h-4 w-4" />}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useState, useEffect, useCallback } from 'react';
|
import { useState, useEffect, useCallback } from 'react';
|
||||||
import { createLogger } from '@automaker/utils/logger';
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { GitPullRequest, Loader2, RefreshCw, ExternalLink, GitMerge, X } from 'lucide-react';
|
import { GitPullRequest, RefreshCw, ExternalLink, GitMerge, X } from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { getElectronAPI, GitHubPR } from '@/lib/electron';
|
import { getElectronAPI, GitHubPR } from '@/lib/electron';
|
||||||
import { useAppStore } from '@/store/app-store';
|
import { useAppStore } from '@/store/app-store';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
@@ -86,7 +87,7 @@ export function GitHubPRsView() {
|
|||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className="flex-1 flex items-center justify-center">
|
<div className="flex-1 flex items-center justify-center">
|
||||||
<Loader2 className="h-8 w-8 animate-spin text-muted-foreground" />
|
<Spinner size="xl" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -134,7 +135,7 @@ export function GitHubPRsView() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Button variant="outline" size="sm" onClick={handleRefresh} disabled={refreshing}>
|
<Button variant="outline" size="sm" onClick={handleRefresh} disabled={refreshing}>
|
||||||
<RefreshCw className={cn('h-4 w-4', refreshing && 'animate-spin')} />
|
{refreshing ? <Spinner size="sm" /> : <RefreshCw className="h-4 w-4" />}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import {
|
|||||||
import { useWorktrees } from './board-view/worktree-panel/hooks';
|
import { useWorktrees } from './board-view/worktree-panel/hooks';
|
||||||
import { useAutoMode } from '@/hooks/use-auto-mode';
|
import { useAutoMode } from '@/hooks/use-auto-mode';
|
||||||
import { pathsEqual } from '@/lib/utils';
|
import { pathsEqual } from '@/lib/utils';
|
||||||
import { RefreshCw } from 'lucide-react';
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
import { createLogger } from '@automaker/utils/logger';
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
@@ -330,7 +330,7 @@ export function GraphViewPage() {
|
|||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<div className="flex-1 flex items-center justify-center" data-testid="graph-view-loading">
|
<div className="flex-1 flex items-center justify-center" data-testid="graph-view-loading">
|
||||||
<RefreshCw className="w-6 h-6 animate-spin text-muted-foreground" />
|
<Spinner size="lg" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { useState, useMemo, useEffect, useCallback } from 'react';
|
import { useState, useMemo, useEffect, useCallback } from 'react';
|
||||||
import { Loader2, AlertCircle, Plus, X, Sparkles, Lightbulb, Trash2 } from 'lucide-react';
|
import { AlertCircle, Plus, X, Sparkles, Lightbulb, Trash2 } from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { Card, CardContent } from '@/components/ui/card';
|
import { Card, CardContent } from '@/components/ui/card';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Badge } from '@/components/ui/badge';
|
import { Badge } from '@/components/ui/badge';
|
||||||
@@ -109,7 +110,7 @@ function SuggestionCard({
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{isAdding ? (
|
{isAdding ? (
|
||||||
<Loader2 className="w-4 h-4 animate-spin" />
|
<Spinner size="sm" />
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Plus className="w-4 h-4" />
|
<Plus className="w-4 h-4" />
|
||||||
@@ -153,11 +154,7 @@ function GeneratingCard({ job }: { job: GenerationJob }) {
|
|||||||
isError ? 'bg-destructive/10 text-destructive' : 'bg-blue-500/10 text-blue-500'
|
isError ? 'bg-destructive/10 text-destructive' : 'bg-blue-500/10 text-blue-500'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{isError ? (
|
{isError ? <AlertCircle className="w-5 h-5" /> : <Spinner size="md" />}
|
||||||
<AlertCircle className="w-5 h-5" />
|
|
||||||
) : (
|
|
||||||
<Loader2 className="w-5 h-5 animate-spin" />
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="font-medium">{job.prompt.title}</p>
|
<p className="font-medium">{job.prompt.title}</p>
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ import {
|
|||||||
Gauge,
|
Gauge,
|
||||||
Accessibility,
|
Accessibility,
|
||||||
BarChart3,
|
BarChart3,
|
||||||
Loader2,
|
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { Card, CardContent } from '@/components/ui/card';
|
import { Card, CardContent } from '@/components/ui/card';
|
||||||
import { useGuidedPrompts } from '@/hooks/use-guided-prompts';
|
import { useGuidedPrompts } from '@/hooks/use-guided-prompts';
|
||||||
import type { IdeaCategory } from '@automaker/types';
|
import type { IdeaCategory } from '@automaker/types';
|
||||||
@@ -53,7 +53,7 @@ export function PromptCategoryGrid({ onSelect, onBack }: PromptCategoryGridProps
|
|||||||
|
|
||||||
{isLoading && (
|
{isLoading && (
|
||||||
<div className="flex items-center justify-center py-12">
|
<div className="flex items-center justify-center py-12">
|
||||||
<Loader2 className="w-6 h-6 animate-spin text-muted-foreground" />
|
<Spinner size="lg" />
|
||||||
<span className="ml-2 text-muted-foreground">Loading categories...</span>
|
<span className="ml-2 text-muted-foreground">Loading categories...</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -3,7 +3,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { useState, useMemo } from 'react';
|
import { useState, useMemo } from 'react';
|
||||||
import { ArrowLeft, Lightbulb, Loader2, CheckCircle2 } from 'lucide-react';
|
import { ArrowLeft, Lightbulb, CheckCircle2 } from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { Card, CardContent } from '@/components/ui/card';
|
import { Card, CardContent } from '@/components/ui/card';
|
||||||
import { useGuidedPrompts } from '@/hooks/use-guided-prompts';
|
import { useGuidedPrompts } from '@/hooks/use-guided-prompts';
|
||||||
import { useIdeationStore } from '@/store/ideation-store';
|
import { useIdeationStore } from '@/store/ideation-store';
|
||||||
@@ -121,7 +122,7 @@ export function PromptList({ category, onBack }: PromptListProps) {
|
|||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
{isLoadingPrompts && (
|
{isLoadingPrompts && (
|
||||||
<div className="flex items-center justify-center py-8">
|
<div className="flex items-center justify-center py-8">
|
||||||
<Loader2 className="w-6 h-6 animate-spin text-muted-foreground" />
|
<Spinner size="lg" />
|
||||||
<span className="ml-2 text-muted-foreground">Loading prompts...</span>
|
<span className="ml-2 text-muted-foreground">Loading prompts...</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -162,7 +163,7 @@ export function PromptList({ category, onBack }: PromptListProps) {
|
|||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{isLoading || isGenerating ? (
|
{isLoading || isGenerating ? (
|
||||||
<Loader2 className="w-5 h-5 animate-spin" />
|
<Spinner size="md" />
|
||||||
) : isStarted ? (
|
) : isStarted ? (
|
||||||
<CheckCircle2 className="w-5 h-5" />
|
<CheckCircle2 className="w-5 h-5" />
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ import { PromptList } from './components/prompt-list';
|
|||||||
import { IdeationDashboard } from './components/ideation-dashboard';
|
import { IdeationDashboard } from './components/ideation-dashboard';
|
||||||
import { useGuidedPrompts } from '@/hooks/use-guided-prompts';
|
import { useGuidedPrompts } from '@/hooks/use-guided-prompts';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { ArrowLeft, ChevronRight, Lightbulb, CheckCheck, Loader2, Trash2 } from 'lucide-react';
|
import { ArrowLeft, ChevronRight, Lightbulb, CheckCheck, Trash2 } from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import type { IdeaCategory } from '@automaker/types';
|
import type { IdeaCategory } from '@automaker/types';
|
||||||
import type { IdeationMode } from '@/store/ideation-store';
|
import type { IdeationMode } from '@/store/ideation-store';
|
||||||
|
|
||||||
@@ -152,11 +153,7 @@ function IdeationHeader({
|
|||||||
className="gap-2"
|
className="gap-2"
|
||||||
disabled={isAcceptingAll}
|
disabled={isAcceptingAll}
|
||||||
>
|
>
|
||||||
{isAcceptingAll ? (
|
{isAcceptingAll ? <Spinner size="sm" /> : <CheckCheck className="w-4 h-4" />}
|
||||||
<Loader2 className="w-4 h-4 animate-spin" />
|
|
||||||
) : (
|
|
||||||
<CheckCheck className="w-4 h-4" />
|
|
||||||
)}
|
|
||||||
Accept All ({acceptAllCount})
|
Accept All ({acceptAllCount})
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ import { useAppStore, Feature } from '@/store/app-store';
|
|||||||
import { Card, CardContent } from '@/components/ui/card';
|
import { Card, CardContent } from '@/components/ui/card';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import { Bot, Send, User, Loader2, Sparkles, FileText, ArrowLeft, CheckCircle } from 'lucide-react';
|
import { Bot, Send, User, Sparkles, FileText, ArrowLeft, CheckCircle } from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { cn, generateUUID } from '@/lib/utils';
|
import { cn, generateUUID } from '@/lib/utils';
|
||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
import { Markdown } from '@/components/ui/markdown';
|
import { Markdown } from '@/components/ui/markdown';
|
||||||
@@ -491,7 +492,7 @@ export function InterviewView() {
|
|||||||
<Card className="border border-primary/30 bg-card">
|
<Card className="border border-primary/30 bg-card">
|
||||||
<CardContent className="p-3">
|
<CardContent className="p-3">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Loader2 className="w-4 h-4 animate-spin text-primary" />
|
<Spinner size="sm" />
|
||||||
<span className="text-sm text-primary">Generating specification...</span>
|
<span className="text-sm text-primary">Generating specification...</span>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
@@ -571,7 +572,7 @@ export function InterviewView() {
|
|||||||
>
|
>
|
||||||
{isGenerating ? (
|
{isGenerating ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
<Spinner size="sm" className="mr-2" />
|
||||||
Creating...
|
Creating...
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ import {
|
|||||||
} from '@/lib/http-api-client';
|
} from '@/lib/http-api-client';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import { KeyRound, AlertCircle, Loader2, RefreshCw, ServerCrash } from 'lucide-react';
|
import { KeyRound, AlertCircle, RefreshCw, ServerCrash } from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { useAuthStore } from '@/store/auth-store';
|
import { useAuthStore } from '@/store/auth-store';
|
||||||
import { useSetupStore } from '@/store/setup-store';
|
import { useSetupStore } from '@/store/setup-store';
|
||||||
|
|
||||||
@@ -349,7 +350,7 @@ export function LoginView() {
|
|||||||
return (
|
return (
|
||||||
<div className="flex min-h-screen items-center justify-center bg-background p-4">
|
<div className="flex min-h-screen items-center justify-center bg-background p-4">
|
||||||
<div className="text-center space-y-4">
|
<div className="text-center space-y-4">
|
||||||
<Loader2 className="h-8 w-8 animate-spin mx-auto text-primary" />
|
<Spinner size="xl" className="mx-auto" />
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
Connecting to server
|
Connecting to server
|
||||||
{state.attempt > 1 ? ` (attempt ${state.attempt}/${MAX_RETRIES})` : '...'}
|
{state.attempt > 1 ? ` (attempt ${state.attempt}/${MAX_RETRIES})` : '...'}
|
||||||
@@ -385,7 +386,7 @@ export function LoginView() {
|
|||||||
return (
|
return (
|
||||||
<div className="flex min-h-screen items-center justify-center bg-background p-4">
|
<div className="flex min-h-screen items-center justify-center bg-background p-4">
|
||||||
<div className="text-center space-y-4">
|
<div className="text-center space-y-4">
|
||||||
<Loader2 className="h-8 w-8 animate-spin mx-auto text-primary" />
|
<Spinner size="xl" className="mx-auto" />
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
{state.phase === 'checking_setup' ? 'Loading settings...' : 'Redirecting...'}
|
{state.phase === 'checking_setup' ? 'Loading settings...' : 'Redirecting...'}
|
||||||
</p>
|
</p>
|
||||||
@@ -447,7 +448,7 @@ export function LoginView() {
|
|||||||
>
|
>
|
||||||
{isLoggingIn ? (
|
{isLoggingIn ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
<Spinner size="sm" className="mr-2" />
|
||||||
Authenticating...
|
Authenticating...
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import {
|
|||||||
FilePlus,
|
FilePlus,
|
||||||
MoreVertical,
|
MoreVertical,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
@@ -299,7 +300,7 @@ export function MemoryView() {
|
|||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<div className="flex-1 flex items-center justify-center" data-testid="memory-view-loading">
|
<div className="flex-1 flex items-center justify-center" data-testid="memory-view-loading">
|
||||||
<RefreshCw className="w-6 h-6 animate-spin text-muted-foreground" />
|
<Spinner size="lg" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ import { useLoadNotifications, useNotificationEvents } from '@/hooks/use-notific
|
|||||||
import { getHttpApiClient } from '@/lib/http-api-client';
|
import { getHttpApiClient } from '@/lib/http-api-client';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
import { Bell, Check, CheckCheck, Trash2, ExternalLink, Loader2 } from 'lucide-react';
|
import { Bell, Check, CheckCheck, Trash2, ExternalLink } from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { useNavigate } from '@tanstack/react-router';
|
import { useNavigate } from '@tanstack/react-router';
|
||||||
import type { Notification } from '@automaker/types';
|
import type { Notification } from '@automaker/types';
|
||||||
|
|
||||||
@@ -146,7 +147,7 @@ export function NotificationsView() {
|
|||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-1 flex-col items-center justify-center p-8">
|
<div className="flex flex-1 flex-col items-center justify-center p-8">
|
||||||
<Loader2 className="h-8 w-8 animate-spin text-muted-foreground" />
|
<Spinner size="xl" />
|
||||||
<p className="text-muted-foreground mt-4">Loading notifications...</p>
|
<p className="text-muted-foreground mt-4">Loading notifications...</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ import {
|
|||||||
Save,
|
Save,
|
||||||
RotateCcw,
|
RotateCcw,
|
||||||
Trash2,
|
Trash2,
|
||||||
Loader2,
|
|
||||||
PanelBottomClose,
|
PanelBottomClose,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { apiGet, apiPut, apiDelete } from '@/lib/api-fetch';
|
import { apiGet, apiPut, apiDelete } from '@/lib/api-fetch';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
@@ -409,7 +409,7 @@ export function WorktreePreferencesSection({ project }: WorktreePreferencesSecti
|
|||||||
|
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<div className="flex items-center justify-center py-8">
|
<div className="flex items-center justify-center py-8">
|
||||||
<Loader2 className="w-5 h-5 animate-spin text-muted-foreground" />
|
<Spinner size="md" />
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
@@ -448,11 +448,7 @@ npm install
|
|||||||
disabled={!scriptExists || isSaving || isDeleting}
|
disabled={!scriptExists || isSaving || isDeleting}
|
||||||
className="gap-1.5 text-destructive hover:text-destructive hover:bg-destructive/10"
|
className="gap-1.5 text-destructive hover:text-destructive hover:bg-destructive/10"
|
||||||
>
|
>
|
||||||
{isDeleting ? (
|
{isDeleting ? <Spinner size="xs" /> : <Trash2 className="w-3.5 h-3.5" />}
|
||||||
<Loader2 className="w-3.5 h-3.5 animate-spin" />
|
|
||||||
) : (
|
|
||||||
<Trash2 className="w-3.5 h-3.5" />
|
|
||||||
)}
|
|
||||||
Delete
|
Delete
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
@@ -461,11 +457,7 @@ npm install
|
|||||||
disabled={!hasChanges || isSaving || isDeleting}
|
disabled={!hasChanges || isSaving || isDeleting}
|
||||||
className="gap-1.5"
|
className="gap-1.5"
|
||||||
>
|
>
|
||||||
{isSaving ? (
|
{isSaving ? <Spinner size="xs" /> : <Save className="w-3.5 h-3.5" />}
|
||||||
<Loader2 className="w-3.5 h-3.5 animate-spin" />
|
|
||||||
) : (
|
|
||||||
<Save className="w-3.5 h-3.5" />
|
|
||||||
)}
|
|
||||||
Save
|
Save
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useState, useEffect, useCallback } from 'react';
|
import { useState, useEffect, useCallback } from 'react';
|
||||||
import { createLogger } from '@automaker/utils/logger';
|
import { createLogger } from '@automaker/utils/logger';
|
||||||
import { Bot, Folder, Loader2, RefreshCw, Square, Activity, FileText } from 'lucide-react';
|
import { Bot, Folder, RefreshCw, Square, Activity, FileText } from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { getElectronAPI, RunningAgent } from '@/lib/electron';
|
import { getElectronAPI, RunningAgent } from '@/lib/electron';
|
||||||
import { useAppStore } from '@/store/app-store';
|
import { useAppStore } from '@/store/app-store';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
@@ -146,7 +147,7 @@ export function RunningAgentsView() {
|
|||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className="flex-1 flex items-center justify-center">
|
<div className="flex-1 flex items-center justify-center">
|
||||||
<Loader2 className="h-8 w-8 animate-spin text-muted-foreground" />
|
<Spinner size="xl" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -169,7 +170,11 @@ export function RunningAgentsView() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Button variant="outline" size="sm" onClick={handleRefresh} disabled={refreshing}>
|
<Button variant="outline" size="sm" onClick={handleRefresh} disabled={refreshing}>
|
||||||
<RefreshCw className={cn('h-4 w-4 mr-2', refreshing && 'animate-spin')} />
|
{refreshing ? (
|
||||||
|
<Spinner size="sm" className="mr-2" />
|
||||||
|
) : (
|
||||||
|
<RefreshCw className="h-4 w-4 mr-2" />
|
||||||
|
)}
|
||||||
Refresh
|
Refresh
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { LogOut, User, Code2, RefreshCw } from 'lucide-react';
|
import { LogOut, User, Code2, RefreshCw } from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { logout } from '@/lib/http-api-client';
|
import { logout } from '@/lib/http-api-client';
|
||||||
import { useAuthStore } from '@/store/auth-store';
|
import { useAuthStore } from '@/store/auth-store';
|
||||||
@@ -143,7 +144,7 @@ export function AccountSection() {
|
|||||||
disabled={isRefreshing || isLoadingEditors}
|
disabled={isRefreshing || isLoadingEditors}
|
||||||
className="shrink-0 h-9 w-9"
|
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>
|
</Button>
|
||||||
</TooltipTrigger>
|
</TooltipTrigger>
|
||||||
<TooltipContent>
|
<TooltipContent>
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import { Label } from '@/components/ui/label';
|
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';
|
import type { ProviderConfig } from '@/config/api-providers';
|
||||||
|
|
||||||
interface ApiKeyFieldProps {
|
interface ApiKeyFieldProps {
|
||||||
@@ -70,7 +71,7 @@ export function ApiKeyField({ config }: ApiKeyFieldProps) {
|
|||||||
>
|
>
|
||||||
{testButton.loading ? (
|
{testButton.loading ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
<Spinner size="sm" className="mr-2" />
|
||||||
Testing...
|
Testing...
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { useAppStore } from '@/store/app-store';
|
import { useAppStore } from '@/store/app-store';
|
||||||
import { useSetupStore } from '@/store/setup-store';
|
import { useSetupStore } from '@/store/setup-store';
|
||||||
import { Button } from '@/components/ui/button';
|
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 { ApiKeyField } from './api-key-field';
|
||||||
import { buildProviderConfigs } from '@/config/api-providers';
|
import { buildProviderConfigs } from '@/config/api-providers';
|
||||||
import { SecurityNotice } from './security-notice';
|
import { SecurityNotice } from './security-notice';
|
||||||
@@ -142,7 +143,7 @@ export function ApiKeysSection() {
|
|||||||
data-testid="delete-anthropic-key"
|
data-testid="delete-anthropic-key"
|
||||||
>
|
>
|
||||||
{isDeletingAnthropicKey ? (
|
{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" />
|
<Trash2 className="w-4 h-4 mr-2" />
|
||||||
)}
|
)}
|
||||||
@@ -159,7 +160,7 @@ export function ApiKeysSection() {
|
|||||||
data-testid="delete-openai-key"
|
data-testid="delete-openai-key"
|
||||||
>
|
>
|
||||||
{isDeletingOpenaiKey ? (
|
{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" />
|
<Trash2 className="w-4 h-4 mr-2" />
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { getElectronAPI } from '@/lib/electron';
|
|||||||
import { useSetupStore } from '@/store/setup-store';
|
import { useSetupStore } from '@/store/setup-store';
|
||||||
import { useAppStore } from '@/store/app-store';
|
import { useAppStore } from '@/store/app-store';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { RefreshCw, AlertCircle } from 'lucide-react';
|
import { RefreshCw, AlertCircle } from 'lucide-react';
|
||||||
|
|
||||||
const ERROR_NO_API = 'Claude usage API not available';
|
const ERROR_NO_API = 'Claude usage API not available';
|
||||||
@@ -178,7 +179,7 @@ export function ClaudeUsageSection() {
|
|||||||
data-testid="refresh-claude-usage"
|
data-testid="refresh-claude-usage"
|
||||||
title={CLAUDE_REFRESH_LABEL}
|
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>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm text-muted-foreground/80 ml-12">{CLAUDE_USAGE_SUBTITLE}</p>
|
<p className="text-sm text-muted-foreground/80 ml-12">{CLAUDE_USAGE_SUBTITLE}</p>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useState, useCallback } from 'react';
|
import { useState, useCallback } from 'react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { CheckCircle2, AlertCircle, RefreshCw, XCircle } from 'lucide-react';
|
import { CheckCircle2, AlertCircle, RefreshCw, XCircle } from 'lucide-react';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import type { CliStatus } from '../shared/types';
|
import type { CliStatus } from '../shared/types';
|
||||||
@@ -172,7 +173,7 @@ export function ClaudeCliStatus({ status, authStatus, isChecking, onRefresh }: C
|
|||||||
'transition-all duration-200'
|
'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>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm text-muted-foreground/80 ml-12">
|
<p className="text-sm text-muted-foreground/80 ml-12">
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { CheckCircle2, AlertCircle, RefreshCw } from 'lucide-react';
|
import { CheckCircle2, AlertCircle, RefreshCw } from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import type { CliStatus } from '../shared/types';
|
import type { CliStatus } from '../shared/types';
|
||||||
|
|
||||||
@@ -56,7 +57,7 @@ export function CliStatusCard({
|
|||||||
'transition-all duration-200'
|
'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>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm text-muted-foreground/80 ml-12">{description}</p>
|
<p className="text-sm text-muted-foreground/80 ml-12">{description}</p>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useState, useCallback } from 'react';
|
import { useState, useCallback } from 'react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { CheckCircle2, AlertCircle, RefreshCw, XCircle } from 'lucide-react';
|
import { CheckCircle2, AlertCircle, RefreshCw, XCircle } from 'lucide-react';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import type { CliStatus } from '../shared/types';
|
import type { CliStatus } from '../shared/types';
|
||||||
@@ -165,7 +166,7 @@ export function CodexCliStatus({ status, authStatus, isChecking, onRefresh }: Cl
|
|||||||
'transition-all duration-200'
|
'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>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm text-muted-foreground/80 ml-12">
|
<p className="text-sm text-muted-foreground/80 ml-12">
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useState, useCallback } from 'react';
|
import { useState, useCallback } from 'react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { CheckCircle2, AlertCircle, RefreshCw, XCircle } from 'lucide-react';
|
import { CheckCircle2, AlertCircle, RefreshCw, XCircle } from 'lucide-react';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { CursorIcon } from '@/components/ui/provider-icon';
|
import { CursorIcon } from '@/components/ui/provider-icon';
|
||||||
@@ -290,7 +291,7 @@ export function CursorCliStatus({ status, isChecking, onRefresh }: CursorCliStat
|
|||||||
'transition-all duration-200'
|
'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>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm text-muted-foreground/80 ml-12">
|
<p className="text-sm text-muted-foreground/80 ml-12">
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Badge } from '@/components/ui/badge';
|
import { Badge } from '@/components/ui/badge';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { CheckCircle2, AlertCircle, RefreshCw, Bot, Cloud } from 'lucide-react';
|
import { CheckCircle2, AlertCircle, RefreshCw, Bot, Cloud } from 'lucide-react';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import type { CliStatus } from '../shared/types';
|
import type { CliStatus } from '../shared/types';
|
||||||
@@ -221,7 +222,7 @@ export function OpencodeCliStatus({
|
|||||||
'transition-all duration-200'
|
'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>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm text-muted-foreground/80 ml-12">
|
<p className="text-sm text-muted-foreground/80 ml-12">
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { RefreshCw, AlertCircle } from 'lucide-react';
|
import { RefreshCw, AlertCircle } from 'lucide-react';
|
||||||
import { OpenAIIcon } from '@/components/ui/provider-icon';
|
import { OpenAIIcon } from '@/components/ui/provider-icon';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
@@ -168,7 +169,7 @@ export function CodexUsageSection() {
|
|||||||
data-testid="refresh-codex-usage"
|
data-testid="refresh-codex-usage"
|
||||||
title={CODEX_REFRESH_LABEL}
|
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>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm text-muted-foreground/80 ml-12">{CODEX_USAGE_SUBTITLE}</p>
|
<p className="text-sm text-muted-foreground/80 ml-12">{CODEX_USAGE_SUBTITLE}</p>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useState, useEffect, useCallback } from 'react';
|
import { useState, useEffect, useCallback } from 'react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import {
|
import {
|
||||||
History,
|
History,
|
||||||
@@ -184,7 +185,11 @@ export function EventHistoryView() {
|
|||||||
</p>
|
</p>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Button variant="outline" size="sm" onClick={loadEvents} disabled={loading}>
|
<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
|
Refresh
|
||||||
</Button>
|
</Button>
|
||||||
{events.length > 0 && (
|
{events.length > 0 && (
|
||||||
|
|||||||
@@ -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 { Button } from '@/components/ui/button';
|
||||||
import { Switch } from '@/components/ui/switch';
|
import { Switch } from '@/components/ui/switch';
|
||||||
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible';
|
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible';
|
||||||
@@ -111,7 +112,7 @@ export function MCPServerCard({
|
|||||||
className="h-8 px-2"
|
className="h-8 px-2"
|
||||||
>
|
>
|
||||||
{testState?.status === 'testing' ? (
|
{testState?.status === 'testing' ? (
|
||||||
<Loader2 className="w-4 h-4 animate-spin" />
|
<Spinner size="sm" />
|
||||||
) : (
|
) : (
|
||||||
<PlayCircle className="w-4 h-4" />
|
<PlayCircle className="w-4 h-4" />
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Plug, RefreshCw, Download, Code, FileJson, Plus } from 'lucide-react';
|
import { Plug, RefreshCw, Download, Code, FileJson, Plus } from 'lucide-react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
interface MCPServerHeaderProps {
|
interface MCPServerHeaderProps {
|
||||||
@@ -43,7 +44,7 @@ export function MCPServerHeader({
|
|||||||
disabled={isRefreshing}
|
disabled={isRefreshing}
|
||||||
data-testid="refresh-mcp-servers-button"
|
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>
|
</Button>
|
||||||
{hasServers && (
|
{hasServers && (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -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 type { ServerType, ServerTestState } from './types';
|
||||||
import { SENSITIVE_PARAM_PATTERNS } from './constants';
|
import { SENSITIVE_PARAM_PATTERNS } from './constants';
|
||||||
|
|
||||||
@@ -40,7 +41,7 @@ export function getServerIcon(type: ServerType = 'stdio') {
|
|||||||
export function getTestStatusIcon(status: ServerTestState['status']) {
|
export function getTestStatusIcon(status: ServerTestState['status']) {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 'testing':
|
case 'testing':
|
||||||
return <Loader2 className="w-4 h-4 animate-spin text-brand-500" />;
|
return <Spinner size="sm" />;
|
||||||
case 'success':
|
case 'success':
|
||||||
return <CheckCircle2 className="w-4 h-4 text-green-500" />;
|
return <CheckCircle2 className="w-4 h-4 text-green-500" />;
|
||||||
case 'error':
|
case 'error':
|
||||||
|
|||||||
@@ -14,16 +14,8 @@ import { Label } from '@/components/ui/label';
|
|||||||
import { Switch } from '@/components/ui/switch';
|
import { Switch } from '@/components/ui/switch';
|
||||||
import { Checkbox } from '@/components/ui/checkbox';
|
import { Checkbox } from '@/components/ui/checkbox';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import {
|
import { Bot, RefreshCw, Users, ExternalLink, Globe, FolderOpen, Sparkles } from 'lucide-react';
|
||||||
Bot,
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
RefreshCw,
|
|
||||||
Loader2,
|
|
||||||
Users,
|
|
||||||
ExternalLink,
|
|
||||||
Globe,
|
|
||||||
FolderOpen,
|
|
||||||
Sparkles,
|
|
||||||
} from 'lucide-react';
|
|
||||||
import { useSubagents } from './hooks/use-subagents';
|
import { useSubagents } from './hooks/use-subagents';
|
||||||
import { useSubagentsSettings } from './hooks/use-subagents-settings';
|
import { useSubagentsSettings } from './hooks/use-subagents-settings';
|
||||||
import { SubagentCard } from './subagent-card';
|
import { SubagentCard } from './subagent-card';
|
||||||
@@ -178,11 +170,7 @@ export function SubagentsSection() {
|
|||||||
title="Refresh agents from disk"
|
title="Refresh agents from disk"
|
||||||
className="gap-1.5 h-7 px-2 text-xs"
|
className="gap-1.5 h-7 px-2 text-xs"
|
||||||
>
|
>
|
||||||
{isLoadingAgents ? (
|
{isLoadingAgents ? <Spinner size="xs" /> : <RefreshCw className="h-3.5 w-3.5" />}
|
||||||
<Loader2 className="h-3.5 w-3.5 animate-spin" />
|
|
||||||
) : (
|
|
||||||
<RefreshCw className="h-3.5 w-3.5" />
|
|
||||||
)}
|
|
||||||
Refresh
|
Refresh
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { Badge } from '@/components/ui/badge';
|
|||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible';
|
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible';
|
||||||
import { Shield, ShieldCheck, ShieldAlert, ChevronDown, Copy, Check } from 'lucide-react';
|
import { Shield, ShieldCheck, ShieldAlert, ChevronDown, Copy, Check } from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import type { CursorStatus } from '../hooks/use-cursor-status';
|
import type { CursorStatus } from '../hooks/use-cursor-status';
|
||||||
import type { PermissionsData } from '../hooks/use-cursor-permissions';
|
import type { PermissionsData } from '../hooks/use-cursor-permissions';
|
||||||
@@ -118,7 +119,7 @@ export function CursorPermissionsSection({
|
|||||||
|
|
||||||
{isLoadingPermissions ? (
|
{isLoadingPermissions ? (
|
||||||
<div className="flex items-center justify-center py-8">
|
<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>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ import {
|
|||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from '@/components/ui/select';
|
} 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 { cn } from '@/lib/utils';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import type {
|
import type {
|
||||||
@@ -500,7 +501,7 @@ export function OpencodeModelConfiguration({
|
|||||||
</p>
|
</p>
|
||||||
{isLoadingDynamicModels && (
|
{isLoadingDynamicModels && (
|
||||||
<div className="flex items-center gap-1.5 text-xs text-muted-foreground">
|
<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>
|
<span>Discovering...</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
import { Button } from '@/components/ui/button';
|
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 { CopyableCommandField } from './copyable-command-field';
|
||||||
import { TerminalOutput } from './terminal-output';
|
import { TerminalOutput } from './terminal-output';
|
||||||
|
|
||||||
@@ -59,7 +60,7 @@ export function CliInstallationCard({
|
|||||||
>
|
>
|
||||||
{isInstalling ? (
|
{isInstalling ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
<Spinner size="sm" className="mr-2" />
|
||||||
Installing...
|
Installing...
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -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 {
|
interface StatusBadgeProps {
|
||||||
status:
|
status:
|
||||||
@@ -34,7 +35,7 @@ export function StatusBadge({ status, label }: StatusBadgeProps) {
|
|||||||
};
|
};
|
||||||
case 'checking':
|
case 'checking':
|
||||||
return {
|
return {
|
||||||
icon: <Loader2 className="w-4 h-4 animate-spin" />,
|
icon: <Spinner size="sm" />,
|
||||||
className: 'bg-yellow-500/10 text-yellow-500 border-yellow-500/20',
|
className: 'bg-yellow-500/10 text-yellow-500 border-yellow-500/20',
|
||||||
};
|
};
|
||||||
case 'unverified':
|
case 'unverified':
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import { useAppStore } from '@/store/app-store';
|
|||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
import {
|
import {
|
||||||
CheckCircle2,
|
CheckCircle2,
|
||||||
Loader2,
|
|
||||||
Key,
|
Key,
|
||||||
ArrowRight,
|
ArrowRight,
|
||||||
ArrowLeft,
|
ArrowLeft,
|
||||||
@@ -27,6 +26,7 @@ import {
|
|||||||
XCircle,
|
XCircle,
|
||||||
Trash2,
|
Trash2,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { StatusBadge, TerminalOutput } from '../components';
|
import { StatusBadge, TerminalOutput } from '../components';
|
||||||
import { useCliStatus, useCliInstallation, useTokenSave } from '../hooks';
|
import { useCliStatus, useCliInstallation, useTokenSave } from '../hooks';
|
||||||
@@ -330,7 +330,7 @@ export function ClaudeSetupStep({ onNext, onBack, onSkip }: ClaudeSetupStepProps
|
|||||||
Authentication Methods
|
Authentication Methods
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<Button variant="ghost" size="sm" onClick={checkStatus} disabled={isChecking}>
|
<Button variant="ghost" size="sm" onClick={checkStatus} disabled={isChecking}>
|
||||||
<RefreshCw className={`w-4 h-4 ${isChecking ? 'animate-spin' : ''}`} />
|
{isChecking ? <Spinner size="sm" /> : <RefreshCw className="w-4 h-4" />}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<CardDescription>
|
<CardDescription>
|
||||||
@@ -412,7 +412,7 @@ export function ClaudeSetupStep({ onNext, onBack, onSkip }: ClaudeSetupStepProps
|
|||||||
>
|
>
|
||||||
{isInstalling ? (
|
{isInstalling ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
<Spinner size="sm" className="mr-2" />
|
||||||
Installing...
|
Installing...
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
@@ -435,7 +435,7 @@ export function ClaudeSetupStep({ onNext, onBack, onSkip }: ClaudeSetupStepProps
|
|||||||
{/* CLI Verification Status */}
|
{/* CLI Verification Status */}
|
||||||
{cliVerificationStatus === 'verifying' && (
|
{cliVerificationStatus === 'verifying' && (
|
||||||
<div className="flex items-center gap-3 p-4 rounded-lg bg-blue-500/10 border border-blue-500/20">
|
<div className="flex items-center gap-3 p-4 rounded-lg bg-blue-500/10 border border-blue-500/20">
|
||||||
<Loader2 className="w-5 h-5 text-blue-500 animate-spin" />
|
<Spinner size="md" />
|
||||||
<div>
|
<div>
|
||||||
<p className="font-medium text-foreground">Verifying CLI authentication...</p>
|
<p className="font-medium text-foreground">Verifying CLI authentication...</p>
|
||||||
<p className="text-sm text-muted-foreground">Running a test query</p>
|
<p className="text-sm text-muted-foreground">Running a test query</p>
|
||||||
@@ -494,7 +494,7 @@ export function ClaudeSetupStep({ onNext, onBack, onSkip }: ClaudeSetupStepProps
|
|||||||
>
|
>
|
||||||
{cliVerificationStatus === 'verifying' ? (
|
{cliVerificationStatus === 'verifying' ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
<Spinner size="sm" className="mr-2" />
|
||||||
Verifying...
|
Verifying...
|
||||||
</>
|
</>
|
||||||
) : cliVerificationStatus === 'error' ? (
|
) : cliVerificationStatus === 'error' ? (
|
||||||
@@ -574,7 +574,7 @@ export function ClaudeSetupStep({ onNext, onBack, onSkip }: ClaudeSetupStepProps
|
|||||||
>
|
>
|
||||||
{isSavingApiKey ? (
|
{isSavingApiKey ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
<Spinner size="sm" className="mr-2" />
|
||||||
Saving...
|
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"
|
className="border-red-500/50 text-red-500 hover:bg-red-500/10 hover:text-red-400"
|
||||||
data-testid="delete-anthropic-key-button"
|
data-testid="delete-anthropic-key-button"
|
||||||
>
|
>
|
||||||
{isDeletingApiKey ? (
|
{isDeletingApiKey ? <Spinner size="sm" /> : <Trash2 className="w-4 h-4" />}
|
||||||
<Loader2 className="w-4 h-4 animate-spin" />
|
|
||||||
) : (
|
|
||||||
<Trash2 className="w-4 h-4" />
|
|
||||||
)}
|
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -602,7 +598,7 @@ export function ClaudeSetupStep({ onNext, onBack, onSkip }: ClaudeSetupStepProps
|
|||||||
{/* API Key Verification Status */}
|
{/* API Key Verification Status */}
|
||||||
{apiKeyVerificationStatus === 'verifying' && (
|
{apiKeyVerificationStatus === 'verifying' && (
|
||||||
<div className="flex items-center gap-3 p-4 rounded-lg bg-blue-500/10 border border-blue-500/20">
|
<div className="flex items-center gap-3 p-4 rounded-lg bg-blue-500/10 border border-blue-500/20">
|
||||||
<Loader2 className="w-5 h-5 text-blue-500 animate-spin" />
|
<Spinner size="md" />
|
||||||
<div>
|
<div>
|
||||||
<p className="font-medium text-foreground">Verifying API key...</p>
|
<p className="font-medium text-foreground">Verifying API key...</p>
|
||||||
<p className="text-sm text-muted-foreground">Running a test query</p>
|
<p className="text-sm text-muted-foreground">Running a test query</p>
|
||||||
@@ -642,7 +638,7 @@ export function ClaudeSetupStep({ onNext, onBack, onSkip }: ClaudeSetupStepProps
|
|||||||
>
|
>
|
||||||
{apiKeyVerificationStatus === 'verifying' ? (
|
{apiKeyVerificationStatus === 'verifying' ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
<Spinner size="sm" className="mr-2" />
|
||||||
Verifying...
|
Verifying...
|
||||||
</>
|
</>
|
||||||
) : apiKeyVerificationStatus === 'error' ? (
|
) : apiKeyVerificationStatus === 'error' ? (
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import { useAppStore } from '@/store/app-store';
|
|||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
import {
|
import {
|
||||||
CheckCircle2,
|
CheckCircle2,
|
||||||
Loader2,
|
|
||||||
Key,
|
Key,
|
||||||
ArrowRight,
|
ArrowRight,
|
||||||
ArrowLeft,
|
ArrowLeft,
|
||||||
@@ -27,6 +26,7 @@ import {
|
|||||||
XCircle,
|
XCircle,
|
||||||
Trash2,
|
Trash2,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { StatusBadge, TerminalOutput } from '../components';
|
import { StatusBadge, TerminalOutput } from '../components';
|
||||||
import { useCliStatus, useCliInstallation, useTokenSave } from '../hooks';
|
import { useCliStatus, useCliInstallation, useTokenSave } from '../hooks';
|
||||||
@@ -332,7 +332,7 @@ export function CliSetupStep({ config, state, onNext, onBack, onSkip }: CliSetup
|
|||||||
Authentication Methods
|
Authentication Methods
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<Button variant="ghost" size="sm" onClick={checkStatus} disabled={isChecking}>
|
<Button variant="ghost" size="sm" onClick={checkStatus} disabled={isChecking}>
|
||||||
<RefreshCw className={`w-4 h-4 ${isChecking ? 'animate-spin' : ''}`} />
|
{isChecking ? <Spinner size="sm" /> : <RefreshCw className="w-4 h-4" />}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<CardDescription>Choose one of the following methods to authenticate:</CardDescription>
|
<CardDescription>Choose one of the following methods to authenticate:</CardDescription>
|
||||||
@@ -408,7 +408,7 @@ export function CliSetupStep({ config, state, onNext, onBack, onSkip }: CliSetup
|
|||||||
>
|
>
|
||||||
{isInstalling ? (
|
{isInstalling ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
<Spinner size="sm" className="mr-2" />
|
||||||
Installing...
|
Installing...
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
@@ -427,7 +427,7 @@ export function CliSetupStep({ config, state, onNext, onBack, onSkip }: CliSetup
|
|||||||
|
|
||||||
{cliVerificationStatus === 'verifying' && (
|
{cliVerificationStatus === 'verifying' && (
|
||||||
<div className="flex items-center gap-3 p-4 rounded-lg bg-blue-500/10 border border-blue-500/20">
|
<div className="flex items-center gap-3 p-4 rounded-lg bg-blue-500/10 border border-blue-500/20">
|
||||||
<Loader2 className="w-5 h-5 text-blue-500 animate-spin" />
|
<Spinner size="md" />
|
||||||
<div>
|
<div>
|
||||||
<p className="font-medium text-foreground">Verifying CLI authentication...</p>
|
<p className="font-medium text-foreground">Verifying CLI authentication...</p>
|
||||||
<p className="text-sm text-muted-foreground">Running a test query</p>
|
<p className="text-sm text-muted-foreground">Running a test query</p>
|
||||||
@@ -605,7 +605,7 @@ export function CliSetupStep({ config, state, onNext, onBack, onSkip }: CliSetup
|
|||||||
>
|
>
|
||||||
{cliVerificationStatus === 'verifying' ? (
|
{cliVerificationStatus === 'verifying' ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
<Spinner size="sm" className="mr-2" />
|
||||||
Verifying...
|
Verifying...
|
||||||
</>
|
</>
|
||||||
) : cliVerificationStatus === 'error' ? (
|
) : cliVerificationStatus === 'error' ? (
|
||||||
@@ -681,7 +681,7 @@ export function CliSetupStep({ config, state, onNext, onBack, onSkip }: CliSetup
|
|||||||
>
|
>
|
||||||
{isSavingApiKey ? (
|
{isSavingApiKey ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
<Spinner size="sm" className="mr-2" />
|
||||||
Saving...
|
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"
|
className="border-red-500/50 text-red-500 hover:bg-red-500/10 hover:text-red-400"
|
||||||
data-testid={config.testIds.deleteApiKeyButton}
|
data-testid={config.testIds.deleteApiKeyButton}
|
||||||
>
|
>
|
||||||
{isDeletingApiKey ? (
|
{isDeletingApiKey ? <Spinner size="sm" /> : <Trash2 className="w-4 h-4" />}
|
||||||
<Loader2 className="w-4 h-4 animate-spin" />
|
|
||||||
) : (
|
|
||||||
<Trash2 className="w-4 h-4" />
|
|
||||||
)}
|
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -708,7 +704,7 @@ export function CliSetupStep({ config, state, onNext, onBack, onSkip }: CliSetup
|
|||||||
|
|
||||||
{apiKeyVerificationStatus === 'verifying' && (
|
{apiKeyVerificationStatus === 'verifying' && (
|
||||||
<div className="flex items-center gap-3 p-4 rounded-lg bg-blue-500/10 border border-blue-500/20">
|
<div className="flex items-center gap-3 p-4 rounded-lg bg-blue-500/10 border border-blue-500/20">
|
||||||
<Loader2 className="w-5 h-5 text-blue-500 animate-spin" />
|
<Spinner size="md" />
|
||||||
<div>
|
<div>
|
||||||
<p className="font-medium text-foreground">Verifying API key...</p>
|
<p className="font-medium text-foreground">Verifying API key...</p>
|
||||||
<p className="text-sm text-muted-foreground">Running a test query</p>
|
<p className="text-sm text-muted-foreground">Running a test query</p>
|
||||||
@@ -767,7 +763,7 @@ export function CliSetupStep({ config, state, onNext, onBack, onSkip }: CliSetup
|
|||||||
>
|
>
|
||||||
{apiKeyVerificationStatus === 'verifying' ? (
|
{apiKeyVerificationStatus === 'verifying' ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
<Spinner size="sm" className="mr-2" />
|
||||||
Verifying...
|
Verifying...
|
||||||
</>
|
</>
|
||||||
) : apiKeyVerificationStatus === 'error' ? (
|
) : apiKeyVerificationStatus === 'error' ? (
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import { useSetupStore } from '@/store/setup-store';
|
|||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
import {
|
import {
|
||||||
CheckCircle2,
|
CheckCircle2,
|
||||||
Loader2,
|
|
||||||
ArrowRight,
|
ArrowRight,
|
||||||
ArrowLeft,
|
ArrowLeft,
|
||||||
ExternalLink,
|
ExternalLink,
|
||||||
@@ -16,6 +15,7 @@ import {
|
|||||||
AlertTriangle,
|
AlertTriangle,
|
||||||
XCircle,
|
XCircle,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { StatusBadge } from '../components';
|
import { StatusBadge } from '../components';
|
||||||
import { CursorIcon } from '@/components/ui/provider-icon';
|
import { CursorIcon } from '@/components/ui/provider-icon';
|
||||||
@@ -204,7 +204,7 @@ export function CursorSetupStep({ onNext, onBack, onSkip }: CursorSetupStepProps
|
|||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{getStatusBadge()}
|
{getStatusBadge()}
|
||||||
<Button variant="ghost" size="sm" onClick={checkStatus} disabled={isChecking}>
|
<Button variant="ghost" size="sm" onClick={checkStatus} disabled={isChecking}>
|
||||||
<RefreshCw className={`w-4 h-4 ${isChecking ? 'animate-spin' : ''}`} />
|
{isChecking ? <Spinner size="sm" /> : <RefreshCw className="w-4 h-4" />}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -318,7 +318,7 @@ export function CursorSetupStep({ onNext, onBack, onSkip }: CursorSetupStepProps
|
|||||||
>
|
>
|
||||||
{isLoggingIn ? (
|
{isLoggingIn ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
<Spinner size="sm" className="mr-2" />
|
||||||
Waiting for login...
|
Waiting for login...
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
@@ -332,7 +332,7 @@ export function CursorSetupStep({ onNext, onBack, onSkip }: CursorSetupStepProps
|
|||||||
{/* Loading State */}
|
{/* Loading State */}
|
||||||
{isChecking && (
|
{isChecking && (
|
||||||
<div className="flex items-center gap-3 p-4 rounded-lg bg-blue-500/10 border border-blue-500/20">
|
<div className="flex items-center gap-3 p-4 rounded-lg bg-blue-500/10 border border-blue-500/20">
|
||||||
<Loader2 className="w-5 h-5 text-blue-500 animate-spin" />
|
<Spinner size="md" />
|
||||||
<div>
|
<div>
|
||||||
<p className="font-medium text-foreground">Checking Cursor CLI status...</p>
|
<p className="font-medium text-foreground">Checking Cursor CLI status...</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import { useSetupStore } from '@/store/setup-store';
|
|||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
import {
|
import {
|
||||||
CheckCircle2,
|
CheckCircle2,
|
||||||
Loader2,
|
|
||||||
ArrowRight,
|
ArrowRight,
|
||||||
ArrowLeft,
|
ArrowLeft,
|
||||||
ExternalLink,
|
ExternalLink,
|
||||||
@@ -16,6 +15,7 @@ import {
|
|||||||
Github,
|
Github,
|
||||||
XCircle,
|
XCircle,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { StatusBadge } from '../components';
|
import { StatusBadge } from '../components';
|
||||||
|
|
||||||
@@ -116,7 +116,7 @@ export function GitHubSetupStep({ onNext, onBack, onSkip }: GitHubSetupStepProps
|
|||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{getStatusBadge()}
|
{getStatusBadge()}
|
||||||
<Button variant="ghost" size="sm" onClick={checkStatus} disabled={isChecking}>
|
<Button variant="ghost" size="sm" onClick={checkStatus} disabled={isChecking}>
|
||||||
<RefreshCw className={`w-4 h-4 ${isChecking ? 'animate-spin' : ''}`} />
|
{isChecking ? <Spinner size="sm" /> : <RefreshCw className="w-4 h-4" />}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -252,7 +252,7 @@ export function GitHubSetupStep({ onNext, onBack, onSkip }: GitHubSetupStepProps
|
|||||||
{/* Loading State */}
|
{/* Loading State */}
|
||||||
{isChecking && (
|
{isChecking && (
|
||||||
<div className="flex items-center gap-3 p-4 rounded-lg bg-blue-500/10 border border-blue-500/20">
|
<div className="flex items-center gap-3 p-4 rounded-lg bg-blue-500/10 border border-blue-500/20">
|
||||||
<Loader2 className="w-5 h-5 text-blue-500 animate-spin" />
|
<Spinner size="md" />
|
||||||
<div>
|
<div>
|
||||||
<p className="font-medium text-foreground">Checking GitHub CLI status...</p>
|
<p className="font-medium text-foreground">Checking GitHub CLI status...</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import { useSetupStore } from '@/store/setup-store';
|
|||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
import {
|
import {
|
||||||
CheckCircle2,
|
CheckCircle2,
|
||||||
Loader2,
|
|
||||||
ArrowRight,
|
ArrowRight,
|
||||||
ArrowLeft,
|
ArrowLeft,
|
||||||
ExternalLink,
|
ExternalLink,
|
||||||
@@ -17,6 +16,7 @@ import {
|
|||||||
XCircle,
|
XCircle,
|
||||||
Terminal,
|
Terminal,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { StatusBadge } from '../components';
|
import { StatusBadge } from '../components';
|
||||||
|
|
||||||
@@ -204,7 +204,7 @@ export function OpencodeSetupStep({ onNext, onBack, onSkip }: OpencodeSetupStepP
|
|||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{getStatusBadge()}
|
{getStatusBadge()}
|
||||||
<Button variant="ghost" size="sm" onClick={checkStatus} disabled={isChecking}>
|
<Button variant="ghost" size="sm" onClick={checkStatus} disabled={isChecking}>
|
||||||
<RefreshCw className={`w-4 h-4 ${isChecking ? 'animate-spin' : ''}`} />
|
{isChecking ? <Spinner size="sm" /> : <RefreshCw className="w-4 h-4" />}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -316,7 +316,7 @@ export function OpencodeSetupStep({ onNext, onBack, onSkip }: OpencodeSetupStepP
|
|||||||
>
|
>
|
||||||
{isLoggingIn ? (
|
{isLoggingIn ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
<Spinner size="sm" className="mr-2" />
|
||||||
Waiting for login...
|
Waiting for login...
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
@@ -330,7 +330,7 @@ export function OpencodeSetupStep({ onNext, onBack, onSkip }: OpencodeSetupStepP
|
|||||||
{/* Loading State */}
|
{/* Loading State */}
|
||||||
{isChecking && (
|
{isChecking && (
|
||||||
<div className="flex items-center gap-3 p-4 rounded-lg bg-blue-500/10 border border-blue-500/20">
|
<div className="flex items-center gap-3 p-4 rounded-lg bg-blue-500/10 border border-blue-500/20">
|
||||||
<Loader2 className="w-5 h-5 text-blue-500 animate-spin" />
|
<Spinner size="md" />
|
||||||
<div>
|
<div>
|
||||||
<p className="font-medium text-foreground">Checking OpenCode CLI status...</p>
|
<p className="font-medium text-foreground">Checking OpenCode CLI status...</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import {
|
|||||||
ArrowRight,
|
ArrowRight,
|
||||||
ArrowLeft,
|
ArrowLeft,
|
||||||
CheckCircle2,
|
CheckCircle2,
|
||||||
Loader2,
|
|
||||||
Key,
|
Key,
|
||||||
ExternalLink,
|
ExternalLink,
|
||||||
Copy,
|
Copy,
|
||||||
@@ -29,6 +28,7 @@ import {
|
|||||||
Terminal,
|
Terminal,
|
||||||
AlertCircle,
|
AlertCircle,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { AnthropicIcon, CursorIcon, OpenAIIcon, OpenCodeIcon } from '@/components/ui/provider-icon';
|
import { AnthropicIcon, CursorIcon, OpenAIIcon, OpenCodeIcon } from '@/components/ui/provider-icon';
|
||||||
@@ -240,7 +240,7 @@ function ClaudeContent() {
|
|||||||
onClick={checkStatus}
|
onClick={checkStatus}
|
||||||
disabled={isChecking || isVerifying}
|
disabled={isChecking || isVerifying}
|
||||||
>
|
>
|
||||||
<RefreshCw className={`w-4 h-4 ${isChecking || isVerifying ? 'animate-spin' : ''}`} />
|
{isChecking || isVerifying ? <Spinner size="sm" /> : <RefreshCw className="w-4 h-4" />}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<CardDescription>
|
<CardDescription>
|
||||||
@@ -278,7 +278,7 @@ function ClaudeContent() {
|
|||||||
{/* Checking/Verifying State */}
|
{/* Checking/Verifying State */}
|
||||||
{(isChecking || isVerifying) && (
|
{(isChecking || isVerifying) && (
|
||||||
<div className="flex items-center gap-3 p-4 rounded-lg bg-blue-500/10 border border-blue-500/20">
|
<div className="flex items-center gap-3 p-4 rounded-lg bg-blue-500/10 border border-blue-500/20">
|
||||||
<Loader2 className="w-5 h-5 text-blue-500 animate-spin" />
|
<Spinner size="md" />
|
||||||
<p className="font-medium text-foreground">
|
<p className="font-medium text-foreground">
|
||||||
{isChecking ? 'Checking Claude CLI status...' : 'Verifying authentication...'}
|
{isChecking ? 'Checking Claude CLI status...' : 'Verifying authentication...'}
|
||||||
</p>
|
</p>
|
||||||
@@ -322,7 +322,7 @@ function ClaudeContent() {
|
|||||||
>
|
>
|
||||||
{isInstalling ? (
|
{isInstalling ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
<Spinner size="sm" className="mr-2" />
|
||||||
Installing...
|
Installing...
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
@@ -417,11 +417,7 @@ function ClaudeContent() {
|
|||||||
disabled={isSavingApiKey || !apiKey.trim()}
|
disabled={isSavingApiKey || !apiKey.trim()}
|
||||||
className="flex-1 bg-brand-500 hover:bg-brand-600 text-white"
|
className="flex-1 bg-brand-500 hover:bg-brand-600 text-white"
|
||||||
>
|
>
|
||||||
{isSavingApiKey ? (
|
{isSavingApiKey ? <Spinner size="sm" /> : 'Save API Key'}
|
||||||
<Loader2 className="w-4 h-4 animate-spin" />
|
|
||||||
) : (
|
|
||||||
'Save API Key'
|
|
||||||
)}
|
|
||||||
</Button>
|
</Button>
|
||||||
{hasApiKey && (
|
{hasApiKey && (
|
||||||
<Button
|
<Button
|
||||||
@@ -431,7 +427,7 @@ function ClaudeContent() {
|
|||||||
className="border-red-500/50 text-red-500 hover:bg-red-500/10"
|
className="border-red-500/50 text-red-500 hover:bg-red-500/10"
|
||||||
>
|
>
|
||||||
{isDeletingApiKey ? (
|
{isDeletingApiKey ? (
|
||||||
<Loader2 className="w-4 h-4 animate-spin" />
|
<Spinner size="sm" />
|
||||||
) : (
|
) : (
|
||||||
<Trash2 className="w-4 h-4" />
|
<Trash2 className="w-4 h-4" />
|
||||||
)}
|
)}
|
||||||
@@ -553,7 +549,7 @@ function CursorContent() {
|
|||||||
Cursor CLI Status
|
Cursor CLI Status
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<Button variant="ghost" size="sm" onClick={checkStatus} disabled={isChecking}>
|
<Button variant="ghost" size="sm" onClick={checkStatus} disabled={isChecking}>
|
||||||
<RefreshCw className={`w-4 h-4 ${isChecking ? 'animate-spin' : ''}`} />
|
{isChecking ? <Spinner size="sm" /> : <RefreshCw className="w-4 h-4" />}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<CardDescription>
|
<CardDescription>
|
||||||
@@ -658,7 +654,7 @@ function CursorContent() {
|
|||||||
>
|
>
|
||||||
{isLoggingIn ? (
|
{isLoggingIn ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
<Spinner size="sm" className="mr-2" />
|
||||||
Waiting for login...
|
Waiting for login...
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
@@ -671,7 +667,7 @@ function CursorContent() {
|
|||||||
|
|
||||||
{isChecking && (
|
{isChecking && (
|
||||||
<div className="flex items-center gap-3 p-4 rounded-lg bg-blue-500/10 border border-blue-500/20">
|
<div className="flex items-center gap-3 p-4 rounded-lg bg-blue-500/10 border border-blue-500/20">
|
||||||
<Loader2 className="w-5 h-5 text-blue-500 animate-spin" />
|
<Spinner size="md" />
|
||||||
<p className="font-medium text-foreground">Checking Cursor CLI status...</p>
|
<p className="font-medium text-foreground">Checking Cursor CLI status...</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -807,7 +803,7 @@ function CodexContent() {
|
|||||||
Codex CLI Status
|
Codex CLI Status
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<Button variant="ghost" size="sm" onClick={checkStatus} disabled={isChecking}>
|
<Button variant="ghost" size="sm" onClick={checkStatus} disabled={isChecking}>
|
||||||
<RefreshCw className={`w-4 h-4 ${isChecking ? 'animate-spin' : ''}`} />
|
{isChecking ? <Spinner size="sm" /> : <RefreshCw className="w-4 h-4" />}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<CardDescription>
|
<CardDescription>
|
||||||
@@ -915,7 +911,7 @@ function CodexContent() {
|
|||||||
>
|
>
|
||||||
{isLoggingIn ? (
|
{isLoggingIn ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
<Spinner size="sm" className="mr-2" />
|
||||||
Waiting for login...
|
Waiting for login...
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
@@ -958,7 +954,7 @@ function CodexContent() {
|
|||||||
disabled={isSaving || !apiKey.trim()}
|
disabled={isSaving || !apiKey.trim()}
|
||||||
className="w-full bg-brand-500 hover:bg-brand-600 text-white"
|
className="w-full bg-brand-500 hover:bg-brand-600 text-white"
|
||||||
>
|
>
|
||||||
{isSaving ? <Loader2 className="w-4 h-4 animate-spin" /> : 'Save API Key'}
|
{isSaving ? <Spinner size="sm" /> : 'Save API Key'}
|
||||||
</Button>
|
</Button>
|
||||||
</AccordionContent>
|
</AccordionContent>
|
||||||
</AccordionItem>
|
</AccordionItem>
|
||||||
@@ -968,7 +964,7 @@ function CodexContent() {
|
|||||||
|
|
||||||
{isChecking && (
|
{isChecking && (
|
||||||
<div className="flex items-center gap-3 p-4 rounded-lg bg-blue-500/10 border border-blue-500/20">
|
<div className="flex items-center gap-3 p-4 rounded-lg bg-blue-500/10 border border-blue-500/20">
|
||||||
<Loader2 className="w-5 h-5 text-blue-500 animate-spin" />
|
<Spinner size="md" />
|
||||||
<p className="font-medium text-foreground">Checking Codex CLI status...</p>
|
<p className="font-medium text-foreground">Checking Codex CLI status...</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -1082,7 +1078,7 @@ function OpencodeContent() {
|
|||||||
OpenCode CLI Status
|
OpenCode CLI Status
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<Button variant="ghost" size="sm" onClick={checkStatus} disabled={isChecking}>
|
<Button variant="ghost" size="sm" onClick={checkStatus} disabled={isChecking}>
|
||||||
<RefreshCw className={`w-4 h-4 ${isChecking ? 'animate-spin' : ''}`} />
|
{isChecking ? <Spinner size="sm" /> : <RefreshCw className="w-4 h-4" />}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<CardDescription>
|
<CardDescription>
|
||||||
@@ -1191,7 +1187,7 @@ function OpencodeContent() {
|
|||||||
>
|
>
|
||||||
{isLoggingIn ? (
|
{isLoggingIn ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
<Spinner size="sm" className="mr-2" />
|
||||||
Waiting for login...
|
Waiting for login...
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
@@ -1204,7 +1200,7 @@ function OpencodeContent() {
|
|||||||
|
|
||||||
{isChecking && (
|
{isChecking && (
|
||||||
<div className="flex items-center gap-3 p-4 rounded-lg bg-blue-500/10 border border-blue-500/20">
|
<div className="flex items-center gap-3 p-4 rounded-lg bg-blue-500/10 border border-blue-500/20">
|
||||||
<Loader2 className="w-5 h-5 text-blue-500 animate-spin" />
|
<Spinner size="md" />
|
||||||
<p className="font-medium text-foreground">Checking OpenCode CLI status...</p>
|
<p className="font-medium text-foreground">Checking OpenCode CLI status...</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -1416,7 +1412,7 @@ export function ProvidersSetupStep({ onNext, onBack }: ProvidersSetupStepProps)
|
|||||||
);
|
);
|
||||||
case 'verifying':
|
case 'verifying':
|
||||||
return (
|
return (
|
||||||
<Loader2 className="w-3 h-3 text-blue-500 absolute -top-1 -right-1.5 bg-background rounded-full animate-spin" />
|
<Spinner size="xs" className="absolute -top-1 -right-1.5 bg-background rounded-full" />
|
||||||
);
|
);
|
||||||
case 'installed_not_auth':
|
case 'installed_not_auth':
|
||||||
return (
|
return (
|
||||||
@@ -1436,7 +1432,7 @@ export function ProvidersSetupStep({ onNext, onBack }: ProvidersSetupStepProps)
|
|||||||
|
|
||||||
{isInitialChecking && (
|
{isInitialChecking && (
|
||||||
<div className="flex items-center justify-center gap-2 p-4 rounded-lg bg-blue-500/10 border border-blue-500/20">
|
<div className="flex items-center justify-center gap-2 p-4 rounded-lg bg-blue-500/10 border border-blue-500/20">
|
||||||
<Loader2 className="w-5 h-5 text-blue-500 animate-spin" />
|
<Spinner size="md" />
|
||||||
<p className="font-medium text-foreground">Checking provider status...</p>
|
<p className="font-medium text-foreground">Checking provider status...</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { RefreshCw } from 'lucide-react';
|
|
||||||
import { useAppStore } from '@/store/app-store';
|
import { useAppStore } from '@/store/app-store';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
|
|
||||||
// Extracted hooks
|
// Extracted hooks
|
||||||
import { useSpecLoading, useSpecSave, useSpecGeneration } from './spec-view/hooks';
|
import { useSpecLoading, useSpecSave, useSpecGeneration } from './spec-view/hooks';
|
||||||
@@ -86,7 +86,7 @@ export function SpecView() {
|
|||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<div className="flex-1 flex items-center justify-center" data-testid="spec-view-loading">
|
<div className="flex-1 flex items-center justify-center" data-testid="spec-view-loading">
|
||||||
<RefreshCw className="w-6 h-6 animate-spin text-muted-foreground" />
|
<Spinner size="lg" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Button } from '@/components/ui/button';
|
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';
|
import { PHASE_LABELS } from '../constants';
|
||||||
|
|
||||||
interface SpecEmptyStateProps {
|
interface SpecEmptyStateProps {
|
||||||
@@ -36,7 +37,7 @@ export function SpecEmptyState({
|
|||||||
{isProcessing && (
|
{isProcessing && (
|
||||||
<div className="flex items-center gap-3 px-6 py-3.5 rounded-xl bg-linear-to-r from-primary/15 to-primary/5 border border-primary/30 shadow-lg backdrop-blur-md">
|
<div className="flex items-center gap-3 px-6 py-3.5 rounded-xl bg-linear-to-r from-primary/15 to-primary/5 border border-primary/30 shadow-lg backdrop-blur-md">
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Loader2 className="w-5 h-5 animate-spin text-primary shrink-0" />
|
<Spinner size="md" className="shrink-0" />
|
||||||
<div className="absolute inset-0 w-5 h-5 animate-ping text-primary/20" />
|
<div className="absolute inset-0 w-5 h-5 animate-ping text-primary/20" />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-1 min-w-0">
|
<div className="flex flex-col gap-1 min-w-0">
|
||||||
@@ -64,7 +65,7 @@ export function SpecEmptyState({
|
|||||||
<div className="mb-6 flex justify-center">
|
<div className="mb-6 flex justify-center">
|
||||||
<div className="p-4 rounded-full bg-primary/10">
|
<div className="p-4 rounded-full bg-primary/10">
|
||||||
{isCreating ? (
|
{isCreating ? (
|
||||||
<Loader2 className="w-12 h-12 text-primary animate-spin" />
|
<Spinner size="xl" className="w-12 h-12" />
|
||||||
) : (
|
) : (
|
||||||
<FilePlus2 className="w-12 h-12 text-primary" />
|
<FilePlus2 className="w-12 h-12 text-primary" />
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ import {
|
|||||||
HeaderActionsPanel,
|
HeaderActionsPanel,
|
||||||
HeaderActionsPanelTrigger,
|
HeaderActionsPanelTrigger,
|
||||||
} from '@/components/ui/header-actions-panel';
|
} 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';
|
import { PHASE_LABELS } from '../constants';
|
||||||
|
|
||||||
interface SpecHeaderProps {
|
interface SpecHeaderProps {
|
||||||
@@ -59,7 +60,7 @@ export function SpecHeader({
|
|||||||
{isProcessing && (
|
{isProcessing && (
|
||||||
<div className="hidden lg:flex items-center gap-3 px-6 py-3.5 rounded-xl bg-linear-to-r from-primary/15 to-primary/5 border border-primary/30 shadow-lg backdrop-blur-md">
|
<div className="hidden lg:flex items-center gap-3 px-6 py-3.5 rounded-xl bg-linear-to-r from-primary/15 to-primary/5 border border-primary/30 shadow-lg backdrop-blur-md">
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Loader2 className="w-5 h-5 animate-spin text-primary shrink-0" />
|
<Spinner size="md" className="shrink-0" />
|
||||||
<div className="absolute inset-0 w-5 h-5 animate-ping text-primary/20" />
|
<div className="absolute inset-0 w-5 h-5 animate-ping text-primary/20" />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-1 min-w-0">
|
<div className="flex flex-col gap-1 min-w-0">
|
||||||
@@ -83,7 +84,7 @@ export function SpecHeader({
|
|||||||
{/* Mobile processing indicator */}
|
{/* Mobile processing indicator */}
|
||||||
{isProcessing && (
|
{isProcessing && (
|
||||||
<div className="lg:hidden flex items-center gap-2 px-3 py-2 rounded-lg bg-primary/10 border border-primary/20">
|
<div className="lg:hidden flex items-center gap-2 px-3 py-2 rounded-lg bg-primary/10 border border-primary/20">
|
||||||
<Loader2 className="w-4 h-4 animate-spin text-primary" />
|
<Spinner size="sm" />
|
||||||
<span className="text-xs font-medium text-primary">Processing...</span>
|
<span className="text-xs font-medium text-primary">Processing...</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -157,7 +158,7 @@ export function SpecHeader({
|
|||||||
{/* Status messages in panel */}
|
{/* Status messages in panel */}
|
||||||
{isProcessing && (
|
{isProcessing && (
|
||||||
<div className="flex items-center gap-3 p-3 rounded-lg bg-primary/10 border border-primary/20">
|
<div className="flex items-center gap-3 p-3 rounded-lg bg-primary/10 border border-primary/20">
|
||||||
<Loader2 className="w-4 h-4 animate-spin text-primary shrink-0" />
|
<Spinner size="sm" className="shrink-0" />
|
||||||
<div className="flex flex-col gap-0.5 min-w-0">
|
<div className="flex flex-col gap-0.5 min-w-0">
|
||||||
<span className="text-sm font-medium text-primary">
|
<span className="text-sm font-medium text-primary">
|
||||||
{isSyncing
|
{isSyncing
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Sparkles, Clock, Loader2 } from 'lucide-react';
|
import { Sparkles, Clock } from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
@@ -163,7 +164,7 @@ export function CreateSpecDialog({
|
|||||||
>
|
>
|
||||||
{isCreatingSpec ? (
|
{isCreatingSpec ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
<Spinner size="sm" className="mr-2" />
|
||||||
Generating...
|
Generating...
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Sparkles, Clock, Loader2 } from 'lucide-react';
|
import { Sparkles, Clock } from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
@@ -158,7 +159,7 @@ export function RegenerateSpecDialog({
|
|||||||
>
|
>
|
||||||
{isRegenerating ? (
|
{isRegenerating ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
<Spinner size="sm" className="mr-2" />
|
||||||
Regenerating...
|
Regenerating...
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -7,13 +7,13 @@ import {
|
|||||||
Unlock,
|
Unlock,
|
||||||
SplitSquareHorizontal,
|
SplitSquareHorizontal,
|
||||||
SplitSquareVertical,
|
SplitSquareVertical,
|
||||||
Loader2,
|
|
||||||
AlertCircle,
|
AlertCircle,
|
||||||
RefreshCw,
|
RefreshCw,
|
||||||
X,
|
X,
|
||||||
SquarePlus,
|
SquarePlus,
|
||||||
Settings,
|
Settings,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { getServerUrlSync } from '@/lib/http-api-client';
|
import { getServerUrlSync } from '@/lib/http-api-client';
|
||||||
import {
|
import {
|
||||||
useAppStore,
|
useAppStore,
|
||||||
@@ -1279,7 +1279,7 @@ export function TerminalView() {
|
|||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className="flex-1 flex items-center justify-center">
|
<div className="flex-1 flex items-center justify-center">
|
||||||
<Loader2 className="h-8 w-8 animate-spin text-muted-foreground" />
|
<Spinner size="xl" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1342,7 +1342,7 @@ export function TerminalView() {
|
|||||||
{authError && <p className="text-sm text-destructive">{authError}</p>}
|
{authError && <p className="text-sm text-destructive">{authError}</p>}
|
||||||
<Button type="submit" className="w-full" disabled={authLoading || !password}>
|
<Button type="submit" className="w-full" disabled={authLoading || !password}>
|
||||||
{authLoading ? (
|
{authLoading ? (
|
||||||
<Loader2 className="h-4 w-4 mr-2 animate-spin" />
|
<Spinner size="sm" className="mr-2" />
|
||||||
) : (
|
) : (
|
||||||
<Unlock className="h-4 w-4 mr-2" />
|
<Unlock className="h-4 w-4 mr-2" />
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import {
|
|||||||
CheckSquare,
|
CheckSquare,
|
||||||
Trash2,
|
Trash2,
|
||||||
ImageIcon,
|
ImageIcon,
|
||||||
Loader2,
|
|
||||||
Settings,
|
Settings,
|
||||||
RotateCcw,
|
RotateCcw,
|
||||||
Search,
|
Search,
|
||||||
@@ -24,6 +23,7 @@ import {
|
|||||||
ArrowDown,
|
ArrowDown,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
|
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
|
||||||
import { Slider } from '@/components/ui/slider';
|
import { Slider } from '@/components/ui/slider';
|
||||||
@@ -1743,7 +1743,7 @@ export function TerminalPanel({
|
|||||||
<div className="flex flex-col items-center gap-2 px-4 py-3 bg-blue-500/90 rounded-md text-white">
|
<div className="flex flex-col items-center gap-2 px-4 py-3 bg-blue-500/90 rounded-md text-white">
|
||||||
{isProcessingImage ? (
|
{isProcessingImage ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 className="h-6 w-6 animate-spin" />
|
<Spinner size="lg" />
|
||||||
<span className="text-sm font-medium">Processing...</span>
|
<span className="text-sm font-medium">Processing...</span>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
@@ -1791,7 +1791,7 @@ export function TerminalPanel({
|
|||||||
)}
|
)}
|
||||||
{connectionStatus === 'reconnecting' && (
|
{connectionStatus === 'reconnecting' && (
|
||||||
<span className="text-[10px] px-1.5 py-0.5 rounded bg-yellow-500/20 text-yellow-500 flex items-center gap-1">
|
<span className="text-[10px] px-1.5 py-0.5 rounded bg-yellow-500/20 text-yellow-500 flex items-center gap-1">
|
||||||
<Loader2 className="h-2.5 w-2.5 animate-spin" />
|
<Spinner size="xs" />
|
||||||
Reconnecting...
|
Reconnecting...
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ import {
|
|||||||
Sparkles,
|
Sparkles,
|
||||||
MessageSquare,
|
MessageSquare,
|
||||||
ChevronDown,
|
ChevronDown,
|
||||||
Loader2,
|
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
import { Spinner } from '@/components/ui/spinner';
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
@@ -758,7 +758,7 @@ export function WelcomeView() {
|
|||||||
<div className="mt-5 p-4 rounded-xl bg-muted/50 border border-border">
|
<div className="mt-5 p-4 rounded-xl bg-muted/50 border border-border">
|
||||||
{isAnalyzing ? (
|
{isAnalyzing ? (
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<Loader2 className="w-4 h-4 text-brand-500 animate-spin" />
|
<Spinner size="sm" />
|
||||||
<p className="text-sm text-brand-500">
|
<p className="text-sm text-brand-500">
|
||||||
AI agent is analyzing your project structure...
|
AI agent is analyzing your project structure...
|
||||||
</p>
|
</p>
|
||||||
@@ -802,7 +802,7 @@ export function WelcomeView() {
|
|||||||
data-testid="project-opening-overlay"
|
data-testid="project-opening-overlay"
|
||||||
>
|
>
|
||||||
<div className="flex flex-col items-center gap-4 p-8 rounded-2xl bg-card border border-border shadow-2xl">
|
<div className="flex flex-col items-center gap-4 p-8 rounded-2xl bg-card border border-border shadow-2xl">
|
||||||
<Loader2 className="w-10 h-10 text-brand-500 animate-spin" />
|
<Spinner size="xl" />
|
||||||
<p className="text-foreground font-medium">Initializing project...</p>
|
<p className="text-foreground font-medium">Initializing project...</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user