diff --git a/studio/frontend/src/components/app-sidebar.tsx b/studio/frontend/src/components/app-sidebar.tsx index 8087b30b91..66accdce3a 100644 --- a/studio/frontend/src/components/app-sidebar.tsx +++ b/studio/frontend/src/components/app-sidebar.tsx @@ -668,15 +668,17 @@ export function AppSidebar() { : "sidebar-row-action group-hover/recent-item:opacity-100 group-hover/recent-item:pointer-events-auto focus-visible:opacity-100 focus-visible:pointer-events-auto"; const buttonClass = cn( "sidebar-nav-btn h-[33px] cursor-pointer rounded-full pr-4 text-[14.5px] leading-[19px] tracking-nav font-medium", - // pl-3 (12px) plus the content's pl-1 (4px) lines the title up with the - // Recents label text at 16px. + // pl-3 (12px) over the content's pl-1.5 (6px) = 18px, aligning the + // title with the nav items above. variant === "project" ? "pl-[39px]" : "pl-3", variant === "project" - ? "group-hover/project-chat-item:pr-8 group-has-[.sidebar-row-action[data-state=open]]/project-chat-item:pr-8" + ? "group-hover/project-chat-item:pr-6 group-has-[.sidebar-row-action[data-state=open]]/project-chat-item:pr-6" : isPinned - ? // Pinned rows show an extra unpin button on hover, so reserve more room. + ? // Pinned rows show an extra unpin button on hover, so reserve more room + // (pr-8 when the menu is open keeps the unpin button clear of the title). "group-hover/recent-item:pr-16 group-has-[.sidebar-row-action[data-state=open]]/recent-item:pr-8" - : "group-hover/recent-item:pr-8 group-has-[.sidebar-row-action[data-state=open]]/recent-item:pr-8", + : // Hover room for the kebab only; title keeps one more character. + "group-hover/recent-item:pr-6 group-has-[.sidebar-row-action[data-state=open]]/recent-item:pr-6", ); const isRenamingThis = @@ -962,7 +964,8 @@ export function AppSidebar() { )} - + {/* Uniform pl-1.5 pr-2 keeps every hover pill the same width, inset from the edge. */} + - + - + - + {pinnedChatItems.map((item) => renderChatSidebarItem(item, "recent"), @@ -1146,7 +1149,7 @@ export function AppSidebar() { - + {recentChatItems.map((item) => renderChatSidebarItem(item, "recent"), 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/components/ui/sonner.tsx b/studio/frontend/src/components/ui/sonner.tsx index 9859190d2c..faaa075fa2 100644 --- a/studio/frontend/src/components/ui/sonner.tsx +++ b/studio/frontend/src/components/ui/sonner.tsx @@ -5,10 +5,10 @@ import { Alert02Icon, CheckmarkCircle02Icon, InformationCircleIcon, - Loading03Icon, MultiplicationSignCircleIcon, } from "@hugeicons/core-free-icons"; import { HugeiconsIcon } from "@hugeicons/react"; +import { Spinner } from "@/components/ui/spinner"; import { useTheme } from "next-themes"; import { Toaster as Sonner, type ToasterProps } from "sonner"; @@ -51,13 +51,8 @@ const Toaster = ({ ...props }: ToasterProps) => { className="size-4" /> ), - loading: ( - - ), + // App-wide arc spinner so loading toasts match the "Downloading model" toast. + loading: , }} style={ { 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 ? (