From 9df607c0dee620ea88f73b79a1fa79ec7871a308 Mon Sep 17 00:00:00 2001 From: Michael Han <107991372+shimmyshimmer@users.noreply.github.com> Date: Fri, 12 Jun 2026 04:51:19 -0700 Subject: [PATCH] Studio: fully rounded Hub pills and refreshed menu icons (#6248) Rounds out the Hub chrome and aligns a handful of icons with the rest of the app. The header stat pills, HTTP/Xet toggle, HF token button, info chips, status badges and the single-row download card are now full pills, and the All formats style dropdown matches the composer + menu geometry. Also widens the chat options menu so "Move to project" fits on one line, and swaps a few icons to their HugeIcons equivalents. Changes: - hub.css: hub-tag-soft/hub-tag-meta pills go fully rounded; a single- row hub-download-card reads as a full pill via :has(), while the expanded card keeps its 22px radius, border and background. - hub-option-menu.tsx: dropdown surface and items match the composer + menu (22px container, 10px side padding, 12px item radius). - hf-token-indicator.tsx, transport-toggle.tsx: fully rounded with a fixed 26px height so they line up with the neighbouring pills. - model-inspector.tsx, dot-tag.tsx, gguf-download-card.tsx: status and info chips go fully rounded. - models-header.tsx: Eject uses RemoveCircleIcon; active model pill fully rounded. - on-device-folders-dialog.tsx: folder rows use Folder02Icon. - pickers.tsx: Recommended star uses the HugeIcons star. - command.tsx, chat-search-dialog.tsx, prompt-storage-dialog.tsx: search fields use the HugeIcons Search01Icon. - app-sidebar.tsx: chat options menu widened to w-56. Pure styling and icon swaps, no behavior changes. --- studio/frontend/src/components/app-sidebar.tsx | 2 +- .../assistant-ui/model-selector/pickers.tsx | 8 ++++---- studio/frontend/src/components/ui/command.tsx | 4 ++-- .../features/chat/components/chat-search-dialog.tsx | 4 ++-- .../chat/prompt-storage/prompt-storage-dialog.tsx | 11 ++++++----- studio/frontend/src/features/hub/catalog/dot-tag.tsx | 2 +- .../src/features/hub/catalog/gguf-download-card.tsx | 4 ++-- .../src/features/hub/catalog/hub-option-menu.tsx | 4 ++-- .../src/features/hub/catalog/model-inspector.tsx | 2 +- .../src/features/hub/catalog/models-header.tsx | 6 +++--- .../features/hub/catalog/on-device-folders-dialog.tsx | 6 +++--- .../src/features/hub/catalog/transport-toggle.tsx | 4 ++-- .../features/hub/components/hf-token-indicator.tsx | 4 ++-- studio/frontend/src/features/hub/hub.css | 7 ++++++- 14 files changed, 37 insertions(+), 31 deletions(-) diff --git a/studio/frontend/src/components/app-sidebar.tsx b/studio/frontend/src/components/app-sidebar.tsx index 41d1b54232..72bb75a84c 100644 --- a/studio/frontend/src/components/app-sidebar.tsx +++ b/studio/frontend/src/components/app-sidebar.tsx @@ -610,7 +610,7 @@ export function AppSidebar() { side="bottom" align="start" sideOffset={0} - className="unsloth-plus-menu menu-flat-destructive w-52" + className="unsloth-plus-menu menu-flat-destructive w-56" > openRenameChat(item)}> diff --git a/studio/frontend/src/components/assistant-ui/model-selector/pickers.tsx b/studio/frontend/src/components/assistant-ui/model-selector/pickers.tsx index be66f23a20..e218103fd9 100644 --- a/studio/frontend/src/components/assistant-ui/model-selector/pickers.tsx +++ b/studio/frontend/src/components/assistant-ui/model-selector/pickers.tsx @@ -39,11 +39,11 @@ import { extractParamLabel } from "@/lib/model-size"; import { cn, formatCompact } from "@/lib/utils"; import type { VramFitStatus } from "@/lib/vram"; import { checkVramFit, estimateLoadingVram } from "@/lib/vram"; -import { Add01Icon, Cancel01Icon, Download01Icon, Folder02Icon, Search01Icon } from "@hugeicons/core-free-icons"; +import { Add01Icon, Cancel01Icon, Download01Icon, Folder02Icon, Search01Icon, StarIcon } from "@hugeicons/core-free-icons"; import { HugeiconsIcon } from "@hugeicons/react"; import { FolderBrowser } from "./folder-browser"; import { ModelDeleteAction } from "./model-delete-action"; -import { ChevronDownIcon, ChevronRightIcon, StarIcon } from "lucide-react"; +import { ChevronDownIcon, ChevronRightIcon } from "lucide-react"; import { type ReactNode, useCallback, @@ -1231,7 +1231,7 @@ export function HubModelPicker({ {!showHfSection && cachedReady ? ( <> } + icon={} collapsed={recommendedCollapsed} onToggle={() => setRecommendedCollapsed((v) => !v)} >Recommended @@ -1292,7 +1292,7 @@ export function HubModelPicker({ {showHfSection && filteredRecommendedIds.length > 0 ? ( <> - }>Recommended + }>Recommended {filteredRecommendedIds.map((id) => { const vram = recommendedVramMap.get(id); return ( diff --git a/studio/frontend/src/components/ui/command.tsx b/studio/frontend/src/components/ui/command.tsx index f27dd6ac9a..9fbf739bf7 100644 --- a/studio/frontend/src/components/ui/command.tsx +++ b/studio/frontend/src/components/ui/command.tsx @@ -15,7 +15,7 @@ import { } from "@/components/ui/dialog"; import { InputGroup, InputGroupAddon } from "@/components/ui/input-group"; import { cn } from "@/lib/utils"; -import { SearchIcon, Tick02Icon } from "@hugeicons/core-free-icons"; +import { Search01Icon, Tick02Icon } from "@hugeicons/core-free-icons"; import { HugeiconsIcon } from "@hugeicons/react"; function Command({ @@ -86,7 +86,7 @@ function CommandInput({ /> diff --git a/studio/frontend/src/features/chat/components/chat-search-dialog.tsx b/studio/frontend/src/features/chat/components/chat-search-dialog.tsx index b0fd3c5f27..e6f51e2da8 100644 --- a/studio/frontend/src/features/chat/components/chat-search-dialog.tsx +++ b/studio/frontend/src/features/chat/components/chat-search-dialog.tsx @@ -8,7 +8,7 @@ import { CommandGroup, CommandList, } from "@/components/ui/command"; -import { Cancel01Icon, Message01Icon, SearchIcon } from "@hugeicons/core-free-icons"; +import { Cancel01Icon, Message01Icon, Search01Icon } from "@hugeicons/core-free-icons"; import { HugeiconsIcon } from "@hugeicons/react"; import { useNavigate } from "@tanstack/react-router"; import { Command as CommandPrimitive } from "cmdk"; @@ -70,7 +70,7 @@ export function ChatSearchDialog() {
diff --git a/studio/frontend/src/features/chat/prompt-storage/prompt-storage-dialog.tsx b/studio/frontend/src/features/chat/prompt-storage/prompt-storage-dialog.tsx index 2b3acbf87d..e68e3c9d24 100644 --- a/studio/frontend/src/features/chat/prompt-storage/prompt-storage-dialog.tsx +++ b/studio/frontend/src/features/chat/prompt-storage/prompt-storage-dialog.tsx @@ -11,6 +11,8 @@ import { DialogTitle, } from "@/components/ui/dialog"; import { cn } from "@/lib/utils"; +import { Search01Icon } from "@hugeicons/core-free-icons"; +import { HugeiconsIcon } from "@hugeicons/react"; import { BookmarkIcon, CheckIcon, @@ -20,7 +22,6 @@ import { PencilIcon, PlayIcon, PlusIcon, - SearchIcon, Trash2Icon, UploadIcon, XIcon, @@ -1879,7 +1880,7 @@ export function PromptStorageDialog({ {/* */}
- + setSearchQuery(e.target.value)} @@ -1898,7 +1899,7 @@ export function PromptStorageDialog({ onClick={() => { setSearchQuery(name); setShowSuggestions(false); }} className="flex w-full items-center gap-2.5 px-3 py-2 text-sm hover:bg-accent hover:text-accent-foreground transition-colors text-left" > - + {name} ))} @@ -1939,7 +1940,7 @@ export function PromptStorageDialog({ {searchQuery.trim() ? ( <>
- +

@@ -2003,7 +2004,7 @@ export function PromptStorageDialog({ {searchQuery.trim() ? ( <>

- +

diff --git a/studio/frontend/src/features/hub/catalog/dot-tag.tsx b/studio/frontend/src/features/hub/catalog/dot-tag.tsx index 77b0a73d7b..8e6c9f97fc 100644 --- a/studio/frontend/src/features/hub/catalog/dot-tag.tsx +++ b/studio/frontend/src/features/hub/catalog/dot-tag.tsx @@ -32,7 +32,7 @@ export function DotTag({ return ( diff --git a/studio/frontend/src/features/hub/catalog/gguf-download-card.tsx b/studio/frontend/src/features/hub/catalog/gguf-download-card.tsx index e0178c3472..92427a6285 100644 --- a/studio/frontend/src/features/hub/catalog/gguf-download-card.tsx +++ b/studio/frontend/src/features/hub/catalog/gguf-download-card.tsx @@ -110,7 +110,7 @@ const FIT_BADGE: Record = { /** Chip styling matching the on-device list's StatChip, no icon. */ const CHIP_BASE = - "inline-flex h-5 shrink-0 items-center justify-center whitespace-nowrap rounded-[7px] border px-1.5 text-[11.5px] font-medium tabular-nums leading-3 shadow-[inset_0_1px_0_rgba(255,255,255,0.04)]"; + "inline-flex h-5 shrink-0 items-center justify-center whitespace-nowrap rounded-full border px-2 text-[11.5px] font-medium tabular-nums leading-3 shadow-[inset_0_1px_0_rgba(255,255,255,0.04)]"; const CHIP_DEFAULT = "border-foreground/15 bg-muted text-foreground/85 dark:border-border/60 dark:bg-white/[0.04] dark:text-foreground/85"; const CHIP_ACTIVE = @@ -328,7 +328,7 @@ const GgufVariantMenuRow = memo(function GgufVariantMenuRow({ onClick={handleDelete} aria-label={`Delete ${item.label}${item.partial && !item.downloaded ? " (partial)" : ""}`} className={cn( - "absolute inset-0 inline-flex cursor-pointer items-center justify-center rounded-[7px]", + "absolute inset-0 inline-flex cursor-pointer items-center justify-center rounded-full", "bg-popover text-foreground/70 ring-1 ring-border transition-colors", "opacity-0 group-hover:opacity-100 focus-visible:opacity-100", "hover:text-destructive hover:ring-destructive/40", diff --git a/studio/frontend/src/features/hub/catalog/hub-option-menu.tsx b/studio/frontend/src/features/hub/catalog/hub-option-menu.tsx index a6596a3d54..a1542ef5c8 100644 --- a/studio/frontend/src/features/hub/catalog/hub-option-menu.tsx +++ b/studio/frontend/src/features/hub/catalog/hub-option-menu.tsx @@ -185,7 +185,7 @@ export function HubOptionMenu({ collisionPadding={12} onCloseAutoFocus={(event) => event.preventDefault()} className={cn( - "hub-menu-instant menu-soft-surface w-max min-w-[var(--radix-popover-trigger-width)] max-w-[min(var(--radix-popover-content-available-width),calc(100vw-1rem))] rounded-[14px] p-1 ring-0", + "hub-menu-instant menu-soft-surface w-max min-w-[var(--radix-popover-trigger-width)] max-w-[min(var(--radix-popover-content-available-width),calc(100vw-1rem))] rounded-[22px] px-[10px] py-2 ring-0", contentClassName, )} > @@ -214,7 +214,7 @@ export function HubOptionMenu({ }} onPointerEnter={() => activateIndex(index)} className={cn( - "relative flex w-full min-w-0 cursor-pointer select-none items-center rounded-xl corner-squircle py-2 pr-8 pl-3 text-left text-sm leading-snug outline-none transition-colors", + "relative flex w-full min-w-0 cursor-pointer select-none items-center rounded-[12px] py-2 pr-8 pl-3 text-left text-sm leading-snug outline-none transition-colors", )} > diff --git a/studio/frontend/src/features/hub/catalog/model-inspector.tsx b/studio/frontend/src/features/hub/catalog/model-inspector.tsx index a3a73f2714..371eebea9b 100644 --- a/studio/frontend/src/features/hub/catalog/model-inspector.tsx +++ b/studio/frontend/src/features/hub/catalog/model-inspector.tsx @@ -200,7 +200,7 @@ function StatusChip({ return ( @@ -125,7 +125,7 @@ export function ModelsHeader({ className="-mr-0.5 ml-0.5 inline-flex cursor-pointer items-center gap-1 rounded-md px-1.5 text-[11px] text-muted-foreground transition-colors hover:text-foreground" > diff --git a/studio/frontend/src/features/hub/catalog/on-device-folders-dialog.tsx b/studio/frontend/src/features/hub/catalog/on-device-folders-dialog.tsx index 94726b9295..d0d7d201b3 100644 --- a/studio/frontend/src/features/hub/catalog/on-device-folders-dialog.tsx +++ b/studio/frontend/src/features/hub/catalog/on-device-folders-dialog.tsx @@ -28,7 +28,7 @@ import { Delete02Icon, FileSearchIcon, FolderAddIcon, - FolderOpenIcon, + Folder02Icon, FolderSearchIcon, PlusSignIcon, RefreshIcon, @@ -294,7 +294,7 @@ export function OnDeviceFoldersDialog({ ) : sortedFolders.length === 0 ? (

@@ -310,7 +310,7 @@ export function OnDeviceFoldersDialog({ >
diff --git a/studio/frontend/src/features/hub/catalog/transport-toggle.tsx b/studio/frontend/src/features/hub/catalog/transport-toggle.tsx index d3fc68fbf3..1fb06221ee 100644 --- a/studio/frontend/src/features/hub/catalog/transport-toggle.tsx +++ b/studio/frontend/src/features/hub/catalog/transport-toggle.tsx @@ -40,7 +40,7 @@ export function TransportToggle() { return (
{OPTIONS.map((opt) => { const active = mode === opt.value; @@ -58,7 +58,7 @@ export function TransportToggle() { onClick={() => setMode(opt.value)} aria-pressed={active} className={cn( - "inline-flex h-[22px] cursor-pointer items-center justify-center rounded-[8px] px-2 font-medium tracking-tight transition-colors", + "inline-flex h-[22px] cursor-pointer items-center justify-center rounded-full px-2 font-medium tracking-tight transition-colors", active ? "hub-tab-toggle-pill text-foreground" : disabled diff --git a/studio/frontend/src/features/hub/components/hf-token-indicator.tsx b/studio/frontend/src/features/hub/components/hf-token-indicator.tsx index 35020de5b1..91f0b5e9ee 100644 --- a/studio/frontend/src/features/hub/components/hf-token-indicator.tsx +++ b/studio/frontend/src/features/hub/components/hf-token-indicator.tsx @@ -87,10 +87,10 @@ export function HfTokenIndicator({ showLabel = false }: HfTokenIndicatorProps = onClick={() => openDialog("general")} aria-label={ariaLabel} className={cn( - "inline-flex items-center justify-center px-2.5 py-1 text-[11.5px] transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", + "inline-flex h-[26px] items-center justify-center px-2.5 text-[11.5px] transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", hasToken ? "hub-tag-soft text-muted-foreground hover:text-foreground/80" - : "rounded-[11px] bg-destructive text-destructive-foreground hover:bg-destructive/90", + : "rounded-full bg-destructive text-destructive-foreground hover:bg-destructive/90", )} > :nth-child(2))) { + border-radius: 9999px; + } + .dark .hub-download-card { background-color: color-mix(in srgb, var(--foreground) 3.5%, transparent); border: 1px solid color-mix(in srgb, var(--foreground) 5.5%, transparent);