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.
This commit is contained in:
parent
240c0c3500
commit
9df607c0de
14 changed files with 37 additions and 31 deletions
|
|
@ -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"
|
||||
>
|
||||
<DropdownMenuItem onSelect={() => openRenameChat(item)}>
|
||||
<HugeiconsIcon icon={Edit03Icon} strokeWidth={1.75} className="size-icon" />
|
||||
|
|
|
|||
|
|
@ -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 ? (
|
||||
<>
|
||||
<ListLabel
|
||||
icon={<StarIcon className="size-3" />}
|
||||
icon={<HugeiconsIcon icon={StarIcon} className="size-3" />}
|
||||
collapsed={recommendedCollapsed}
|
||||
onToggle={() => setRecommendedCollapsed((v) => !v)}
|
||||
>Recommended</ListLabel>
|
||||
|
|
@ -1292,7 +1292,7 @@ export function HubModelPicker({
|
|||
|
||||
{showHfSection && filteredRecommendedIds.length > 0 ? (
|
||||
<>
|
||||
<ListLabel icon={<StarIcon className="size-3" />}>Recommended</ListLabel>
|
||||
<ListLabel icon={<HugeiconsIcon icon={StarIcon} className="size-3" />}>Recommended</ListLabel>
|
||||
{filteredRecommendedIds.map((id) => {
|
||||
const vram = recommendedVramMap.get(id);
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -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({
|
|||
/>
|
||||
<InputGroupAddon>
|
||||
<HugeiconsIcon
|
||||
icon={SearchIcon}
|
||||
icon={Search01Icon}
|
||||
strokeWidth={2}
|
||||
className="size-4 shrink-0 opacity-50"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -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() {
|
|||
<Command className="rounded-3xl p-0" filter={chatSearchFilter}>
|
||||
<div className="flex items-center gap-3 border-b border-border/40 px-4 py-3">
|
||||
<HugeiconsIcon
|
||||
icon={SearchIcon}
|
||||
icon={Search01Icon}
|
||||
strokeWidth={2}
|
||||
className="size-4 shrink-0 text-muted-foreground"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -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({
|
|||
|
||||
{/* */}
|
||||
<div className="relative">
|
||||
<SearchIcon className="pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 size-3.5 text-muted-foreground/60" />
|
||||
<HugeiconsIcon icon={Search01Icon} strokeWidth={1.75} className="pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 size-3.5 text-muted-foreground/60" />
|
||||
<input
|
||||
value={searchQuery}
|
||||
onChange={(e) => 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"
|
||||
>
|
||||
<SearchIcon className="size-3 shrink-0 text-muted-foreground/60" />
|
||||
<HugeiconsIcon icon={Search01Icon} strokeWidth={1.75} className="size-3 shrink-0 text-muted-foreground/60" />
|
||||
<span className="truncate">{name}</span>
|
||||
</button>
|
||||
))}
|
||||
|
|
@ -1939,7 +1940,7 @@ export function PromptStorageDialog({
|
|||
{searchQuery.trim() ? (
|
||||
<>
|
||||
<div className="flex size-12 items-center justify-center rounded-2xl bg-muted/60">
|
||||
<SearchIcon className="size-5 text-muted-foreground/40" />
|
||||
<HugeiconsIcon icon={Search01Icon} strokeWidth={1.75} className="size-5 text-muted-foreground/40" />
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<p className="text-sm font-medium text-muted-foreground">
|
||||
|
|
@ -2003,7 +2004,7 @@ export function PromptStorageDialog({
|
|||
{searchQuery.trim() ? (
|
||||
<>
|
||||
<div className="flex size-12 items-center justify-center rounded-2xl bg-muted/60">
|
||||
<SearchIcon className="size-5 text-muted-foreground/40" />
|
||||
<HugeiconsIcon icon={Search01Icon} strokeWidth={1.75} className="size-5 text-muted-foreground/40" />
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<p className="text-sm font-medium text-muted-foreground">
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ export function DotTag({
|
|||
return (
|
||||
<span
|
||||
className={cn(
|
||||
"inline-flex h-5 shrink-0 items-center gap-1.5 whitespace-nowrap rounded-[7px] border border-border/60 bg-transparent px-1.5 text-[11px] font-medium leading-none text-muted-foreground",
|
||||
"inline-flex h-5 shrink-0 items-center gap-1.5 whitespace-nowrap rounded-full border border-border/60 bg-transparent px-2 text-[11px] font-medium leading-none text-muted-foreground",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ const FIT_BADGE: Record<GgufFitClass, FitBadgeMeta> = {
|
|||
|
||||
/** 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",
|
||||
|
|
|
|||
|
|
@ -185,7 +185,7 @@ export function HubOptionMenu<T extends string>({
|
|||
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<T extends string>({
|
|||
}}
|
||||
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",
|
||||
)}
|
||||
>
|
||||
<span className="flex min-w-0 flex-1 items-center gap-2.5 overflow-hidden whitespace-normal break-words">
|
||||
|
|
|
|||
|
|
@ -200,7 +200,7 @@ function StatusChip({
|
|||
return (
|
||||
<span
|
||||
className={cn(
|
||||
"inline-flex h-5 shrink-0 items-center whitespace-nowrap rounded-[7px] border bg-transparent px-1.5 text-[11px] font-medium leading-none",
|
||||
"inline-flex h-5 shrink-0 items-center whitespace-nowrap rounded-full border bg-transparent px-2 text-[11px] font-medium leading-none",
|
||||
toneClass,
|
||||
className,
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import { cn } from "@/lib/utils";
|
|||
import {
|
||||
ChipIcon,
|
||||
Database02Icon,
|
||||
Logout01Icon,
|
||||
RemoveCircleIcon,
|
||||
PackageIcon,
|
||||
RamMemoryIcon,
|
||||
} from "@hugeicons/core-free-icons";
|
||||
|
|
@ -38,7 +38,7 @@ function StatPill({
|
|||
className={cn(
|
||||
"inline-flex cursor-default items-center gap-1.5 px-2.5 py-1 text-[11.5px] transition-colors duration-150",
|
||||
tone === "active"
|
||||
? "rounded-[11px] bg-emerald-500/10 text-emerald-700 ring-1 ring-inset ring-emerald-500/20 dark:text-emerald-300"
|
||||
? "rounded-full bg-emerald-500/10 text-emerald-700 ring-1 ring-inset ring-emerald-500/20 dark:text-emerald-300"
|
||||
: "hub-tag-soft text-muted-foreground hover:text-foreground/80",
|
||||
)}
|
||||
>
|
||||
|
|
@ -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"
|
||||
>
|
||||
<HugeiconsIcon
|
||||
icon={Logout01Icon}
|
||||
icon={RemoveCircleIcon}
|
||||
strokeWidth={1.75}
|
||||
className="size-3"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ import {
|
|||
Delete02Icon,
|
||||
FileSearchIcon,
|
||||
FolderAddIcon,
|
||||
FolderOpenIcon,
|
||||
Folder02Icon,
|
||||
FolderSearchIcon,
|
||||
PlusSignIcon,
|
||||
RefreshIcon,
|
||||
|
|
@ -294,7 +294,7 @@ export function OnDeviceFoldersDialog({
|
|||
) : sortedFolders.length === 0 ? (
|
||||
<div className="flex h-28 flex-col items-center justify-center gap-2 px-4 text-center text-[12px] text-muted-foreground">
|
||||
<HugeiconsIcon
|
||||
icon={FolderOpenIcon}
|
||||
icon={Folder02Icon}
|
||||
strokeWidth={1.75}
|
||||
className="size-5 text-muted-foreground/60"
|
||||
/>
|
||||
|
|
@ -310,7 +310,7 @@ export function OnDeviceFoldersDialog({
|
|||
>
|
||||
<div className="flex size-8 shrink-0 items-center justify-center rounded-[9px] bg-muted text-muted-foreground">
|
||||
<HugeiconsIcon
|
||||
icon={FolderOpenIcon}
|
||||
icon={Folder02Icon}
|
||||
strokeWidth={1.75}
|
||||
className="size-4"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ export function TransportToggle() {
|
|||
return (
|
||||
<fieldset
|
||||
aria-label="Download transport"
|
||||
className="hub-tag-soft m-0 inline-flex h-[26px] min-w-0 items-center gap-0.5 rounded-[11px] border-0 p-0.5 text-[11px]"
|
||||
className="hub-tag-soft m-0 inline-flex h-[26px] min-w-0 items-center gap-0.5 rounded-full border-0 p-0.5 text-[11px]"
|
||||
>
|
||||
{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
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
)}
|
||||
>
|
||||
<HugeiconsIcon
|
||||
|
|
|
|||
|
|
@ -320,7 +320,7 @@
|
|||
@layer utilities {
|
||||
.hub-tag-soft,
|
||||
.hub-tag-meta {
|
||||
@apply min-h-[26px] rounded-[11px] bg-muted/70 ring-1 ring-inset;
|
||||
@apply min-h-[26px] rounded-full bg-muted/70 ring-1 ring-inset;
|
||||
--tw-ring-color: color-mix(in srgb, var(--foreground) 3.5%, transparent);
|
||||
}
|
||||
|
||||
|
|
@ -335,6 +335,11 @@
|
|||
box-shadow: none;
|
||||
}
|
||||
|
||||
/* Single-row state (variant + actions only) reads as a full pill. */
|
||||
.hub-download-card:not(:has(> :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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue