feat: replace Loader2 with Spinner component for consistent loading indicators

This commit is contained in:
Anish Sarkar 2026-01-25 15:23:45 +05:30
parent 9215118bab
commit 2d17d1a1b6
34 changed files with 113 additions and 105 deletions

View file

@ -1,6 +1,6 @@
"use client"; "use client";
import { ChevronDown, ChevronUp, FileX, Loader2, Plus } from "lucide-react"; import { ChevronDown, ChevronUp, FileX, Plus } from "lucide-react";
import { motion } from "motion/react"; import { motion } from "motion/react";
import { useParams } from "next/navigation"; import { useParams } from "next/navigation";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
@ -8,6 +8,7 @@ import React from "react";
import { useDocumentUploadDialog } from "@/components/assistant-ui/document-upload-popup"; import { useDocumentUploadDialog } from "@/components/assistant-ui/document-upload-popup";
import { DocumentViewer } from "@/components/document-viewer"; import { DocumentViewer } from "@/components/document-viewer";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Spinner } from "@/components/ui/spinner";
import { Checkbox } from "@/components/ui/checkbox"; import { Checkbox } from "@/components/ui/checkbox";
import { import {
Table, Table,
@ -114,7 +115,7 @@ export function DocumentsTableShell({
{loading ? ( {loading ? (
<div className="flex h-[400px] w-full items-center justify-center"> <div className="flex h-[400px] w-full items-center justify-center">
<div className="flex flex-col items-center gap-2"> <div className="flex flex-col items-center gap-2">
<Loader2 className="h-8 w-8 animate-spin text-primary" /> <Spinner size="lg" className="text-primary" />
<p className="text-sm text-muted-foreground">{t("loading")}</p> <p className="text-sm text-muted-foreground">{t("loading")}</p>
</div> </div>
</div> </div>

View file

@ -1,8 +1,7 @@
"use client"; "use client";
import { useQueryClient } from "@tanstack/react-query";
import { useAtom } from "jotai"; import { useAtom } from "jotai";
import { AlertCircle, ArrowLeft, FileText, Loader2, Save } from "lucide-react"; import { AlertCircle, ArrowLeft, FileText, Save } from "lucide-react";
import { motion } from "motion/react"; import { motion } from "motion/react";
import { useParams, useRouter } from "next/navigation"; import { useParams, useRouter } from "next/navigation";
import { useEffect, useMemo, useState } from "react"; import { useEffect, useMemo, useState } from "react";
@ -21,6 +20,7 @@ import {
} from "@/components/ui/alert-dialog"; } from "@/components/ui/alert-dialog";
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 { Spinner } from "@/components/ui/spinner";
import { notesApiService } from "@/lib/apis/notes-api.service"; import { notesApiService } from "@/lib/apis/notes-api.service";
import { authenticatedFetch, getBearerToken, redirectToLogin } from "@/lib/auth-utils"; import { authenticatedFetch, getBearerToken, redirectToLogin } from "@/lib/auth-utils";
@ -78,7 +78,6 @@ function extractTitleFromBlockNote(blocknoteDocument: BlockNoteDocument): string
export default function EditorPage() { export default function EditorPage() {
const params = useParams(); const params = useParams();
const router = useRouter(); const router = useRouter();
const queryClient = useQueryClient();
const documentId = params.documentId as string; const documentId = params.documentId as string;
const searchSpaceId = Number(params.search_space_id); const searchSpaceId = Number(params.search_space_id);
const isNewNote = documentId === "new"; const isNewNote = documentId === "new";
@ -349,8 +348,8 @@ export default function EditorPage() {
<div className="flex items-center justify-center min-h-[400px] p-6"> <div className="flex items-center justify-center min-h-[400px] p-6">
<Card className="w-full max-w-md"> <Card className="w-full max-w-md">
<CardContent className="flex flex-col items-center justify-center py-12"> <CardContent className="flex flex-col items-center justify-center py-12">
<Loader2 className="h-12 w-12 text-primary animate-spin mb-4" /> <Spinner size="xl" className="text-primary mb-4" />
<p className="text-muted-foreground">Loading editor...</p> <p className="text-muted-foreground">Loading editor</p>
</CardContent> </CardContent>
</Card> </Card>
</div> </div>
@ -437,7 +436,7 @@ export default function EditorPage() {
> >
{saving ? ( {saving ? (
<> <>
<Loader2 className="h-3.5 w-3.5 md:h-4 md:w-4 animate-spin" /> <Spinner size="sm" className="h-3.5 w-3.5 md:h-4 md:w-4" />
<span className="text-xs md:text-sm">{isNewNote ? "Creating" : "Saving"}</span> <span className="text-xs md:text-sm">{isNewNote ? "Creating" : "Saving"}</span>
</> </>
) : ( ) : (

View file

@ -1,7 +1,6 @@
"use client"; "use client";
import { useAtomValue } from "jotai"; import { useAtomValue } from "jotai";
import { Loader2 } from "lucide-react";
import { motion } from "motion/react"; import { motion } from "motion/react";
import { useParams, useRouter } from "next/navigation"; import { useParams, useRouter } from "next/navigation";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
@ -17,6 +16,7 @@ import {
import { Logo } from "@/components/Logo"; import { Logo } from "@/components/Logo";
import { LLMConfigForm, type LLMConfigFormData } from "@/components/shared/llm-config-form"; import { LLMConfigForm, type LLMConfigFormData } from "@/components/shared/llm-config-form";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Spinner } from "@/components/ui/spinner";
import { getBearerToken, redirectToLogin } from "@/lib/auth-utils"; import { getBearerToken, redirectToLogin } from "@/lib/auth-utils";
export default function OnboardPage() { export default function OnboardPage() {
@ -156,7 +156,7 @@ export default function OnboardPage() {
<div className="relative"> <div className="relative">
<div className="absolute inset-0 blur-3xl bg-gradient-to-r from-violet-500/20 to-cyan-500/20 rounded-full" /> <div className="absolute inset-0 blur-3xl bg-gradient-to-r from-violet-500/20 to-cyan-500/20 rounded-full" />
<div className="relative flex items-center justify-center w-24 h-24 mx-auto rounded-2xl bg-gradient-to-br from-violet-500 to-purple-600 shadow-2xl shadow-violet-500/25"> <div className="relative flex items-center justify-center w-24 h-24 mx-auto rounded-2xl bg-gradient-to-br from-violet-500 to-purple-600 shadow-2xl shadow-violet-500/25">
<Loader2 className="h-12 w-12 text-white animate-spin" /> <Spinner size="xl" className="text-white" />
</div> </div>
</div> </div>
<div className="space-y-2"> <div className="space-y-2">

View file

@ -14,7 +14,6 @@ import {
Hash, Hash,
Link2, Link2,
LinkIcon, LinkIcon,
Loader2,
Logs, Logs,
type LucideIcon, type LucideIcon,
MessageCircle, MessageCircle,
@ -106,6 +105,7 @@ import {
} from "@/components/ui/table"; } from "@/components/ui/table";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Textarea } from "@/components/ui/textarea"; import { Textarea } from "@/components/ui/textarea";
import { Spinner } from "@/components/ui/spinner";
import type { import type {
CreateInviteRequest, CreateInviteRequest,
DeleteInviteRequest, DeleteInviteRequest,
@ -321,7 +321,7 @@ export default function TeamManagementPage() {
animate={{ opacity: 1, scale: 1 }} animate={{ opacity: 1, scale: 1 }}
className="flex flex-col items-center gap-4" className="flex flex-col items-center gap-4"
> >
<Loader2 className="h-10 w-10 text-primary animate-spin" /> <Spinner size="lg" className="text-primary" />
<p className="text-muted-foreground">Loading team data...</p> <p className="text-muted-foreground">Loading team data...</p>
</motion.div> </motion.div>
</div> </div>
@ -571,7 +571,7 @@ function MembersTab({
if (loading) { if (loading) {
return ( return (
<div className="flex items-center justify-center py-12"> <div className="flex items-center justify-center py-12">
<Loader2 className="h-8 w-8 text-primary animate-spin" /> <Spinner size="md" className="text-primary" />
</div> </div>
); );
} }
@ -911,7 +911,7 @@ function RolesTab({
if (loading) { if (loading) {
return ( return (
<div className="flex items-center justify-center py-12"> <div className="flex items-center justify-center py-12">
<Loader2 className="h-8 w-8 text-primary animate-spin" /> <Spinner size="md" className="text-primary" />
</div> </div>
); );
} }
@ -1068,7 +1068,7 @@ function InvitesTab({
if (loading) { if (loading) {
return ( return (
<div className="flex items-center justify-center py-12"> <div className="flex items-center justify-center py-12">
<Loader2 className="h-8 w-8 text-primary animate-spin" /> <Spinner size="md" className="text-primary" />
</div> </div>
); );
} }
@ -1446,7 +1446,7 @@ function CreateInviteDialog({
<Button onClick={handleCreate} disabled={creating}> <Button onClick={handleCreate} disabled={creating}>
{creating ? ( {creating ? (
<> <>
<Loader2 className="h-4 w-4 mr-2 animate-spin" /> <Spinner size="sm" className="mr-2" />
Creating Creating
</> </>
) : ( ) : (
@ -1699,7 +1699,7 @@ function CreateRoleDialog({
<Button onClick={handleCreate} disabled={creating || !name.trim()}> <Button onClick={handleCreate} disabled={creating || !name.trim()}>
{creating ? ( {creating ? (
<> <>
<Loader2 className="h-4 w-4 mr-2 animate-spin" /> <Spinner size="sm" className="mr-2" />
Creating Creating
</> </>
) : ( ) : (

View file

@ -1,7 +1,7 @@
"use client"; "use client";
import { useAtomValue } from "jotai"; import { useAtomValue } from "jotai";
import { Loader2, Menu, User } from "lucide-react"; import { Menu, User } from "lucide-react";
import { AnimatePresence, motion } from "motion/react"; import { AnimatePresence, motion } from "motion/react";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
@ -11,6 +11,7 @@ import { currentUserAtom } from "@/atoms/user/user-query.atoms";
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 { Spinner } from "@/components/ui/spinner";
interface ProfileContentProps { interface ProfileContentProps {
onMenuClick: () => void; onMenuClick: () => void;
@ -129,7 +130,7 @@ export function ProfileContent({ onMenuClick }: ProfileContentProps) {
> >
{isUserLoading ? ( {isUserLoading ? (
<div className="flex items-center justify-center py-12"> <div className="flex items-center justify-center py-12">
<Loader2 className="h-6 w-6 animate-spin text-muted-foreground" /> <Spinner size="md" className="text-muted-foreground" />
</div> </div>
) : ( ) : (
<form onSubmit={handleSubmit} className="space-y-6"> <form onSubmit={handleSubmit} className="space-y-6">
@ -166,7 +167,7 @@ export function ProfileContent({ onMenuClick }: ProfileContentProps) {
<div className="flex justify-end"> <div className="flex justify-end">
<Button type="submit" disabled={isPending || !hasChanges}> <Button type="submit" disabled={isPending || !hasChanges}>
{isPending && <Loader2 className="mr-2 h-4 w-4 animate-spin" />} {isPending && <Spinner size="sm" className="mr-2" />}
{t("profile_save")} {t("profile_save")}
</Button> </Button>
</div> </div>

View file

@ -6,8 +6,6 @@ import {
AlertCircle, AlertCircle,
ArrowRight, ArrowRight,
CheckCircle2, CheckCircle2,
Clock,
Loader2,
LogIn, LogIn,
Shield, Shield,
Sparkles, Sparkles,
@ -30,6 +28,7 @@ import {
CardHeader, CardHeader,
CardTitle, CardTitle,
} from "@/components/ui/card"; } from "@/components/ui/card";
import { Spinner } from "@/components/ui/spinner";
import type { AcceptInviteResponse } from "@/contracts/types/invites.types"; import type { AcceptInviteResponse } from "@/contracts/types/invites.types";
import { invitesApiService } from "@/lib/apis/invites-api.service"; import { invitesApiService } from "@/lib/apis/invites-api.service";
import { getBearerToken } from "@/lib/auth-utils"; import { getBearerToken } from "@/lib/auth-utils";
@ -164,7 +163,7 @@ export default function InviteAcceptPage() {
animate={{ rotate: 360 }} animate={{ rotate: 360 }}
transition={{ duration: 1, repeat: Infinity, ease: "linear" }} transition={{ duration: 1, repeat: Infinity, ease: "linear" }}
> >
<Loader2 className="h-12 w-12 text-primary" /> <Spinner size="xl" className="text-primary" />
</motion.div> </motion.div>
<p className="mt-4 text-muted-foreground">Loading invite details...</p> <p className="mt-4 text-muted-foreground">Loading invite details...</p>
</CardContent> </CardContent>
@ -353,7 +352,7 @@ export default function InviteAcceptPage() {
<Button className="flex-1 gap-2" onClick={handleAccept} disabled={accepting}> <Button className="flex-1 gap-2" onClick={handleAccept} disabled={accepting}>
{accepting ? ( {accepting ? (
<> <>
<Loader2 className="h-4 w-4 animate-spin" /> <Spinner size="sm" />
Accepting... Accepting...
</> </>
) : ( ) : (

View file

@ -7,12 +7,13 @@ import {
useAssistantApi, useAssistantApi,
useAssistantState, useAssistantState,
} from "@assistant-ui/react"; } from "@assistant-ui/react";
import { FileText, Loader2, Paperclip, PlusIcon, Upload, XIcon } from "lucide-react"; import { FileText, Paperclip, PlusIcon, Upload, XIcon } from "lucide-react";
import Image from "next/image"; import Image from "next/image";
import { type FC, type PropsWithChildren, useEffect, useRef, useState } from "react"; import { type FC, type PropsWithChildren, useEffect, useRef, useState } from "react";
import { useShallow } from "zustand/shallow"; import { useShallow } from "zustand/shallow";
import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button"; import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Spinner } from "@/components/ui/spinner";
import { Dialog, DialogContent, DialogTitle, DialogTrigger } from "@/components/ui/dialog"; import { Dialog, DialogContent, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
import { import {
DropdownMenu, DropdownMenu,
@ -135,7 +136,7 @@ const AttachmentThumb: FC = () => {
if (isProcessing) { if (isProcessing) {
return ( return (
<div className="flex h-full w-full items-center justify-center bg-muted"> <div className="flex h-full w-full items-center justify-center bg-muted">
<Loader2 className="size-6 animate-spin text-muted-foreground" /> <Spinner size="md" className="text-muted-foreground" />
</div> </div>
); );
} }
@ -213,7 +214,7 @@ const AttachmentUI: FC = () => {
> >
{isProcessing ? ( {isProcessing ? (
<span className="flex items-center gap-1.5"> <span className="flex items-center gap-1.5">
<Loader2 className="size-3 animate-spin" /> <Spinner size="xs" />
Processing... Processing...
</span> </span>
) : ( ) : (

View file

@ -1,8 +1,8 @@
"use client"; "use client";
import { Loader2 } from "lucide-react";
import type { FC } from "react"; import type { FC } from "react";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { Spinner } from "@/components/ui/spinner";
interface ChatSessionStatusProps { interface ChatSessionStatusProps {
isAiResponding: boolean; isAiResponding: boolean;
@ -43,7 +43,7 @@ export const ChatSessionStatus: FC<ChatSessionStatusProps> = ({
className className
)} )}
> >
<Loader2 className="size-3.5 animate-spin" /> <Spinner size="xs" />
<span>Currently responding to {displayName}</span> <span>Currently responding to {displayName}</span>
</div> </div>
); );

View file

@ -1,13 +1,14 @@
"use client"; "use client";
import { useAtomValue } from "jotai"; import { useAtomValue } from "jotai";
import { Cable, Loader2 } from "lucide-react"; import { Cable } from "lucide-react";
import { useSearchParams } from "next/navigation"; import { useSearchParams } from "next/navigation";
import type { FC } from "react"; import type { FC } from "react";
import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms"; import { activeSearchSpaceIdAtom } from "@/atoms/search-spaces/search-space-query.atoms";
import { currentUserAtom } from "@/atoms/user/user-query.atoms"; import { currentUserAtom } from "@/atoms/user/user-query.atoms";
import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button"; import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button";
import { Dialog, DialogContent, DialogTitle } from "@/components/ui/dialog"; import { Dialog, DialogContent, DialogTitle } from "@/components/ui/dialog";
import { Spinner } from "@/components/ui/spinner";
import { Tabs, TabsContent } from "@/components/ui/tabs"; import { Tabs, TabsContent } from "@/components/ui/tabs";
import type { SearchSourceConnector } from "@/contracts/types/connector.types"; import type { SearchSourceConnector } from "@/contracts/types/connector.types";
import { useConnectorsElectric } from "@/hooks/use-connectors-electric"; import { useConnectorsElectric } from "@/hooks/use-connectors-electric";
@ -174,7 +175,7 @@ export const ConnectorIndicator: FC = () => {
onClick={() => handleOpenChange(true)} onClick={() => handleOpenChange(true)}
> >
{isLoading ? ( {isLoading ? (
<Loader2 className="size-4 animate-spin" /> <Spinner size="sm" />
) : ( ) : (
<> <>
<Cable className="size-4 stroke-[1.5px]" /> <Cable className="size-4 stroke-[1.5px]" />

View file

@ -1,9 +1,10 @@
"use client"; "use client";
import { IconBrandYoutube } from "@tabler/icons-react"; import { IconBrandYoutube } from "@tabler/icons-react";
import { FileText, Loader2 } from "lucide-react"; import { FileText } from "lucide-react";
import type { FC } from "react"; import type { FC } from "react";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Spinner } from "@/components/ui/spinner";
import { EnumConnectorName } from "@/contracts/enums/connector"; import { EnumConnectorName } from "@/contracts/enums/connector";
import { getConnectorIcon } from "@/contracts/enums/connectorIcons"; import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
@ -111,7 +112,7 @@ export const ConnectorCard: FC<ConnectorCardProps> = ({
</div> </div>
{isIndexing ? ( {isIndexing ? (
<p className="text-[11px] text-primary mt-1 flex items-center gap-1.5"> <p className="text-[11px] text-primary mt-1 flex items-center gap-1.5">
<Loader2 className="size-3 animate-spin" /> <Spinner size="xs" />
Syncing Syncing
</p> </p>
) : isConnected ? ( ) : isConnected ? (
@ -151,7 +152,7 @@ export const ConnectorCard: FC<ConnectorCardProps> = ({
disabled={isConnecting || !isEnabled} disabled={isConnecting || !isEnabled}
> >
{isConnecting ? ( {isConnecting ? (
<Loader2 className="size-3 animate-spin" /> <Spinner size="xs" />
) : !isEnabled ? ( ) : !isEnabled ? (
"Unavailable" "Unavailable"
) : isConnected ? ( ) : isConnected ? (

View file

@ -1,8 +1,9 @@
"use client"; "use client";
import { ArrowLeft, Loader2 } from "lucide-react"; import { ArrowLeft } from "lucide-react";
import { type FC, useMemo } from "react"; import { type FC, useMemo } from "react";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Spinner } from "@/components/ui/spinner";
import type { EnumConnectorName } from "@/contracts/enums/connector"; import type { EnumConnectorName } from "@/contracts/enums/connector";
import { getConnectorIcon } from "@/contracts/enums/connectorIcons"; import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
import { getConnectorTypeDisplay } from "@/lib/connectors/utils"; import { getConnectorTypeDisplay } from "@/lib/connectors/utils";
@ -139,7 +140,7 @@ export const ConnectorConnectView: FC<ConnectorConnectViewProps> = ({
> >
{isSubmitting ? ( {isSubmitting ? (
<> <>
<Loader2 className="mr-2 h-4 w-4 animate-spin" /> <Spinner size="sm" className="mr-2" />
Connecting Connecting
</> </>
) : connectorType === "MCP_CONNECTOR" ? ( ) : connectorType === "MCP_CONNECTOR" ? (

View file

@ -1,8 +1,9 @@
"use client"; "use client";
import { ArrowLeft, Info, Loader2, RefreshCw, Trash2 } from "lucide-react"; import { ArrowLeft, Info, RefreshCw, Trash2 } from "lucide-react";
import { type FC, useCallback, useEffect, useMemo, useRef, useState } from "react"; import { type FC, useCallback, useEffect, useMemo, useRef, useState } from "react";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Spinner } from "@/components/ui/spinner";
import { getConnectorIcon } from "@/contracts/enums/connectorIcons"; import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
import type { SearchSourceConnector } from "@/contracts/types/connector.types"; import type { SearchSourceConnector } from "@/contracts/types/connector.types";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
@ -311,7 +312,7 @@ export const ConnectorEditView: FC<ConnectorEditViewProps> = ({
> >
{isDisconnecting ? ( {isDisconnecting ? (
<> <>
<Loader2 className="mr-2 h-4 w-4 animate-spin" /> <Spinner size="sm" className="mr-2" />
Disconnecting Disconnecting
</> </>
) : ( ) : (
@ -347,7 +348,7 @@ export const ConnectorEditView: FC<ConnectorEditViewProps> = ({
> >
{isSaving ? ( {isSaving ? (
<> <>
<Loader2 className="mr-2 h-4 w-4 animate-spin" /> <Spinner size="sm" className="mr-2" />
Saving Saving
</> </>
) : ( ) : (

View file

@ -1,9 +1,10 @@
"use client"; "use client";
import { ArrowLeft, Check, Info, Loader2 } from "lucide-react"; import { ArrowLeft, Check, Info } from "lucide-react";
import { useSearchParams } from "next/navigation"; import { useSearchParams } from "next/navigation";
import { type FC, useCallback, useEffect, useMemo, useRef, useState } from "react"; import { type FC, useCallback, useEffect, useMemo, useRef, useState } from "react";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Spinner } from "@/components/ui/spinner";
import type { SearchSourceConnector } from "@/contracts/types/connector.types"; import type { SearchSourceConnector } from "@/contracts/types/connector.types";
import { getConnectorTypeDisplay } from "@/lib/connectors/utils"; import { getConnectorTypeDisplay } from "@/lib/connectors/utils";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
@ -216,7 +217,7 @@ export const IndexingConfigurationView: FC<IndexingConfigurationViewProps> = ({
> >
{isStartingIndexing ? ( {isStartingIndexing ? (
<> <>
<Loader2 className="mr-2 h-4 w-4 animate-spin" /> <Spinner size="sm" className="mr-2" />
Starting... Starting...
</> </>
) : ( ) : (

View file

@ -1,11 +1,12 @@
"use client"; "use client";
import { ArrowRight, Cable, Loader2 } from "lucide-react"; import { ArrowRight, Cable } from "lucide-react";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import type { FC } from "react"; import type { FC } from "react";
import { useState } from "react"; import { useState } from "react";
import { getDocumentTypeLabel } from "@/app/dashboard/[search_space_id]/documents/(manage)/components/DocumentTypeIcon"; import { getDocumentTypeLabel } from "@/app/dashboard/[search_space_id]/documents/(manage)/components/DocumentTypeIcon";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Spinner } from "@/components/ui/spinner";
import { Switch } from "@/components/ui/switch"; import { Switch } from "@/components/ui/switch";
import { TabsContent } from "@/components/ui/tabs"; import { TabsContent } from "@/components/ui/tabs";
import { getConnectorIcon } from "@/contracts/enums/connectorIcons"; import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
@ -209,7 +210,7 @@ export const ActiveConnectorsTab: FC<ActiveConnectorsTabProps> = ({
<p className="text-[14px] font-semibold leading-tight truncate">{title}</p> <p className="text-[14px] font-semibold leading-tight truncate">{title}</p>
{isAnyIndexing ? ( {isAnyIndexing ? (
<p className="text-[11px] text-primary mt-1 flex items-center gap-1.5"> <p className="text-[11px] text-primary mt-1 flex items-center gap-1.5">
<Loader2 className="size-3 animate-spin" /> <Spinner size="xs" />
Syncing Syncing
</p> </p>
) : ( ) : (
@ -270,7 +271,7 @@ export const ActiveConnectorsTab: FC<ActiveConnectorsTabProps> = ({
</div> </div>
{isIndexing ? ( {isIndexing ? (
<p className="text-[11px] text-primary mt-1 flex items-center gap-1.5"> <p className="text-[11px] text-primary mt-1 flex items-center gap-1.5">
<Loader2 className="size-3 animate-spin" /> <Spinner size="xs" />
Syncing Syncing
</p> </p>
) : !isMCPConnector ? ( ) : !isMCPConnector ? (

View file

@ -1,9 +1,10 @@
"use client"; "use client";
import { differenceInDays, differenceInMinutes, format, isToday, isYesterday } from "date-fns"; import { differenceInDays, differenceInMinutes, format, isToday, isYesterday } from "date-fns";
import { ArrowLeft, Loader2, Plus, Server } from "lucide-react"; import { ArrowLeft, Plus, Server } from "lucide-react";
import type { FC } from "react"; import type { FC } from "react";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Spinner } from "@/components/ui/spinner";
import { EnumConnectorName } from "@/contracts/enums/connector"; import { EnumConnectorName } from "@/contracts/enums/connector";
import { getConnectorIcon } from "@/contracts/enums/connectorIcons"; import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
import type { SearchSourceConnector } from "@/contracts/types/connector.types"; import type { SearchSourceConnector } from "@/contracts/types/connector.types";
@ -143,7 +144,7 @@ export const ConnectorAccountsListView: FC<ConnectorAccountsListViewProps> = ({
> >
<div className="flex h-5 w-5 items-center justify-center rounded-md bg-primary/10 shrink-0"> <div className="flex h-5 w-5 items-center justify-center rounded-md bg-primary/10 shrink-0">
{isConnecting ? ( {isConnecting ? (
<Loader2 className="size-3 animate-spin text-primary" /> <Spinner size="xs" className="text-primary" />
) : ( ) : (
<Plus className="size-3 text-primary" /> <Plus className="size-3 text-primary" />
)} )}
@ -207,7 +208,7 @@ export const ConnectorAccountsListView: FC<ConnectorAccountsListViewProps> = ({
</p> </p>
{isIndexing ? ( {isIndexing ? (
<p className="text-[11px] text-primary mt-1 flex items-center gap-1.5"> <p className="text-[11px] text-primary mt-1 flex items-center gap-1.5">
<Loader2 className="size-3 animate-spin" /> <Spinner size="xs" />
Syncing Syncing
</p> </p>
) : ( ) : (

View file

@ -2,13 +2,14 @@
import { TagInput, type Tag as TagType } from "emblor"; import { TagInput, type Tag as TagType } from "emblor";
import { useAtom } from "jotai"; import { useAtom } from "jotai";
import { ArrowLeft, Loader2 } from "lucide-react"; import { ArrowLeft } from "lucide-react";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
import { type FC, useState } from "react"; import { type FC, useState } from "react";
import { toast } from "sonner"; import { toast } from "sonner";
import { createDocumentMutationAtom } from "@/atoms/documents/document-mutation.atoms"; import { createDocumentMutationAtom } from "@/atoms/documents/document-mutation.atoms";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Spinner } from "@/components/ui/spinner";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { EnumConnectorName } from "@/contracts/enums/connector"; import { EnumConnectorName } from "@/contracts/enums/connector";
import { getConnectorIcon } from "@/contracts/enums/connectorIcons"; import { getConnectorIcon } from "@/contracts/enums/connectorIcons";
@ -222,7 +223,7 @@ export const YouTubeCrawlerView: FC<YouTubeCrawlerViewProps> = ({ searchSpaceId,
> >
{isSubmitting ? ( {isSubmitting ? (
<> <>
<Loader2 className="mr-2 h-4 w-4 animate-spin" /> <Spinner size="sm" className="mr-2" />
{t("processing")} {t("processing")}
</> </>
) : ( ) : (

View file

@ -6,6 +6,7 @@ import {
DropdownMenu, DropdownMenu,
DropdownMenuContent, DropdownMenuContent,
DropdownMenuItem, DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger, DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"; } from "@/components/ui/dropdown-menu";
import type { CommentActionsProps } from "./types"; import type { CommentActionsProps } from "./types";
@ -23,7 +24,7 @@ export function CommentActions({ canEdit, canDelete, onEdit, onDelete }: Comment
size="icon" size="icon"
className="size-7 opacity-100 md:opacity-0 md:group-hover:opacity-100 transition-opacity" className="size-7 opacity-100 md:opacity-0 md:group-hover:opacity-100 transition-opacity"
> >
<MoreHorizontal className="size-4" /> <MoreHorizontal className="size-4 text-muted-foreground" />
</Button> </Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent align="end"> <DropdownMenuContent align="end">
@ -33,8 +34,9 @@ export function CommentActions({ canEdit, canDelete, onEdit, onDelete }: Comment
Edit Edit
</DropdownMenuItem> </DropdownMenuItem>
)} )}
{canEdit && canDelete && <DropdownMenuSeparator />}
{canDelete && ( {canDelete && (
<DropdownMenuItem onClick={onDelete} className="text-destructive"> <DropdownMenuItem onClick={onDelete} className="text-destructive focus:text-destructive">
<Trash2 className="mr-2 size-4" /> <Trash2 className="mr-2 size-4" />
Delete Delete
</DropdownMenuItem> </DropdownMenuItem>

View file

@ -1,7 +1,7 @@
"use client"; "use client";
import { Loader2 } from "lucide-react";
import { ScrollArea } from "@/components/ui/scroll-area"; import { ScrollArea } from "@/components/ui/scroll-area";
import { Spinner } from "@/components/ui/spinner";
import { MemberMentionItem } from "./member-mention-item"; import { MemberMentionItem } from "./member-mention-item";
import type { MemberMentionPickerProps } from "./types"; import type { MemberMentionPickerProps } from "./types";
@ -24,7 +24,7 @@ export function MemberMentionPicker({
if (isLoading) { if (isLoading) {
return ( return (
<div className="flex items-center justify-center py-6"> <div className="flex items-center justify-center py-6">
<Loader2 className="size-5 animate-spin text-muted-foreground" /> <Spinner size="md" className="text-muted-foreground" />
</div> </div>
); );
} }

View file

@ -10,12 +10,12 @@ import {
FolderOpen, FolderOpen,
HardDrive, HardDrive,
Image, Image,
Loader2,
Presentation, Presentation,
} from "lucide-react"; } from "lucide-react";
import { useState } from "react"; import { useState } from "react";
import { Checkbox } from "@/components/ui/checkbox"; import { Checkbox } from "@/components/ui/checkbox";
import { ScrollArea } from "@/components/ui/scroll-area"; import { ScrollArea } from "@/components/ui/scroll-area";
import { Spinner } from "@/components/ui/spinner";
import { useComposioDriveFolders } from "@/hooks/use-composio-drive-folders"; import { useComposioDriveFolders } from "@/hooks/use-composio-drive-folders";
import { connectorsApiService } from "@/lib/apis/connectors-api.service"; import { connectorsApiService } from "@/lib/apis/connectors-api.service";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
@ -253,7 +253,7 @@ export function ComposioDriveFolderTree({
aria-label={isExpanded ? `Collapse ${item.name}` : `Expand ${item.name}`} aria-label={isExpanded ? `Collapse ${item.name}` : `Expand ${item.name}`}
> >
{isLoading ? ( {isLoading ? (
<Loader2 className="h-2.5 w-2.5 sm:h-3 sm:w-3 animate-spin" /> <Spinner size="xs" className="h-2.5 w-2.5 sm:h-3 sm:w-3" />
) : isExpanded ? ( ) : isExpanded ? (
<ChevronDown className="h-3 w-3 sm:h-4 sm:w-4" /> <ChevronDown className="h-3 w-3 sm:h-4 sm:w-4" />
) : ( ) : (
@ -344,7 +344,7 @@ export function ComposioDriveFolderTree({
{isLoadingRoot && ( {isLoadingRoot && (
<div className="flex items-center justify-center py-4 sm:py-8"> <div className="flex items-center justify-center py-4 sm:py-8">
<Loader2 className="h-4 w-4 sm:h-6 sm:w-6 animate-spin text-muted-foreground" /> <Spinner size="sm" className="sm:h-6 sm:w-6 text-muted-foreground" />
</div> </div>
)} )}

View file

@ -10,12 +10,12 @@ import {
FolderOpen, FolderOpen,
HardDrive, HardDrive,
Image, Image,
Loader2,
Presentation, Presentation,
} from "lucide-react"; } from "lucide-react";
import { useState } from "react"; import { useState } from "react";
import { Checkbox } from "@/components/ui/checkbox"; import { Checkbox } from "@/components/ui/checkbox";
import { ScrollArea } from "@/components/ui/scroll-area"; import { ScrollArea } from "@/components/ui/scroll-area";
import { Spinner } from "@/components/ui/spinner";
import { useGoogleDriveFolders } from "@/hooks/use-google-drive-folders"; import { useGoogleDriveFolders } from "@/hooks/use-google-drive-folders";
import { connectorsApiService } from "@/lib/apis/connectors-api.service"; import { connectorsApiService } from "@/lib/apis/connectors-api.service";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
@ -253,7 +253,7 @@ export function GoogleDriveFolderTree({
aria-label={isExpanded ? `Collapse ${item.name}` : `Expand ${item.name}`} aria-label={isExpanded ? `Collapse ${item.name}` : `Expand ${item.name}`}
> >
{isLoading ? ( {isLoading ? (
<Loader2 className="h-2.5 w-2.5 sm:h-3 sm:w-3 animate-spin" /> <Spinner size="xs" className="h-2.5 w-2.5 sm:h-3 sm:w-3" />
) : isExpanded ? ( ) : isExpanded ? (
<ChevronDown className="h-3 w-3 sm:h-4 sm:w-4" /> <ChevronDown className="h-3 w-3 sm:h-4 sm:w-4" />
) : ( ) : (
@ -344,7 +344,7 @@ export function GoogleDriveFolderTree({
{isLoadingRoot && ( {isLoadingRoot && (
<div className="flex items-center justify-center py-4 sm:py-8"> <div className="flex items-center justify-center py-4 sm:py-8">
<Loader2 className="h-4 w-4 sm:h-6 sm:w-6 animate-spin text-muted-foreground" /> <Spinner size="sm" className="sm:h-6 sm:w-6 text-muted-foreground" />
</div> </div>
)} )}

View file

@ -2,7 +2,7 @@
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { useAtomValue } from "jotai"; import { useAtomValue } from "jotai";
import { Loader2, Plus, Search } from "lucide-react"; import { Plus, Search } from "lucide-react";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
import { useState } from "react"; import { useState } from "react";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
@ -26,6 +26,7 @@ import {
FormMessage, FormMessage,
} from "@/components/ui/form"; } from "@/components/ui/form";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Spinner } from "@/components/ui/spinner";
import { trackSearchSpaceCreated } from "@/lib/posthog/events"; import { trackSearchSpaceCreated } from "@/lib/posthog/events";
const formSchema = z.object({ const formSchema = z.object({
@ -145,7 +146,7 @@ export function CreateSearchSpaceDialog({ open, onOpenChange }: CreateSearchSpac
<Button type="submit" disabled={isSubmitting} className="w-full sm:w-auto h-9 sm:h-10 text-sm"> <Button type="submit" disabled={isSubmitting} className="w-full sm:w-auto h-9 sm:h-10 text-sm">
{isSubmitting ? ( {isSubmitting ? (
<> <>
<Loader2 className="mr-1.5 h-4 w-4 animate-spin" /> <Spinner size="sm" className="mr-1.5" />
{t("creating")} {t("creating")}
</> </>
) : ( ) : (

View file

@ -4,7 +4,6 @@ import { useQuery, useQueryClient } from "@tanstack/react-query";
import { format } from "date-fns"; import { format } from "date-fns";
import { import {
ArchiveIcon, ArchiveIcon,
Loader2,
MessageCircleMore, MessageCircleMore,
MoreHorizontal, MoreHorizontal,
RotateCcwIcon, RotateCcwIcon,
@ -30,6 +29,7 @@ import {
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { Spinner } from "@/components/ui/spinner";
import { useDebouncedValue } from "@/hooks/use-debounced-value"; import { useDebouncedValue } from "@/hooks/use-debounced-value";
import { import {
deleteThread, deleteThread,
@ -304,7 +304,7 @@ export function AllPrivateChatsSidebar({
<div className="flex-1 overflow-y-auto overflow-x-hidden p-2"> <div className="flex-1 overflow-y-auto overflow-x-hidden p-2">
{isLoading ? ( {isLoading ? (
<div className="flex items-center justify-center py-8"> <div className="flex items-center justify-center py-8">
<Loader2 className="h-6 w-6 animate-spin text-muted-foreground" /> <Spinner size="md" className="text-muted-foreground" />
</div> </div>
) : error ? ( ) : error ? (
<div className="text-center py-8 text-sm text-destructive"> <div className="text-center py-8 text-sm text-destructive">
@ -365,7 +365,7 @@ export function AllPrivateChatsSidebar({
disabled={isBusy} disabled={isBusy}
> >
{isDeleting ? ( {isDeleting ? (
<Loader2 className="h-3.5 w-3.5 animate-spin" /> <Spinner size="xs" />
) : ( ) : (
<MoreHorizontal className="h-3.5 w-3.5 text-muted-foreground" /> <MoreHorizontal className="h-3.5 w-3.5 text-muted-foreground" />
)} )}

View file

@ -4,7 +4,6 @@ import { useQuery, useQueryClient } from "@tanstack/react-query";
import { format } from "date-fns"; import { format } from "date-fns";
import { import {
ArchiveIcon, ArchiveIcon,
Loader2,
MessageCircleMore, MessageCircleMore,
MoreHorizontal, MoreHorizontal,
RotateCcwIcon, RotateCcwIcon,
@ -30,6 +29,7 @@ import {
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { Spinner } from "@/components/ui/spinner";
import { useDebouncedValue } from "@/hooks/use-debounced-value"; import { useDebouncedValue } from "@/hooks/use-debounced-value";
import { import {
deleteThread, deleteThread,
@ -304,7 +304,7 @@ export function AllSharedChatsSidebar({
<div className="flex-1 overflow-y-auto overflow-x-hidden p-2"> <div className="flex-1 overflow-y-auto overflow-x-hidden p-2">
{isLoading ? ( {isLoading ? (
<div className="flex items-center justify-center py-8"> <div className="flex items-center justify-center py-8">
<Loader2 className="h-6 w-6 animate-spin text-muted-foreground" /> <Spinner size="md" className="text-muted-foreground" />
</div> </div>
) : error ? ( ) : error ? (
<div className="text-center py-8 text-sm text-destructive"> <div className="text-center py-8 text-sm text-destructive">
@ -365,7 +365,7 @@ export function AllSharedChatsSidebar({
disabled={isBusy} disabled={isBusy}
> >
{isDeleting ? ( {isDeleting ? (
<Loader2 className="h-3.5 w-3.5 animate-spin" /> <Spinner size="xs" />
) : ( ) : (
<MoreHorizontal className="h-3.5 w-3.5 text-muted-foreground" /> <MoreHorizontal className="h-3.5 w-3.5 text-muted-foreground" />
)} )}

View file

@ -1,6 +1,5 @@
"use client"; "use client";
import { Check } from "lucide-react";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import type { NavItem } from "../../types/layout.types"; import type { NavItem } from "../../types/layout.types";
@ -40,9 +39,6 @@ export function NavSection({ items, onItemClick, isCollapsed = false }: NavSecti
{...joyrideAttr} {...joyrideAttr}
> >
<Icon className="h-4 w-4" /> <Icon className="h-4 w-4" />
{item.isActive && (
<Check className="absolute bottom-0.5 right-0.5 h-3 w-3 text-primary" />
)}
{item.badge && ( {item.badge && (
<span className="absolute top-0.5 right-0.5 inline-flex items-center justify-center min-w-[14px] h-[14px] px-0.5 rounded-full bg-red-500 text-white text-[9px] font-medium"> <span className="absolute top-0.5 right-0.5 inline-flex items-center justify-center min-w-[14px] h-[14px] px-0.5 rounded-full bg-red-500 text-white text-[9px] font-medium">
{item.badge} {item.badge}
@ -73,9 +69,6 @@ export function NavSection({ items, onItemClick, isCollapsed = false }: NavSecti
> >
<Icon className="h-4 w-4 shrink-0" /> <Icon className="h-4 w-4 shrink-0" />
<span className="flex-1 truncate">{item.title}</span> <span className="flex-1 truncate">{item.title}</span>
{item.isActive && (
<Check className="h-4 w-4 shrink-0 text-primary" />
)}
{item.badge && ( {item.badge && (
<span className="inline-flex items-center justify-center min-w-4 h-4 px-1 rounded-full bg-red-500 text-white text-[10px] font-medium"> <span className="inline-flex items-center justify-center min-w-4 h-4 px-1 rounded-full bg-red-500 text-white text-[10px] font-medium">
{item.badge} {item.badge}

View file

@ -8,7 +8,6 @@ import {
Cloud, Cloud,
Edit3, Edit3,
Globe, Globe,
Loader2,
Plus, Plus,
Settings2, Settings2,
Sparkles, Sparkles,
@ -36,6 +35,7 @@ import {
CommandSeparator, CommandSeparator,
} from "@/components/ui/command"; } from "@/components/ui/command";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { Spinner } from "@/components/ui/spinner";
import type { import type {
GlobalNewLLMConfig, GlobalNewLLMConfig,
NewLLMConfigPublic, NewLLMConfigPublic,
@ -179,7 +179,7 @@ export function ModelSelector({ onEdit, onAddNew, className }: ModelSelectorProp
> >
{isLoading ? ( {isLoading ? (
<> <>
<Loader2 className="h-4 w-4 animate-spin text-muted-foreground" /> <Spinner size="sm" className="text-muted-foreground" />
<span className="text-muted-foreground hidden md:inline">Loading</span> <span className="text-muted-foreground hidden md:inline">Loading</span>
</> </>
) : currentConfig ? ( ) : currentConfig ? (

View file

@ -6,7 +6,6 @@ import {
Bot, Bot,
CheckCircle, CheckCircle,
FileText, FileText,
Loader2,
RefreshCw, RefreshCw,
RotateCcw, RotateCcw,
Save, Save,
@ -32,6 +31,7 @@ import {
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
} from "@/components/ui/select"; } from "@/components/ui/select";
import { Spinner } from "@/components/ui/spinner";
const ROLE_DESCRIPTIONS = { const ROLE_DESCRIPTIONS = {
agent: { agent: {
@ -206,7 +206,7 @@ export function LLMRoleManager({ searchSpaceId }: LLMRoleManagerProps) {
<Card> <Card>
<CardContent className="flex items-center justify-center py-8 md:py-12"> <CardContent className="flex items-center justify-center py-8 md:py-12">
<div className="flex items-center gap-2 text-muted-foreground"> <div className="flex items-center gap-2 text-muted-foreground">
<Loader2 className="w-4 h-4 md:w-5 md:h-5 animate-spin" /> <Spinner size="sm" className="md:h-5 md:w-5" />
<span className="text-xs md:text-sm"> <span className="text-xs md:text-sm">
{configsLoading && preferencesLoading {configsLoading && preferencesLoading
? "Loading configurations and preferences..." ? "Loading configurations and preferences..."

View file

@ -7,7 +7,6 @@ import {
Clock, Clock,
Edit3, Edit3,
FileText, FileText,
Loader2,
MessageSquareQuote, MessageSquareQuote,
Plus, Plus,
RefreshCw, RefreshCw,
@ -49,6 +48,7 @@ import {
DialogTitle, DialogTitle,
} from "@/components/ui/dialog"; } from "@/components/ui/dialog";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
import { Spinner } from "@/components/ui/spinner";
import type { NewLLMConfig } from "@/contracts/types/new-llm-config.types"; import type { NewLLMConfig } from "@/contracts/types/new-llm-config.types";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
@ -211,7 +211,7 @@ export function ModelConfigManager({ searchSpaceId }: ModelConfigManagerProps) {
<Card> <Card>
<CardContent className="flex items-center justify-center py-10 md:py-16"> <CardContent className="flex items-center justify-center py-10 md:py-16">
<div className="flex flex-col items-center gap-2 md:gap-3"> <div className="flex flex-col items-center gap-2 md:gap-3">
<Loader2 className="h-6 w-6 md:h-8 md:w-8 animate-spin text-muted-foreground" /> <Spinner size="md" className="md:h-8 md:w-8 text-muted-foreground" />
<span className="text-xs md:text-sm text-muted-foreground"> <span className="text-xs md:text-sm text-muted-foreground">
Loading configurations... Loading configurations...
</span> </span>
@ -484,7 +484,7 @@ export function ModelConfigManager({ searchSpaceId }: ModelConfigManagerProps) {
> >
{isDeleting ? ( {isDeleting ? (
<> <>
<Loader2 className="mr-2 h-4 w-4 animate-spin" /> <Spinner size="sm" className="mr-2" />
Deleting Deleting
</> </>
) : ( ) : (

View file

@ -8,7 +8,6 @@ import {
ChevronDown, ChevronDown,
ChevronsUpDown, ChevronsUpDown,
Key, Key,
Loader2,
MessageSquareQuote, MessageSquareQuote,
Rocket, Rocket,
Sparkles, Sparkles,
@ -48,6 +47,7 @@ import {
SelectValue, SelectValue,
} from "@/components/ui/select"; } from "@/components/ui/select";
import { Separator } from "@/components/ui/separator"; import { Separator } from "@/components/ui/separator";
import { Spinner } from "@/components/ui/spinner";
import { Switch } from "@/components/ui/switch"; import { Switch } from "@/components/ui/switch";
import { Textarea } from "@/components/ui/textarea"; import { Textarea } from "@/components/ui/textarea";
import { getModelsByProvider } from "@/contracts/enums/llm-models"; import { getModelsByProvider } from "@/contracts/enums/llm-models";
@ -592,7 +592,7 @@ export function LLMConfigForm({
> >
{isSubmitting ? ( {isSubmitting ? (
<> <>
<Loader2 className="h-3.5 w-3.5 sm:h-4 sm:w-4 animate-spin" /> <Spinner size="sm" />
{mode === "edit" ? "Updating..." : "Creating"} {mode === "edit" ? "Updating..." : "Creating"}
</> </>
) : ( ) : (

View file

@ -1,13 +1,14 @@
"use client"; "use client";
import { useAtom } from "jotai"; import { useAtom } from "jotai";
import { CheckCircle2, FileType, Info, Loader2, Tag, Upload, X } from "lucide-react"; import { CheckCircle2, FileType, Info, Tag, Upload, X } from "lucide-react";
import { AnimatePresence, motion } from "motion/react"; import { AnimatePresence, motion } from "motion/react";
import { useTranslations } from "next-intl"; import { useTranslations } from "next-intl";
import { useCallback, useMemo, useRef, useState } from "react"; import { useCallback, useMemo, useRef, useState } from "react";
import { useDropzone } from "react-dropzone"; import { useDropzone } from "react-dropzone";
import { toast } from "sonner"; import { toast } from "sonner";
import { uploadDocumentMutationAtom } from "@/atoms/documents/document-mutation.atoms"; import { uploadDocumentMutationAtom } from "@/atoms/documents/document-mutation.atoms";
import { Spinner } from "@/components/ui/spinner";
import { import {
Accordion, Accordion,
AccordionContent, AccordionContent,
@ -424,7 +425,7 @@ export function DocumentUploadTab({
> >
{isUploading ? ( {isUploading ? (
<span className="flex items-center gap-2"> <span className="flex items-center gap-2">
<Loader2 className="h-4 w-4 sm:h-5 sm:w-5 animate-spin" /> <Spinner size="sm" />
{t("uploading")} {t("uploading")}
</span> </span>
) : ( ) : (

View file

@ -1,10 +1,11 @@
"use client"; "use client";
import { makeAssistantToolUI } from "@assistant-ui/react"; import { makeAssistantToolUI } from "@assistant-ui/react";
import { AlertCircleIcon, Loader2Icon, MicIcon } from "lucide-react"; import { AlertCircleIcon, MicIcon } from "lucide-react";
import { useCallback, useEffect, useRef, useState } from "react"; import { useCallback, useEffect, useRef, useState } from "react";
import { z } from "zod"; import { z } from "zod";
import { Audio } from "@/components/tool-ui/audio"; import { Audio } from "@/components/tool-ui/audio";
import { Spinner } from "@/components/ui/spinner";
import { baseApiService } from "@/lib/apis/base-api.service"; import { baseApiService } from "@/lib/apis/base-api.service";
import { authenticatedFetch } from "@/lib/auth-utils"; import { authenticatedFetch } from "@/lib/auth-utils";
import { clearActivePodcastTaskId, setActivePodcastTaskId } from "@/lib/chat/podcast-state"; import { clearActivePodcastTaskId, setActivePodcastTaskId } from "@/lib/chat/podcast-state";
@ -97,8 +98,8 @@ function PodcastGeneratingState({ title }: { title: string }) {
<div className="flex-1"> <div className="flex-1">
<h3 className="font-semibold text-foreground text-lg">{title}</h3> <h3 className="font-semibold text-foreground text-lg">{title}</h3>
<div className="mt-2 flex items-center gap-2 text-muted-foreground"> <div className="mt-2 flex items-center gap-2 text-muted-foreground">
<Loader2Icon className="size-4 animate-spin" /> <Spinner size="sm" />
<span className="text-sm">Generating podcast. This may take a few minutes</span> <span className="text-sm">Generating podcast. This may take a few minutes.</span>
</div> </div>
<div className="mt-3"> <div className="mt-3">
<div className="h-1.5 w-full overflow-hidden rounded-full bg-primary/10"> <div className="h-1.5 w-full overflow-hidden rounded-full bg-primary/10">
@ -144,7 +145,7 @@ function AudioLoadingState({ title }: { title: string }) {
<div className="flex-1"> <div className="flex-1">
<h3 className="font-semibold text-foreground">{title}</h3> <h3 className="font-semibold text-foreground">{title}</h3>
<div className="mt-2 flex items-center gap-2 text-muted-foreground"> <div className="mt-2 flex items-center gap-2 text-muted-foreground">
<Loader2Icon className="size-4 animate-spin" /> <Spinner size="sm" />
<span className="text-sm">Loading audio...</span> <span className="text-sm">Loading audio...</span>
</div> </div>
</div> </div>

View file

@ -1,11 +1,12 @@
"use client"; "use client";
import { ExternalLinkIcon, ImageIcon, Loader2 } from "lucide-react"; import { ExternalLinkIcon, ImageIcon } from "lucide-react";
import NextImage from "next/image"; import NextImage from "next/image";
import { Component, type ReactNode, useState } from "react"; import { Component, type ReactNode, useState } from "react";
import { z } from "zod"; import { z } from "zod";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { Card } from "@/components/ui/card"; import { Card } from "@/components/ui/card";
import { Spinner } from "@/components/ui/spinner";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
/** /**
@ -184,7 +185,7 @@ export function ImageLoading({ title = "Loading image..." }: { title?: string })
<Card className="w-full max-w-md overflow-hidden"> <Card className="w-full max-w-md overflow-hidden">
<div className="aspect-[4/3] bg-muted flex items-center justify-center"> <div className="aspect-[4/3] bg-muted flex items-center justify-center">
<div className="flex flex-col items-center gap-3"> <div className="flex flex-col items-center gap-3">
<Loader2 className="size-8 text-muted-foreground animate-spin" /> <Spinner size="lg" className="text-muted-foreground" />
<p className="text-muted-foreground text-sm">{title}</p> <p className="text-muted-foreground text-sm">{title}</p>
</div> </div>
</div> </div>

View file

@ -1,12 +1,13 @@
"use client"; "use client";
import { ExternalLinkIcon, Globe, ImageIcon, LinkIcon, Loader2 } from "lucide-react"; import { ExternalLinkIcon, Globe, ImageIcon, LinkIcon } from "lucide-react";
import Image from "next/image"; import Image from "next/image";
import { Component, type ReactNode } from "react"; import { Component, type ReactNode } from "react";
import { z } from "zod"; import { z } from "zod";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card"; import { Card, CardContent } from "@/components/ui/card";
import { Spinner } from "@/components/ui/spinner";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
@ -299,18 +300,17 @@ export function MediaCard({
{/* Response Actions */} {/* Response Actions */}
{responseActions && responseActions.length > 0 && ( {responseActions && responseActions.length > 0 && (
<div <div className="mt-4 flex items-center justify-end gap-2 border-t pt-3">
className="mt-4 flex items-center justify-end gap-2 border-t pt-3"
onClick={(e) => e.stopPropagation()}
onKeyDown={(e) => e.stopPropagation()}
>
{responseActions.map((action) => ( {responseActions.map((action) => (
<Tooltip key={action.id}> <Tooltip key={action.id}>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<Button <Button
variant={action.variant || "secondary"} variant={action.variant || "secondary"}
size="sm" size="sm"
onClick={() => onResponseAction?.(action.id)} onClick={(e) => {
e.stopPropagation();
onResponseAction?.(action.id);
}}
> >
{action.label} {action.label}
</Button> </Button>
@ -337,7 +337,7 @@ export function MediaCardLoading({ title = "Loading preview..." }: { title?: str
return ( return (
<Card className="w-full max-w-md overflow-hidden"> <Card className="w-full max-w-md overflow-hidden">
<div className="aspect-[2/1] bg-muted animate-pulse flex items-center justify-center"> <div className="aspect-[2/1] bg-muted animate-pulse flex items-center justify-center">
<Loader2 className="size-8 text-muted-foreground animate-spin" /> <Spinner size="lg" className="text-muted-foreground" />
</div> </div>
<CardContent className="p-4"> <CardContent className="p-4">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">

View file

@ -2,7 +2,6 @@
import { makeAssistantToolUI, useAssistantState } from "@assistant-ui/react"; import { makeAssistantToolUI, useAssistantState } from "@assistant-ui/react";
import { useAtomValue, useSetAtom } from "jotai"; import { useAtomValue, useSetAtom } from "jotai";
import { Loader2 } from "lucide-react";
import { useEffect, useMemo } from "react"; import { useEffect, useMemo } from "react";
import { z } from "zod"; import { z } from "zod";
import { import {
@ -11,6 +10,7 @@ import {
registerPlanOwner, registerPlanOwner,
updatePlanStateAtom, updatePlanStateAtom,
} from "@/atoms/chat/plan-state.atom"; } from "@/atoms/chat/plan-state.atom";
import { Spinner } from "@/components/ui/spinner";
import { Plan, PlanErrorBoundary, parseSerializablePlan, TodoStatusSchema } from "./plan"; import { Plan, PlanErrorBoundary, parseSerializablePlan, TodoStatusSchema } from "./plan";
// ============================================================================ // ============================================================================
@ -46,7 +46,7 @@ function WriteTodosLoading() {
return ( return (
<div className="my-4 w-full max-w-xl rounded-2xl border bg-card/60 px-5 py-4 shadow-sm"> <div className="my-4 w-full max-w-xl rounded-2xl border bg-card/60 px-5 py-4 shadow-sm">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<Loader2 className="size-5 animate-spin text-primary" /> <Spinner size="md" className="text-primary" />
<span className="text-sm text-muted-foreground">Creating plan...</span> <span className="text-sm text-muted-foreground">Creating plan...</span>
</div> </div>
</div> </div>

View file

@ -321,10 +321,10 @@
"columns": "Columns", "columns": "Columns",
"confirm_delete": "Confirm Delete", "confirm_delete": "Confirm Delete",
"confirm_delete_desc": "Are you sure you want to delete {count} document(s)? This action cannot be undone.", "confirm_delete_desc": "Are you sure you want to delete {count} document(s)? This action cannot be undone.",
"uploading": "Uploading...", "uploading": "Uploading",
"upload_success": "Document uploaded successfully", "upload_success": "Document uploaded successfully",
"upload_failed": "Failed to upload document", "upload_failed": "Failed to upload document",
"loading": "Loading documents...", "loading": "Loading documents",
"error_loading": "Error loading documents", "error_loading": "Error loading documents",
"retry": "Retry", "retry": "Retry",
"no_documents": "No documents found", "no_documents": "No documents found",
@ -391,8 +391,8 @@
"selected_files": "Selected Files ({count})", "selected_files": "Selected Files ({count})",
"total_size": "Total size", "total_size": "Total size",
"clear_all": "Clear all", "clear_all": "Clear all",
"uploading_files": "Uploading files...", "uploading_files": "Uploading files",
"uploading": "Uploading...", "uploading": "Uploading",
"upload_button": "Upload {count} {count, plural, one {file} other {files}}", "upload_button": "Upload {count} {count, plural, one {file} other {files}}",
"upload_initiated": "Upload Task Initiated", "upload_initiated": "Upload Task Initiated",
"upload_initiated_desc": "Files Uploading Initiated", "upload_initiated_desc": "Files Uploading Initiated",