refactor(ui): adopt the Spinner primitive and drop redundant icon sizing

Two mechanical cleanups over the same files.

<Loader2 className="size-4 animate-spin"> and <Loader2 className="animate-spin">
appeared 28 times across 20 files while Spinner variant="circle" — the same
LoaderCircleIcon with animate-spin already baked in — was the idiom elsewhere.
Loader2 spinners with a deliberate size (size-3/5/6/10/16) or a colour are left
alone: Spinner sets no size of its own and would fall back to lucide's 24px.

Separately, 51 icons carried className="size-4" inside a Button, DropdownMenuItem
or AppHeaderAction, all of which already force [&_svg]:size-4 on descendants, so
the class was a no-op. Only those 51 are stripped — the sites picked by walking
each icon's real JSX ancestors through the TypeScript AST, because an indentation
heuristic mistakes multi-line opening tags for the parent. The 40 icons with no
forcing ancestor (Alert, TabsTrigger, AppHeaderTitle, a resize handle) keep their
size-4: without it they would render at lucide's 24px.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Sergey Kozyrenko
2026-07-17 09:36:20 +07:00
co-authored by Claude Opus 4.8
parent 5d15be2f4f
commit d4dabc2783
26 changed files with 103 additions and 132 deletions
@@ -1,11 +1,10 @@
import type { ReactNode } from 'react';
import { Loader2 } from 'lucide-react';
import { Breadcrumb, BreadcrumbItem, BreadcrumbList, BreadcrumbPage } from '@/components/ui/breadcrumb';
import { Button, type ButtonProps } from '@/components/ui/button';
import { Separator } from '@/components/ui/separator';
import { SidebarTrigger } from '@/components/ui/sidebar';
import { Spinner } from '@/components/ui/spinner';
import { cn } from '@/lib/utils';
interface AppHeaderActionProps extends Omit<ButtonProps, 'children'> {
@@ -49,7 +48,7 @@ export function AppHeaderAction({
size={size}
{...props}
>
{loading ? <Loader2 className="size-4 animate-spin" /> : icon}
{loading ? <Spinner variant="circle" /> : icon}
<span className="hidden md:inline">{label}</span>
{endIcon ? <span className="hidden md:inline-flex">{endIcon}</span> : null}
</Button>
@@ -297,7 +297,7 @@ export function MainSidebar() {
>
<Avatar className="bg-background dark:bg-muted size-8 rounded-lg">
<AvatarFallback className="flex size-8 items-center justify-center">
<UserIcon className="size-4" />
<UserIcon />
</AvatarFallback>
</Avatar>
<div className="grid flex-1 text-left text-sm leading-tight">
@@ -347,21 +347,21 @@ export function MainSidebar() {
className="dark:data-[state=active]:bg-card h-6 px-2"
value="system"
>
<Monitor className="size-4" />
<Monitor />
</TabsTrigger>
<TabsTrigger
aria-label="Light theme"
className="dark:data-[state=active]:bg-card h-6 px-2"
value="light"
>
<Sun className="size-4" />
<Sun />
</TabsTrigger>
<TabsTrigger
aria-label="Dark theme"
className="dark:data-[state=active]:bg-card h-6 px-2"
value="dark"
>
<Moon className="size-4" />
<Moon />
</TabsTrigger>
</TabsList>
</Tabs>
@@ -93,7 +93,7 @@ export function SettingsSidebar() {
<SidebarFooter>
<SidebarMenuButton asChild>
<NavLink to={returnUrl}>
<ArrowLeft className="size-4" />
<ArrowLeft />
Back to App
</NavLink>
</SidebarMenuButton>
+4 -4
View File
@@ -39,10 +39,10 @@ that every list reuses.
## Components
| File | Role |
| ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
| [`detail-navigation/`](detail-navigation/) | Prev / Position / Next toolbar + listbox sheet for detail pages, and the navigation hooks that feed it. |
| [`inline-edit/`](inline-edit/) | Generic inline-edit input (Save/Cancel addons, Enter/Escape) plus the paired `useInlineEdit` state machine. |
| File | Role |
| ------------------------------------------ | ----------------------------------------------------------------------------------------------------------- |
| [`detail-navigation/`](detail-navigation/) | Prev / Position / Next toolbar + listbox sheet for detail pages, and the navigation hooks that feed it. |
| [`inline-edit/`](inline-edit/) | Generic inline-edit input (Save/Cancel addons, Enter/Escape) plus the paired `useInlineEdit` state machine. |
## Hooks
@@ -1,6 +1,6 @@
import type { ReactElement } from 'react';
import { Loader2, Trash2 } from 'lucide-react';
import { Trash2 } from 'lucide-react';
import { cloneElement, isValidElement, useState } from 'react';
import { Button } from '@/components/ui/button';
@@ -12,6 +12,7 @@ import {
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
import { Spinner } from '@/components/ui/spinner';
import { cn } from '@/lib/utils';
type ConfirmationDialogIconProps = ReactElement<React.SVGProps<SVGSVGElement>>;
@@ -127,7 +128,7 @@ function ConfirmationDialog({
}}
variant={confirmVariant}
>
{isProcessing ? <Loader2 className="size-4 animate-spin" /> : processIcon(confirmIcon)}
{isProcessing ? <Spinner variant="circle" /> : processIcon(confirmIcon)}
{confirmText}
</Button>
</DialogFooter>
@@ -184,7 +184,7 @@ export function FileManagerBulkActionsBar({
handleActionClick(action);
}}
>
{action.icon ? <action.icon className="size-4" /> : null}
{action.icon ? <action.icon /> : null}
{action.label}
</DropdownMenuItem>
))}
@@ -1,7 +1,8 @@
import { Check, Loader2, X } from 'lucide-react';
import { Check, X } from 'lucide-react';
import { type KeyboardEvent, type Ref } from 'react';
import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput } from '@/components/ui/input-group';
import { Spinner } from '@/components/ui/spinner';
import { cn } from '@/lib/utils';
interface InlineEditInputProps {
@@ -98,7 +99,7 @@ export function InlineEditInput({
disabled={busy}
onClick={onSave}
>
{busy ? <Loader2 className="animate-spin" /> : <Check />}
{busy ? <Spinner variant="circle" /> : <Check />}
</InputGroupButton>
<InputGroupButton
aria-label="Cancel"
@@ -1,8 +1,9 @@
import type { ComponentType } from 'react';
import { Loader2, Replace } from 'lucide-react';
import { Replace } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Spinner } from '@/components/ui/spinner';
interface OverwriteButtonsProps {
/**
@@ -66,7 +67,7 @@ export function OverwriteButtons({
onClick={primaryType === 'submit' ? undefined : onPrimary}
type={primaryType}
>
{isProcessing ? <Loader2 className="animate-spin" /> : <PrimaryIcon />}
{isProcessing ? <Spinner variant="circle" /> : <PrimaryIcon />}
{primaryLabel}
</Button>
<Button
+2 -2
View File
@@ -941,8 +941,8 @@ function DataTableColumnHeader<TData, TValue = unknown>({ column, title }: DataT
variant="link"
>
{title}
{sorted === 'asc' ? <ArrowDown className="size-4" /> : null}
{sorted === 'desc' ? <ArrowUp className="size-4" /> : null}
{sorted === 'asc' ? <ArrowDown /> : null}
{sorted === 'desc' ? <ArrowUp /> : null}
</Button>
);
}
@@ -1,7 +1,7 @@
import { Loader2 } from 'lucide-react';
import { useFormContext } from 'react-hook-form';
import { Button } from '@/components/ui/button';
import { Spinner } from '@/components/ui/spinner';
interface FormSubmitButtonProps extends React.ComponentProps<typeof Button> {
/**
@@ -54,7 +54,7 @@ function FormSubmitButton({ children, icon, loading, requireValid = true, ...pro
const isFormInvalid = !!formState && requireValid && formState.isSubmitted && !formState.isValid;
const isDisabled = isSubmitting || isFormInvalid;
const renderedIcon = isSubmitting ? <Loader2 className="size-4 animate-spin" /> : icon;
const renderedIcon = isSubmitting ? <Spinner variant="circle" /> : icon;
return (
<Button
@@ -1,4 +1,4 @@
import { ArrowDownToLine, ArrowUp, FolderOpen, Loader2, RefreshCw, TriangleAlert } from 'lucide-react';
import { ArrowDownToLine, ArrowUp, FolderOpen, RefreshCw, TriangleAlert } from 'lucide-react';
import { useCallback, useMemo, useState } from 'react';
import {
@@ -28,6 +28,7 @@ import {
} from '@/components/ui/dialog';
import { Empty, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from '@/components/ui/empty';
import { Label } from '@/components/ui/label';
import { Spinner } from '@/components/ui/spinner';
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
import { findPullConflicts } from './flow-files-conflicts';
@@ -443,7 +444,7 @@ function FlowFilesPullDialogForm({ cachedFiles, flowId, onClose, onSuccess }: Fl
type="button"
variant="outline"
>
{isListingLoading ? <Loader2 className="animate-spin" /> : <RefreshCw />}
{isListingLoading ? <Spinner variant="circle" /> : <RefreshCw />}
</Button>
</span>
</TooltipTrigger>
@@ -1,4 +1,4 @@
import { ArrowDownToLine, FolderInput, FolderOutput, FolderUp, Loader2, Search, Upload, X } from 'lucide-react';
import { ArrowDownToLine, FolderInput, FolderOutput, FolderUp, Search, Upload, X } from 'lucide-react';
import { useCallback, useMemo, useState } from 'react';
import { toast } from 'sonner';
@@ -20,6 +20,7 @@ import { Button } from '@/components/ui/button';
import { Empty, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from '@/components/ui/empty';
import { Form, FormControl, FormField, FormItem } from '@/components/ui/form';
import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput } from '@/components/ui/input-group';
import { Spinner } from '@/components/ui/spinner';
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
import { StatusType } from '@/graphql/types';
import { useFilesDragAndDrop } from '@/hooks/use-files-drag-and-drop';
@@ -255,7 +256,7 @@ function FlowFiles() {
type="button"
variant="outline"
>
{upload.isUploading ? <Loader2 className="animate-spin" /> : <Upload />}
{upload.isUploading ? <Spinner variant="circle" /> : <Upload />}
</Button>
</span>
</TooltipTrigger>
@@ -1,5 +1,5 @@
import { zodResolver } from '@hookform/resolvers/zod';
import { Check, ChevronDown, ListFilter, Loader2, Plus, Search, Trash2, X } from 'lucide-react';
import { Check, ChevronDown, ListFilter, Plus, Search, Trash2, X } from 'lucide-react';
import { useEffect, useMemo, useState } from 'react';
import { useForm } from 'react-hook-form';
import { useDebouncedCallback } from 'use-debounce';
@@ -16,6 +16,7 @@ import { Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTi
import { Form, FormControl, FormField } from '@/components/ui/form';
import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput } from '@/components/ui/input-group';
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
import { Spinner } from '@/components/ui/spinner';
import { StatusType } from '@/graphql/types';
import { useAutoScroll } from '@/hooks/use-auto-scroll';
import { Log } from '@/lib/log';
@@ -549,7 +550,7 @@ function FlowAssistantMessages({ className }: FlowAssistantMessagesProps) {
<Empty>
<EmptyHeader>
<EmptyMedia variant="icon">
<Loader2 className="animate-spin" />
<Spinner variant="circle" />
</EmptyMedia>
<EmptyTitle>Creating assistant...</EmptyTitle>
<EmptyDescription>Please wait while we set up your new assistant</EmptyDescription>
@@ -177,7 +177,7 @@ function FlowTerminal() {
title="Previous match"
type="button"
>
<ChevronUp className="size-4" />
<ChevronUp />
</InputGroupButton>
<InputGroupButton
onClick={handleFindNext}
@@ -185,7 +185,7 @@ function FlowTerminal() {
title="Next match"
type="button"
>
<ChevronDown className="size-4" />
<ChevronDown />
</InputGroupButton>
</>
)}
@@ -196,7 +196,7 @@ function FlowTerminal() {
title="Clear search"
type="button"
>
<X className="size-4" />
<X />
</InputGroupButton>
)}
</InputGroupAddon>
@@ -1,6 +1,6 @@
import type { ReactNode } from 'react';
import { Ellipsis, HatGlasses, LibraryBig, Loader2, Pencil, Trash } from 'lucide-react';
import { Ellipsis, HatGlasses, LibraryBig, Pencil, Trash } from 'lucide-react';
import { useCallback, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { toast } from 'sonner';
@@ -236,12 +236,12 @@ export function KnowledgeHeader({
>
{isAnonymizing ? (
<>
<Loader2 className="size-4 animate-spin" />
<Spinner variant="circle" />
Anonymizing...
</>
) : (
<>
<HatGlasses className="size-4" />
<HatGlasses />
Anonymize
</>
)}
@@ -254,7 +254,7 @@ export function KnowledgeHeader({
className="cursor-default hover:bg-transparent focus:bg-transparent"
onSelect={(event) => event.preventDefault()}
>
<LibraryBig className="size-4" />
<LibraryBig />
Knowledges
<div className="-my-1.5 -mr-2 ml-auto flex items-center">
<DetailNavigationButtons<Knowledge>
@@ -302,12 +302,12 @@ export function KnowledgeHeader({
>
{isDeleting ? (
<>
<Loader2 className="size-4 animate-spin" />
<Spinner variant="circle" />
Deleting...
</>
) : (
<>
<Trash className="size-4" />
<Trash />
Delete
</>
)}
+9 -8
View File
@@ -41,6 +41,7 @@ import {
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from '@/components/ui/resizable';
import { Spinner } from '@/components/ui/spinner';
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
import FlowCentralTabs from '@/features/flows/flow-central-tabs';
import FlowTabs from '@/features/flows/flow-tabs';
@@ -295,7 +296,7 @@ function Flow() {
className="cursor-default hover:bg-transparent focus:bg-transparent"
onSelect={(event) => event.preventDefault()}
>
<GitFork className="size-4" />
<GitFork />
Flows
<div className="-my-1.5 -mr-2 ml-auto flex items-center">
<DetailNavigationButtons<FlowItem>
@@ -331,7 +332,7 @@ function Flow() {
>
{isFinishing ? (
<>
<Loader2 className="animate-spin" />
<Spinner variant="circle" />
Finishing...
</>
) : (
@@ -349,12 +350,12 @@ function Flow() {
>
{isDeleting ? (
<>
<Loader2 className="size-4 animate-spin" />
<Spinner variant="circle" />
Deleting...
</>
) : (
<>
<Trash className="size-4" />
<Trash />
Delete
</>
)}
@@ -496,7 +497,7 @@ function FlowReportDropdown() {
disabled={isReportDisabled}
onClick={handleOpenWebView}
>
<ExternalLink className="size-4" />
<ExternalLink />
Open web view
</DropdownMenuItem>
<DropdownMenuItem
@@ -504,7 +505,7 @@ function FlowReportDropdown() {
disabled={isReportDisabled}
onClick={handleCopyToClipboard}
>
<Copy className="size-4" />
<Copy />
Copy to clipboard
</DropdownMenuItem>
<DropdownMenuItem
@@ -512,7 +513,7 @@ function FlowReportDropdown() {
disabled={isReportDisabled}
onClick={handleDownloadMD}
>
<Download className="size-4" />
<Download />
Download MD
</DropdownMenuItem>
<DropdownMenuItem
@@ -520,7 +521,7 @@ function FlowReportDropdown() {
disabled={isReportDisabled}
onClick={handleDownloadPDF}
>
<Download className="size-4" />
<Download />
Download PDF
</DropdownMenuItem>
</DropdownMenuContent>
+5 -4
View File
@@ -30,6 +30,7 @@ import {
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from '@/components/ui/empty';
import { Spinner } from '@/components/ui/spinner';
import { Toggle } from '@/components/ui/toggle';
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
import { RenameFlowDocument, ResultType, StatusType, type TerminalFragmentFragment } from '@/graphql/types';
@@ -416,7 +417,7 @@ function Flows() {
size="sm"
variant="outline"
>
<Star className="size-4" />
<Star />
</Toggle>
<DropdownMenu>
<DropdownMenuTrigger asChild>
@@ -449,7 +450,7 @@ function Flows() {
>
{finishingFlowIds.has(flow.id) ? (
<>
<Loader2 className="animate-spin" />
<Spinner variant="circle" />
Finishing...
</>
) : (
@@ -467,12 +468,12 @@ function Flows() {
>
{deletingFlowIds.has(flow.id) ? (
<>
<Loader2 className="size-4 animate-spin" />
<Spinner variant="circle" />
Deleting...
</>
) : (
<>
<Trash className="size-4" />
<Trash />
Delete
</>
)}
+3 -2
View File
@@ -29,6 +29,7 @@ import {
} from '@/components/ui/dropdown-menu';
import { Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from '@/components/ui/empty';
import { InputSearch } from '@/components/ui/input-search';
import { Spinner } from '@/components/ui/spinner';
import { KnowledgeDocType } from '@/graphql/types';
import { useTableState } from '@/hooks/use-table-state';
import { routes } from '@/lib/routes';
@@ -328,12 +329,12 @@ function Knowledges() {
>
{deletingIds.has(k.id) ? (
<>
<Loader2 className="size-4 animate-spin" />
<Spinner variant="circle" />
Deleting...
</>
) : (
<>
<Trash className="size-4" />
<Trash />
Delete
</>
)}
+3 -13
View File
@@ -1,15 +1,4 @@
import {
ColumnsSettings,
Copy,
FileSymlink,
Folder,
FolderPlus,
FolderUp,
Loader2,
Search,
Upload,
X,
} from 'lucide-react';
import { ColumnsSettings, Copy, FileSymlink, Folder, FolderPlus, FolderUp, Search, Upload, X } from 'lucide-react';
import { useCallback, useMemo, useState } from 'react';
import { toast } from 'sonner';
@@ -53,6 +42,7 @@ import {
import { Empty, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from '@/components/ui/empty';
import { FileDropZone } from '@/components/ui/file-drop-zone';
import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput } from '@/components/ui/input-group';
import { Spinner } from '@/components/ui/spinner';
import { ResourcesCopyDialog } from '@/features/resources/resources-copy-dialog';
import { ResourcesMkdirDialog } from '@/features/resources/resources-mkdir-dialog';
import { ResourcesMoveDialog } from '@/features/resources/resources-move-dialog';
@@ -405,7 +395,7 @@ function Resources() {
<AppHeaderAction
aria-label={upload.isUploading ? 'Uploading...' : 'Upload files'}
disabled={upload.isUploading}
icon={upload.isUploading ? <Loader2 className="animate-spin" /> : <Upload />}
icon={upload.isUploading ? <Spinner variant="circle" /> : <Upload />}
label={upload.isUploading ? 'Uploading...' : 'Upload files'}
onClick={upload.openFilePicker}
variant="secondary"
@@ -50,6 +50,7 @@ import { Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTi
import { Input } from '@/components/ui/input';
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { Spinner } from '@/components/ui/spinner';
import {
ApiTokenCreatedDocument,
ApiTokenDeletedDocument,
@@ -188,7 +189,7 @@ function CreateRowActions({
size="icon-sm"
variant="ghost"
>
{isLoading ? <Loader2 className="animate-spin" /> : <Check />}
{isLoading ? <Spinner variant="circle" /> : <Check />}
</Button>
<Button
aria-label="Cancel"
@@ -226,7 +227,7 @@ function EditRowActions({
size="icon-sm"
variant="ghost"
>
{isLoading ? <Loader2 className="animate-spin" /> : <Check />}
{isLoading ? <Spinner variant="circle" /> : <Check />}
</Button>
<Button
aria-label="Cancel"
@@ -738,7 +739,7 @@ function SettingsAPITokens() {
>
{isDeleteLoading && deletingToken?.tokenId === token.tokenId ? (
<>
<Loader2 className="animate-spin" />
<Spinner variant="circle" />
Deleting...
</>
) : (
@@ -843,7 +844,7 @@ function SettingsAPITokens() {
rel="noopener noreferrer"
target="_blank"
>
<ExternalLink className="size-4" />
<ExternalLink />
GraphQL Playground
</a>
</DropdownMenuItem>
@@ -853,7 +854,7 @@ function SettingsAPITokens() {
rel="noopener noreferrer"
target="_blank"
>
<ExternalLink className="size-4" />
<ExternalLink />
Swagger UI
</a>
</DropdownMenuItem>
@@ -919,7 +920,7 @@ function SettingsAPITokens() {
onClick={handleCreateNew}
variant="secondary"
>
<Plus className="size-4" />
<Plus />
Create Token
</Button>
</EmptyContent>
@@ -985,7 +986,7 @@ function SettingsAPITokens() {
}}
variant="secondary"
>
<Copy className="size-4" />
<Copy />
Copy Token
</Button>
<Button
@@ -72,6 +72,7 @@ import {
import { Empty, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from '@/components/ui/empty';
import { Form, FormControl, FormItem, FormMessage } from '@/components/ui/form';
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
import { Spinner } from '@/components/ui/spinner';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import {
CreatePromptDocument,
@@ -662,13 +663,7 @@ function SettingsPrompt() {
<AppHeaderActions>
<AppHeaderAction
disabled={isLoading}
icon={
isValidateLoading ? (
<Loader2 className="size-4 animate-spin" />
) : (
<CheckCircle className="size-4" />
)
}
icon={isValidateLoading ? <Spinner variant="circle" /> : <CheckCircle />}
label={isValidateLoading ? 'Validating...' : 'Validate'}
onClick={handleValidate}
type="button"
@@ -676,7 +671,7 @@ function SettingsPrompt() {
/>
<AppHeaderAction
form={activeFormId}
icon={<Save className="size-4" />}
icon={<Save />}
label="Save"
loading={isLoading}
type="submit"
@@ -699,18 +694,14 @@ function SettingsPrompt() {
{hasOverride && (
<>
<DropdownMenuItem onClick={() => setIsDiffDialogOpen(true)}>
<FileDiff className="size-4" />
<FileDiff />
Diff
</DropdownMenuItem>
<DropdownMenuItem
disabled={isLoading}
onClick={handleReset}
>
{isDeleteLoading ? (
<Loader2 className="size-4 animate-spin" />
) : (
<RotateCcw className="size-4" />
)}
{isDeleteLoading ? <Spinner variant="circle" /> : <RotateCcw />}
{isDeleteLoading ? 'Resetting...' : 'Reset'}
</DropdownMenuItem>
<DropdownMenuSeparator />
@@ -1075,7 +1066,7 @@ function Variables({ currentTemplate, onVariableClick, variables }: VariablesPro
size="sm"
variant="secondary"
>
<Braces className="size-4" />
<Braces />
{VARIABLES_TITLE}
<Badge
className="ml-auto h-5 font-normal tabular-nums"
@@ -338,11 +338,7 @@ function SettingsPrompts() {
variant="link"
>
Agent Name
{sorted === 'asc' ? (
<ArrowDown className="size-4" />
) : sorted === 'desc' ? (
<ArrowUp className="size-4" />
) : null}
{sorted === 'asc' ? <ArrowDown /> : sorted === 'desc' ? <ArrowUp /> : null}
</Button>
);
},
@@ -509,11 +505,7 @@ function SettingsPrompts() {
variant="link"
>
Tool Name
{sorted === 'asc' ? (
<ArrowDown className="size-4" />
) : sorted === 'desc' ? (
<ArrowUp className="size-4" />
) : null}
{sorted === 'asc' ? <ArrowDown /> : sorted === 'desc' ? <ArrowUp /> : null}
</Button>
);
},
@@ -57,6 +57,7 @@ import { Input } from '@/components/ui/input';
import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput } from '@/components/ui/input-group';
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { Spinner } from '@/components/ui/spinner';
import {
AgentConfigType,
CreateProviderDocument,
@@ -1667,9 +1668,9 @@ function SettingsProvider() {
}}
>
{isAgentTestLoading && currentAgentKey === agentKey ? (
<Loader2 className="size-4 animate-spin" />
<Spinner variant="circle" />
) : (
<Play className="size-4" />
<Play />
)}
<span className="no-underline! hover:no-underline!">
{isAgentTestLoading && currentAgentKey === agentKey ? 'Testing...' : 'Test'}
@@ -1879,7 +1880,7 @@ function SettingsProvider() {
<AppHeaderActions>
<AppHeaderAction
disabled={isLoading || isTestLoading || isAgentTestLoading}
icon={isTestLoading ? <Loader2 className="size-4 animate-spin" /> : <Play className="size-4" />}
icon={isTestLoading ? <Spinner variant="circle" /> : <Play />}
label={isTestLoading ? 'Testing...' : 'Test'}
onClick={() => handleTest()}
type="button"
@@ -1887,7 +1888,7 @@ function SettingsProvider() {
/>
<AppHeaderAction
form="provider-form"
icon={<Save className="size-4" />}
icon={<Save />}
label={isNew ? 'Create' : 'Save'}
loading={isLoading}
type="submit"
@@ -1912,11 +1913,7 @@ function SettingsProvider() {
disabled={isDeleteLoading}
onClick={handleDelete}
>
{isDeleteLoading ? (
<Loader2 className="size-4 animate-spin" />
) : (
<Trash2 className="size-4" />
)}
{isDeleteLoading ? <Spinner variant="circle" /> : <Trash2 />}
{isDeleteLoading ? 'Deleting...' : 'Delete'}
</DropdownMenuItem>
</DropdownMenuContent>
@@ -23,6 +23,7 @@ import {
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from '@/components/ui/empty';
import { Spinner } from '@/components/ui/spinner';
import { DeleteProviderDocument, ProviderType, SettingsProvidersDocument } from '@/graphql/types';
import { useTableState } from '@/hooks/use-table-state';
import { routes } from '@/lib/routes';
@@ -90,7 +91,7 @@ export function SettingsProvidersHeader() {
key={type}
onClick={() => handleProviderCreate(type)}
>
{Icon && <Icon className="size-4" />}
{Icon && <Icon />}
{label}
</DropdownMenuItem>
);
@@ -264,7 +265,7 @@ function SettingsProviders() {
Edit
</DropdownMenuItem>
<DropdownMenuItem onClick={() => handleProviderClone(provider.id)}>
<Copy className="size-4" />
<Copy />
Clone
</DropdownMenuItem>
<DropdownMenuSeparator />
@@ -274,12 +275,12 @@ function SettingsProviders() {
>
{isDeleteLoading && deletingProvider?.id === provider.id ? (
<>
<Loader2 className="size-4 animate-spin" />
<Spinner variant="circle" />
Deleting...
</>
) : (
<>
<Trash className="size-4" />
<Trash />
Delete
</>
)}
@@ -459,7 +460,7 @@ function SettingsProviders() {
onClick={() => navigate(routes.settings.newProvider())}
variant="secondary"
>
<Plus className="size-4" />
<Plus />
Add Provider
</Button>
</EmptyContent>
+7 -17
View File
@@ -1,17 +1,7 @@
import type { ReactNode } from 'react';
import { skipToken, useQuery } from '@apollo/client/react';
import {
ChevronDown,
Ellipsis,
FileSymlink,
FileText,
LayoutTemplate,
Loader2,
Pencil,
Save,
Trash,
} from 'lucide-react';
import { ChevronDown, Ellipsis, FileSymlink, FileText, LayoutTemplate, Pencil, Save, Trash } from 'lucide-react';
import { useCallback, useMemo, useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { toast } from 'sonner';
@@ -478,7 +468,7 @@ function Template() {
<AppHeaderAction
disabled={!isNew && !hasUnsavedChanges}
form="template-form"
icon={<Save className="size-4" />}
icon={<Save />}
label={isNew ? 'Create' : 'Save'}
loading={isSaving}
type="submit"
@@ -507,7 +497,7 @@ function Template() {
className="cursor-default hover:bg-transparent focus:bg-transparent"
onSelect={(event) => event.preventDefault()}
>
<FileText className="size-4" />
<FileText />
Templates
<div className="-my-1.5 -mr-2 ml-auto flex items-center">
<DetailNavigationButtons<Template>
@@ -521,7 +511,7 @@ function Template() {
</>
)}
<DropdownMenuItem onClick={handleTemplateRenameStart}>
<Pencil className="size-4" />
<Pencil />
Rename
</DropdownMenuItem>
<DropdownMenuSeparator />
@@ -548,12 +538,12 @@ function Template() {
>
{isDeleting ? (
<>
<Loader2 className="size-4 animate-spin" />
<Spinner variant="circle" />
Deleting...
</>
) : (
<>
<Trash className="size-4" />
<Trash />
Delete
</>
)}
@@ -660,7 +650,7 @@ function Template() {
size="sm"
variant="secondary"
>
<LayoutTemplate className="size-4" />
<LayoutTemplate />
{PRESETS_TITLE}
<Badge
className="ml-auto h-5 font-normal tabular-nums"
+5 -4
View File
@@ -1,6 +1,6 @@
import type { ColumnDef } from '@tanstack/react-table';
import { Ellipsis, FileText, Loader2, Pencil, PencilLine, Plus, Trash } from 'lucide-react';
import { Ellipsis, FileText, Pencil, PencilLine, Plus, Trash } from 'lucide-react';
import { useCallback, useRef, useState } from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
import { toast } from 'sonner';
@@ -25,6 +25,7 @@ import {
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from '@/components/ui/empty';
import { Spinner } from '@/components/ui/spinner';
import { useTableState } from '@/hooks/use-table-state';
import { routes } from '@/lib/routes';
import { mergeHrefWithSearchParams } from '@/lib/url-params';
@@ -203,12 +204,12 @@ function Templates() {
>
{deletingIds.has(template.id) ? (
<>
<Loader2 className="size-4 animate-spin" />
<Spinner variant="circle" />
Deleting...
</>
) : (
<>
<Trash className="size-4" />
<Trash />
Delete
</>
)}
@@ -281,7 +282,7 @@ function Templates() {
onClick={() => navigate(routes.newTemplate)}
variant="secondary"
>
<Plus className="size-4" />
<Plus />
New Template
</Button>
</EmptyContent>