From 7aa065b363b29028b9c5d0515d886044d0a67a5b Mon Sep 17 00:00:00 2001 From: shimmyshimmer Date: Mon, 15 Jun 2026 03:32:24 -0700 Subject: [PATCH] Studio: consolidate loading spinners onto the shared Spinner Replace the duplicate ToolCallSpinner and the remaining inline loading spinners (hugeicons Loading03, lucide Loader2/Loader/LoaderCircle, and the hand-rolled border rings) with the shared Spinner component, then delete tool-call-spinner.tsx. Refresh and regenerate action icons and the shimmer button are left alone since they are not loading spinners. --- .../src/components/assistant-ui/image.tsx | 4 ++-- .../assistant-ui/tool-call-spinner.tsx | 17 ----------------- .../components/assistant-ui/tool-fallback.tsx | 4 ++-- .../src/components/assistant-ui/tool-group.tsx | 4 ++-- .../assistant-ui/tool-ui-code-execution.tsx | 4 ++-- .../assistant-ui/tool-ui-knowledge-base.tsx | 5 +++-- .../components/assistant-ui/tool-ui-python.tsx | 4 ++-- .../assistant-ui/tool-ui-terminal.tsx | 4 ++-- .../src/components/tauri/startup-screen.tsx | 18 +++++------------- .../src/components/tauri/update-screen.tsx | 12 ++---------- .../rag/components/document-preview-sheet.tsx | 6 +++--- .../rag/components/document-status-chip.tsx | 9 +++------ .../components/recipe-graph-node.tsx | 6 ++---- .../dialogs/seed/unstructured-drop-zone.tsx | 7 ++----- .../dataset-preview-dialog-mapping.tsx | 5 +++-- 15 files changed, 35 insertions(+), 74 deletions(-) delete mode 100644 studio/frontend/src/components/assistant-ui/tool-call-spinner.tsx diff --git a/studio/frontend/src/components/assistant-ui/image.tsx b/studio/frontend/src/components/assistant-ui/image.tsx index 0850a8230a..f10c1bf024 100644 --- a/studio/frontend/src/components/assistant-ui/image.tsx +++ b/studio/frontend/src/components/assistant-ui/image.tsx @@ -17,10 +17,10 @@ import { CopyIcon, ImageIcon, ImageOffIcon, - Loader2Icon, RefreshCwIcon, ShieldAlertIcon, } from "lucide-react"; +import { Spinner } from "@/components/ui/spinner"; import { Download01Icon } from "@hugeicons/core-free-icons"; import { HugeiconsIcon } from "@hugeicons/react"; import { @@ -348,7 +348,7 @@ function ImageGenerating({ className }: { className?: string }) { className, )} > - + Generating image… ); diff --git a/studio/frontend/src/components/assistant-ui/tool-call-spinner.tsx b/studio/frontend/src/components/assistant-ui/tool-call-spinner.tsx deleted file mode 100644 index 79789692cb..0000000000 --- a/studio/frontend/src/components/assistant-ui/tool-call-spinner.tsx +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0-only -// Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0 - -"use client"; - -import { Loader2Icon } from "lucide-react"; -import { cn } from "@/lib/utils"; - -/** Spinner shown while a tool call runs. Inherits text color to match its surroundings. */ -export function ToolCallSpinner({ className }: { className?: string }) { - return ( - - ); -} diff --git a/studio/frontend/src/components/assistant-ui/tool-fallback.tsx b/studio/frontend/src/components/assistant-ui/tool-fallback.tsx index 8930b6386f..965ad56bfa 100644 --- a/studio/frontend/src/components/assistant-ui/tool-fallback.tsx +++ b/studio/frontend/src/components/assistant-ui/tool-fallback.tsx @@ -8,7 +8,7 @@ import { CollapsibleContent, CollapsibleTrigger, } from "@/components/ui/collapsible"; -import { ToolCallSpinner } from "@/components/assistant-ui/tool-call-spinner"; +import { Spinner } from "@/components/ui/spinner"; import { useCollapseScrollLock } from "@/hooks/use-collapse-scroll-lock"; import { cn } from "@/lib/utils"; import { @@ -142,7 +142,7 @@ function ToolFallbackTrigger({ {...props} > {isRunning ? ( - + ) : ToolIcon ? ( {active ? ( - + ) : ( {isRunning ? (
- + {runningLabel}
) : resultText ? ( diff --git a/studio/frontend/src/components/assistant-ui/tool-ui-knowledge-base.tsx b/studio/frontend/src/components/assistant-ui/tool-ui-knowledge-base.tsx index 09d6af2d64..ec24060072 100644 --- a/studio/frontend/src/components/assistant-ui/tool-ui-knowledge-base.tsx +++ b/studio/frontend/src/components/assistant-ui/tool-ui-knowledge-base.tsx @@ -7,7 +7,8 @@ import { type ToolCallMessagePartComponent, useAuiState, } from "@assistant-ui/react"; -import { FileTextIcon, LibraryBigIcon, LoaderIcon } from "lucide-react"; +import { FileTextIcon, LibraryBigIcon } from "lucide-react"; +import { Spinner } from "@/components/ui/spinner"; import { memo, useEffect, useMemo, useState } from "react"; import { Badge } from "./badge"; import { @@ -106,7 +107,7 @@ const KnowledgeBaseToolUIImpl: ToolCallMessagePartComponent = ({ {isRunning ? (
- + {query ? ( <>Searching documents for “{query}”… diff --git a/studio/frontend/src/components/assistant-ui/tool-ui-python.tsx b/studio/frontend/src/components/assistant-ui/tool-ui-python.tsx index 1231ca4a7f..d7b0e58f18 100644 --- a/studio/frontend/src/components/assistant-ui/tool-ui-python.tsx +++ b/studio/frontend/src/components/assistant-ui/tool-ui-python.tsx @@ -8,7 +8,7 @@ import { getAuthToken } from "@/features/auth/session"; import type { ToolCallMessagePartComponent } from "@assistant-ui/react"; import { code as codePlugin } from "@streamdown/code"; import { CheckIcon, CodeIcon, CopyIcon } from "lucide-react"; -import { ToolCallSpinner } from "@/components/assistant-ui/tool-call-spinner"; +import { Spinner } from "@/components/ui/spinner"; import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react"; import { Streamdown } from "streamdown"; import { @@ -149,7 +149,7 @@ const PythonToolUIImpl: ToolCallMessagePartComponent = ({ {/* Output */} {isRunning ? (
- + Running…
) : output ? ( diff --git a/studio/frontend/src/components/assistant-ui/tool-ui-terminal.tsx b/studio/frontend/src/components/assistant-ui/tool-ui-terminal.tsx index 40cb28d4e4..3c12b16e6b 100644 --- a/studio/frontend/src/components/assistant-ui/tool-ui-terminal.tsx +++ b/studio/frontend/src/components/assistant-ui/tool-ui-terminal.tsx @@ -6,7 +6,7 @@ import { copyToClipboard } from "@/lib/copy-to-clipboard"; import type { ToolCallMessagePartComponent } from "@assistant-ui/react"; import { CheckIcon, CopyIcon, TerminalIcon } from "lucide-react"; -import { ToolCallSpinner } from "@/components/assistant-ui/tool-call-spinner"; +import { Spinner } from "@/components/ui/spinner"; import { memo, useCallback, useEffect, useRef, useState } from "react"; import { ToolFallbackContent, @@ -87,7 +87,7 @@ const TerminalToolUIImpl: ToolCallMessagePartComponent = ({
{isRunning ? (
- + Running…
) : output ? ( diff --git a/studio/frontend/src/components/tauri/startup-screen.tsx b/studio/frontend/src/components/tauri/startup-screen.tsx index 8e373c82a0..fd67a8a841 100644 --- a/studio/frontend/src/components/tauri/startup-screen.tsx +++ b/studio/frontend/src/components/tauri/startup-screen.tsx @@ -2,6 +2,7 @@ // Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0 import { ShimmerButton } from "@/components/ui/shimmer-button"; +import { Spinner } from "@/components/ui/spinner"; import type { BackendStatus } from "@/hooks/use-tauri-backend"; import type { CopySupportDiagnosticsResult } from "@/lib/tauri-diagnostics"; import { AnimatePresence, motion } from "motion/react"; @@ -98,15 +99,6 @@ const EASE_OUT_QUART: [number, number, number, number] = [0.165, 0.84, 0.44, 1]; // Sub-components // --------------------------------------------------------------------------- -function TealSpinner({ size = 24 }: { size?: number }) { - return ( - - ); -} - function Logo() { return (
@@ -148,7 +140,7 @@ function CheckingContent() {
- +

Checking...

@@ -194,7 +186,7 @@ function InstallingContent({
- +

Installing...

Please wait a few mins, then you can start training. @@ -227,7 +219,7 @@ function RepairingContent({

- +

Updating existing Unsloth install...

{latest && (

{latest}

@@ -325,7 +317,7 @@ function StartingContent() {
- +

Starting server...

diff --git a/studio/frontend/src/components/tauri/update-screen.tsx b/studio/frontend/src/components/tauri/update-screen.tsx index 53e39612c0..aa540b7813 100644 --- a/studio/frontend/src/components/tauri/update-screen.tsx +++ b/studio/frontend/src/components/tauri/update-screen.tsx @@ -4,6 +4,7 @@ import type { UpdateStatus } from "@/hooks/use-tauri-update"; import type { CopySupportDiagnosticsResult } from "@/lib/tauri-diagnostics"; import { AnimatePresence, motion } from "motion/react"; +import { Spinner } from "@/components/ui/spinner"; import { useEffect, useRef, useState } from "react"; interface UpdateScreenProps { @@ -18,15 +19,6 @@ interface UpdateScreenProps { const EASE_OUT_QUART: [number, number, number, number] = [0.165, 0.84, 0.44, 1]; -function Spinner({ size = 24 }: { size?: number }) { - return ( - - ); -} - function Logo() { return (
@@ -135,7 +127,7 @@ export function UpdateScreen({
- {!isError && } + {!isError && }

{statusLabel(status)}

diff --git a/studio/frontend/src/features/rag/components/document-preview-sheet.tsx b/studio/frontend/src/features/rag/components/document-preview-sheet.tsx index fb65914ac6..d7f6842d83 100644 --- a/studio/frontend/src/features/rag/components/document-preview-sheet.tsx +++ b/studio/frontend/src/features/rag/components/document-preview-sheet.tsx @@ -9,10 +9,10 @@ import { ChevronLeftIcon, ChevronRightIcon, FileTextIcon, - LoaderIcon, ZoomInIcon, ZoomOutIcon, } from "lucide-react"; +import { Spinner } from "@/components/ui/spinner"; import { Sheet, @@ -209,7 +209,7 @@ function PdfPreview({ onLoadError={(e) => setError(e.message)} loading={
- Loading PDF… + Loading PDF…
} > @@ -444,7 +444,7 @@ export function DocumentPreviewSheet() {
{loading ? (
- Resolving source… + Resolving source…
) : error ? (
diff --git a/studio/frontend/src/features/rag/components/document-status-chip.tsx b/studio/frontend/src/features/rag/components/document-status-chip.tsx index 0f2d5341c9..839541871a 100644 --- a/studio/frontend/src/features/rag/components/document-status-chip.tsx +++ b/studio/frontend/src/features/rag/components/document-status-chip.tsx @@ -1,7 +1,8 @@ // SPDX-License-Identifier: AGPL-3.0-only // Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0 -import { LoaderCircleIcon, XIcon } from "lucide-react"; +import { XIcon } from "lucide-react"; +import { Spinner } from "@/components/ui/spinner"; import { HugeiconsIcon } from "@hugeicons/react"; import { File02Icon } from "@hugeicons/core-free-icons"; import { Badge } from "@/components/assistant-ui/badge"; @@ -40,11 +41,7 @@ export function DocumentStatusChip({ {filename} {/* spinner while indexing, else close button */} {processing ? ( - + ) : onRemove ? (