Studio: unify shadows, backgrounds and dark mode consistency in chat UI (#6116)

* Studio: refine chat UI shadow, background and sidebar divider

- Give both chat composers a Gemini style drop shadow with a short
  transition, and add a visible shadow in dark mode.
- Set the main chat surface to a soft off white (#fbfbfc) in light mode
  so the white composer reads as a card; dark mode is unchanged.
- Remove the divider line between the side menu and the page.

* Studio: lighten chat background to #fcfcfd

* Studio: make dark mode composer shadow visible

* Studio: drop composer shadow in dark mode

* Studio: unify all light mode shadows on the chatbox shadow

* Studio: unify dark mode surface colors and make hover states visible

* Studio: fix barely visible dark mode hover in account and plus menus

* Studio: lift settings dialog off the page background in dark mode

* Studio: drop greeting periods and restyle projects page to match the chatbox

* Studio: chatbox shadow on search dialog, grey sort pill on projects

* Studio: slimmer sidebar profile row, borderless login card

* Studio: match select popups to their trigger, taller profile hover target

* Studio: bigger projects search pill with the original chatbox glow

* Studio: keep select trigger shape while open

* Studio: center and narrow the projects search pill like Gemini

* Studio: borderless export card with the chatbox shadow

* Studio: chatbox shadow for selectable pills, warmer composer, search pill tweaks

* Studio: one warm background token for every page, softer search glow

* Studio: keep off white depth when warming the page background

* Studio: distinct sidebar surface from the page background

* Studio: white sidebar on the warm page background

* Studio: soften projects search shadow

* Studio: nudge the chat zero state up 5px

* Studio: darker dark mode page background, 28.5vh welcome offset

* Studio: dark mode shadows match the chatbox geometry, borderless recipes empty state

* Studio: revert dark mode shadows, desaturate dark recipe cards

* Studio: lift dark recipe cards, no hover shadow in dark

* Studio: drop the pale hover halo on recipe cards

* Studio: lighter dark recipe text, flat dark menus, flush select popups

* Studio: page-bg shadow on model selector, flush popovers and dropdowns

* Studio: zero menu offsets so dropdowns sit flush against triggers

* Studio: one 14px radius for list menus and sidebar buttons

* Studio: 14px buttons, pill hover shapes, narrower slider inputs

* Studio: pill buttons, keep profile row rectangular

* Studio: pill Save and Delete, small gap under account menu

* Studio: skinnier dropdown popups, larger account menu gap

* Studio: dropdown popups slightly wider than their trigger

* Restore light mode sidebar separator line

* Size panel number input pills to their content

* Lighten light mode page background

* Pure white composer, nudge page background whiter

* Use inline ch width for panel number pills

* Fix panel number pills at uniform 4ch width

---------

Co-authored-by: Daniel Han <danielhanchen@gmail.com>
This commit is contained in:
Michael Han 2026-06-10 03:09:37 -07:00 committed by GitHub
commit f542ffb023
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 181 additions and 140 deletions

View file

@ -223,7 +223,7 @@ function NavItem({
onClick={onClick}
isActive={active}
data-tour={dataTour}
className="sidebar-nav-btn h-[33px] rounded-[11px] gap-[8.5px] px-2.5 font-medium group-data-[collapsible=icon]:!w-[32px] group-data-[collapsible=icon]:!rounded-[10px] group-data-[collapsible=icon]:mx-auto"
className="sidebar-nav-btn h-[33px] rounded-full gap-[8.5px] px-2.5 font-medium group-data-[collapsible=icon]:!w-[32px] group-data-[collapsible=icon]:!rounded-full group-data-[collapsible=icon]:mx-auto"
>
<HugeiconsIcon icon={icon} strokeWidth={1.75} className="size-icon! shrink-0 group-hover/menu-button:animate-icon-pop" />
<span className="text-[14.5px] leading-[19px] tracking-nav">{label}</span>
@ -597,7 +597,7 @@ export function AppSidebar() {
? "sidebar-row-action group-hover/project-chat-item:opacity-100 group-hover/project-chat-item:pointer-events-auto focus-visible:opacity-100 focus-visible:pointer-events-auto"
: "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-[11px] pr-4 text-[14.5px] leading-[19px] tracking-nav font-medium",
"sidebar-nav-btn h-[33px] cursor-pointer rounded-full pr-4 text-[14.5px] leading-[19px] tracking-nav font-medium",
variant === "project" ? "pl-[37px]" : "pl-2.5",
variant === "project"
? "group-hover/project-chat-item:pr-8 group-has-[.sidebar-row-action[data-state=open]]/project-chat-item:pr-8"
@ -647,7 +647,7 @@ export function AppSidebar() {
<DropdownMenuContent
side="bottom"
align="start"
sideOffset={6}
sideOffset={0}
className="unsloth-plus-menu menu-flat-destructive w-52"
>
<DropdownMenuItem onSelect={() => openRenameChat(item)}>
@ -660,7 +660,7 @@ export function AppSidebar() {
<span>Move to project</span>
</DropdownMenuSubTrigger>
<DropdownMenuSubContent
sideOffset={8}
sideOffset={0}
alignOffset={-4}
className="unsloth-plus-menu w-52"
>
@ -827,7 +827,7 @@ export function AppSidebar() {
)}
</SidebarHeader>
<SidebarGroup className="group-data-[collapsible=icon]:px-0 px-2 pt-[9px] pb-px shrink-0">
<SidebarGroup className="group-data-[collapsible=icon]:px-0 px-1.5 pt-[9px] pb-px shrink-0">
<SidebarGroupContent>
<SidebarMenu>
<NavItem
@ -867,7 +867,7 @@ export function AppSidebar() {
scrolled && "is-scrolled",
)}
>
<SidebarGroup className="group-data-[collapsible=icon]:px-0 px-2 py-0 shrink-0">
<SidebarGroup className="group-data-[collapsible=icon]:px-0 px-1.5 py-0 shrink-0">
<SidebarGroupContent>
<SidebarMenu>
<NavItem
@ -918,7 +918,7 @@ export function AppSidebar() {
</CollapsibleTrigger>
</SidebarGroupLabel>
<CollapsibleContent>
<SidebarGroupContent className="px-2">
<SidebarGroupContent className="px-1.5">
<SidebarMenu>
<NavItem
icon={TestTubeOutlineIcon}
@ -1025,7 +1025,7 @@ export function AppSidebar() {
</div>
</SidebarGroupLabel>
<CollapsibleContent>
<SidebarGroupContent className="px-2">
<SidebarGroupContent className="px-1.5">
<SidebarMenu>
{recentChatItems.map((item) =>
renderChatSidebarItem(item, "recent"),
@ -1065,7 +1065,7 @@ export function AppSidebar() {
>
<SidebarMenuButton
isActive={isActiveRun}
className="sidebar-nav-btn h-auto flex-col items-start gap-0.5 py-[5px] rounded-[11px] pl-2.5 pr-7 text-[14.5px] tracking-nav font-medium"
className="sidebar-nav-btn h-auto flex-col items-start gap-0.5 py-[5px] rounded-full pl-2.5 pr-7 text-[14.5px] tracking-nav font-medium"
onClick={() => {
setSelectedHistoryRunId(run.id);
closeMobileIfOpen();
@ -1106,8 +1106,8 @@ export function AppSidebar() {
<DropdownMenuContent
side="bottom"
align="end"
sideOffset={4}
className="app-user-menu menu-soft-surface menu-flat-destructive ring-0 w-44 py-2 font-heading rounded-[14px] border-0"
sideOffset={0}
className="app-user-menu menu-soft-surface menu-flat-destructive ring-0 w-44 py-2 font-heading rounded-full border-0"
>
<DropdownMenuItem onSelect={() => openRenameRun(run)}>
<HugeiconsIcon icon={Edit03Icon} strokeWidth={1.75} className="size-icon" />
@ -1154,7 +1154,7 @@ export function AppSidebar() {
<SidebarMenuButton
size="lg"
aria-label={t("shell.accountMenu", { name: displayTitle })}
className="sidebar-nav-btn !h-[44px] gap-[9px] px-2 py-[3px] rounded-[14px]"
className="sidebar-nav-btn !h-[44px] -my-[3px] gap-[9px] px-2 py-[3px] rounded-[14px]"
>
<div className="flex shrink-0 items-center">
<UserAvatar
@ -1174,7 +1174,7 @@ export function AppSidebar() {
<DropdownMenuContent
side="top"
align="center"
sideOffset={6}
sideOffset={8}
className="app-user-menu menu-soft-surface-up ring-0 w-[16rem] px-1.5 py-2.5 font-heading rounded-[20px] border-0"
>
<DropdownMenuGroup>

View file

@ -740,8 +740,8 @@ function buildWelcome(hour: number, name: string): Welcome {
// Use the name on ~a third of lines (only direct salutations where it reads
// naturally); the rest stay name-free so greetings don't feel repetitive.
const base: Welcome[] = [
g(name ? `Good to see you, ${name}.` : "Good to see you.", "large sloth wave.png"),
g("Ready when you are.", "large sloth thumbs.png"),
g(name ? `Good to see you, ${name}` : "Good to see you", "large sloth wave.png"),
g("Ready when you are", "large sloth thumbs.png"),
DEFAULT_WELCOME,
g("How can I help?", "sloth sir large.png"),
];
@ -785,7 +785,7 @@ const ThreadWelcome: FC<{
return (
<div className="aui-thread-welcome-root mx-auto my-auto flex w-full max-w-(--thread-max-width) grow flex-col">
<div className="aui-thread-welcome-center flex w-full grow flex-col items-center justify-start pt-[28vh]">
<div className="aui-thread-welcome-center flex w-full grow flex-col items-center justify-start pt-[28.5vh]">
<div className="aui-thread-welcome-message flex w-full flex-col justify-center gap-9 px-4">
{/* Center the greeting (sloth + title) over the composer. */}
<div className="flex flex-row items-center justify-center gap-[15px]">
@ -2131,7 +2131,7 @@ const ComposerToolsMenu: FC<{ side?: "top" | "bottom" }> = ({
<DropdownMenuContent
side={side}
align="start"
sideOffset={2}
sideOffset={0}
avoidCollisions={true}
className="unsloth-plus-menu w-[212px]"
// Don't refocus the + on close; restored focus showed a stray ring.
@ -2655,7 +2655,7 @@ const AssistantActionBar: FC = () => {
side="bottom"
align="start"
onCloseAutoFocus={(e) => e.preventDefault()}
className="aui-action-bar-more-content z-50 min-w-32 overflow-hidden rounded-md [--radius:1.1rem] bg-popover p-1 text-popover-foreground shadow-[0_2px_8px_-2px_rgba(27,27,31,0.16)] dark:shadow-none"
className="aui-action-bar-more-content z-50 min-w-32 overflow-hidden rounded-md [--radius:1.1rem] bg-popover p-1 text-popover-foreground shadow-[0_2px_8px_-2px_rgba(0,0,0,0.16)] dark:shadow-none"
>
<ActionBarPrimitive.ExportMarkdown asChild={true}>
<ActionBarMorePrimitive.Item className="aui-action-bar-more-item flex cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground">

View file

@ -10,7 +10,7 @@ import type * as React from "react";
import { cn } from "@/lib/utils";
export const buttonVariants = cva(
"focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 rounded-4xl border border-transparent text-sm font-medium focus-visible:ring-[3px] aria-invalid:ring-[3px] [&_svg:not([class*='size-'])]:size-4 inline-flex items-center justify-center whitespace-nowrap transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none shrink-0 [&_svg]:shrink-0 outline-none group/button select-none cursor-pointer",
"focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 rounded-full border border-transparent text-sm font-medium focus-visible:ring-[3px] aria-invalid:ring-[3px] [&_svg:not([class*='size-'])]:size-4 inline-flex items-center justify-center whitespace-nowrap transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none shrink-0 [&_svg]:shrink-0 outline-none group/button select-none cursor-pointer",
{
variants: {
variant: {

View file

@ -146,7 +146,7 @@ function ComboboxInput({
function ComboboxContent({
className,
side = "bottom",
sideOffset = 6,
sideOffset = 0,
align = "start",
alignOffset = 0,
anchor,

View file

@ -36,7 +36,7 @@ function DropdownMenuTrigger({
function DropdownMenuContent({
className,
align = "start",
sideOffset = 4,
sideOffset = 0,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {
return (
@ -46,7 +46,7 @@ function DropdownMenuContent({
sideOffset={sideOffset}
align={align}
className={cn(
"data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 bg-popover text-popover-foreground min-w-48 rounded-lg p-1 duration-100 z-50 max-h-(--radix-dropdown-menu-content-available-height) w-(--radix-dropdown-menu-trigger-width) origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto data-[state=closed]:overflow-hidden",
"data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 bg-popover text-popover-foreground min-w-48 rounded-lg p-1 duration-100 z-50 max-h-(--radix-dropdown-menu-content-available-height) w-[calc(var(--radix-dropdown-menu-trigger-width)_+_6px)] data-[align=start]:-translate-x-[3px] data-[align=end]:translate-x-[3px] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto data-[state=closed]:overflow-hidden",
className,
)}
{...props}
@ -78,7 +78,7 @@ function DropdownMenuItem({
data-inset={inset}
data-variant={variant}
className={cn(
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:text-destructive not-data-[variant=destructive]:focus:**:text-accent-foreground gap-2.5 rounded-lg px-3 py-2 text-sm [&_svg:not([class*='size-'])]:size-4 group/dropdown-menu-item relative flex cursor-pointer items-center outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0",
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:text-destructive not-data-[variant=destructive]:focus:**:text-accent-foreground gap-2.5 rounded-full px-3 py-2 text-sm [&_svg:not([class*='size-'])]:size-4 group/dropdown-menu-item relative flex cursor-pointer items-center outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0",
className,
)}
{...props}
@ -96,7 +96,7 @@ function DropdownMenuCheckboxItem({
<DropdownMenuPrimitive.CheckboxItem
data-slot="dropdown-menu-checkbox-item"
className={cn(
"focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground gap-2.5 rounded-lg py-2 pr-8 pl-3 text-sm [&_svg:not([class*='size-'])]:size-4 relative flex cursor-pointer items-center outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
"focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground gap-2.5 rounded-full py-2 pr-8 pl-3 text-sm [&_svg:not([class*='size-'])]:size-4 relative flex cursor-pointer items-center outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
className,
)}
checked={checked}
@ -135,7 +135,7 @@ function DropdownMenuRadioItem({
<DropdownMenuPrimitive.RadioItem
data-slot="dropdown-menu-radio-item"
className={cn(
"focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground gap-2.5 rounded-lg py-2 pr-8 pl-3 text-sm [&_svg:not([class*='size-'])]:size-4 relative flex cursor-pointer items-center outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
"focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground gap-2.5 rounded-full py-2 pr-8 pl-3 text-sm [&_svg:not([class*='size-'])]:size-4 relative flex cursor-pointer items-center outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
className,
)}
{...props}
@ -221,7 +221,7 @@ function DropdownMenuSubTrigger({
data-slot="dropdown-menu-sub-trigger"
data-inset={inset}
className={cn(
"focus:bg-accent focus:text-accent-foreground data-open:bg-accent data-open:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground gap-2 rounded-lg px-3 py-2 text-sm [&_svg:not([class*='size-'])]:size-4 flex cursor-pointer items-center outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0",
"focus:bg-accent focus:text-accent-foreground data-open:bg-accent data-open:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground gap-2 rounded-full px-3 py-2 text-sm [&_svg:not([class*='size-'])]:size-4 flex cursor-pointer items-center outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0",
className,
)}
{...props}

View file

@ -72,7 +72,7 @@ function MenubarContent({
className,
align = "start",
alignOffset = -4,
sideOffset = 8,
sideOffset = 0,
...props
}: React.ComponentProps<typeof MenubarPrimitive.Content>) {
return (

View file

@ -23,7 +23,7 @@ function PopoverTrigger({
function PopoverContent({
className,
align = "center",
sideOffset = 4,
sideOffset = 0,
...props
}: React.ComponentProps<typeof PopoverPrimitive.Content>) {
return (

View file

@ -63,7 +63,9 @@ function SelectTrigger({
children,
icon,
iconClassName,
animateRadius = true,
/* Off by default: the radius morph matched the old item-aligned popup
that overlaid the trigger; popper menus drop below it instead. */
animateRadius = false,
...props
}: React.ComponentProps<typeof SelectPrimitive.Trigger> & {
size?: "sm" | "default";
@ -111,7 +113,7 @@ function SelectTrigger({
function SelectContent({
className,
children,
position = "item-aligned",
position = "popper",
align = "center",
container,
...props
@ -125,9 +127,8 @@ function SelectContent({
data-slot="select-content"
data-align-trigger={position === "item-aligned"}
className={cn(
"bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 min-w-36 rounded-xl p-1 corner-squircle duration-100 relative z-50 max-h-(--radix-select-content-available-height) origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto ",
position === "popper" &&
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
"bg-popover text-popover-foreground font-heading data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 min-w-36 rounded-xl p-1 corner-squircle duration-100 relative z-50 max-h-(--radix-select-content-available-height) origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto ",
// No popper translate offset: the menu sits flush against the trigger.
className,
)}
position={position}
@ -172,7 +173,7 @@ function SelectItem({
<SelectPrimitive.Item
data-slot="select-item"
className={cn(
"focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground gap-2.5 rounded-xl corner-squircle py-2 pr-8 pl-3 text-sm [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2 relative flex w-full cursor-pointer items-center outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
"focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground gap-2.5 rounded-full py-2 pr-8 pl-3 text-sm [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2 relative flex w-full cursor-pointer items-center outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
className,
)}
{...props}

View file

@ -305,9 +305,8 @@ function Sidebar({
data-sidebar="sidebar"
data-slot="sidebar-inner"
className={cn(
"bg-sidebar flex size-full flex-col overflow-hidden border-r border-sidebar-border",
"bg-sidebar flex size-full flex-col overflow-hidden border-r border-sidebar-border dark:border-r-0",
"group-data-[variant=floating]:ring-sidebar-border group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:shadow-sm group-data-[variant=floating]:ring-1",
hasPinMode && "ring-1 ring-sidebar-border/60",
)}
>
{children}

View file

@ -16,7 +16,7 @@ export function LoginPage() {
length="70vh"
style={{ opacity: 0.4 }}
/>
<Card className="relative z-10 w-full max-w-sm px-5 py-6 shadow-border ring-1 ring-border sm:px-6 sm:py-8">
<Card className="relative z-10 w-full max-w-sm px-5 py-6 shadow-border ring-0 sm:px-6 sm:py-8">
<AuthForm mode="login" />
</Card>
</div>

View file

@ -1009,7 +1009,7 @@ function ProjectLanding({
: { compare: item.id, project: projectId },
});
}}
className="group flex min-h-[58px] w-full items-center gap-4 rounded-[10px] px-4 py-2 text-left transition-colors hover:bg-nav-surface-hover"
className="group flex min-h-[58px] w-full items-center gap-4 rounded-full px-4 py-2 text-left transition-colors hover:bg-nav-surface-hover"
>
<div className="min-w-0 flex-1">
<div className="truncate text-[15px] font-semibold leading-5 text-foreground">

View file

@ -187,12 +187,16 @@ function NumericValueInput({
}
};
const displayed = focused ? draft : (displayValue ?? String(value));
return (
<input
type="text"
inputMode="decimal"
size={sizeAttr}
value={focused ? draft : (displayValue ?? String(value))}
/* Fixed 4ch pill; grows only when a longer value would clip. */
style={{ width: `calc(${Math.max(displayed.length, 4)}ch + 18px)` }}
value={displayed}
aria-label={ariaLabel}
onFocus={(e) => {
cancelBlurCommitRef.current = false;
@ -263,7 +267,7 @@ function ParamSlider({
onChange={onChange}
displayValue={displayValue}
ariaLabel={label}
size={valueSize ?? 6}
size={valueSize ?? 4}
/>
</div>
<Slider
@ -837,7 +841,7 @@ export function ChatSettingsPanel({
animateRadius={false}
icon={ArrowDown01Icon}
iconClassName="size-3.5"
className="grid h-7 w-[60px] min-w-0 grid-cols-[minmax(0,1fr)_auto] items-center gap-1 rounded-[10px] border-transparent bg-black/[0.04] dark:bg-white/[0.05] hover:bg-black/[0.06] dark:hover:bg-white/[0.07] px-2 py-0 text-[13px]! font-medium text-nav-fg focus-visible:ring-0 focus-visible:border-transparent [&_[data-slot=select-value]]:min-w-0 [&_[data-slot=select-value]]:truncate [&>svg]:shrink-0"
className="grid h-7 w-[60px] min-w-0 grid-cols-[minmax(0,1fr)_auto] items-center gap-1 rounded-[10px] border-transparent bg-black/[0.04] dark:bg-white/[0.05] hover:bg-black/[0.06] dark:hover:bg-white/[0.1] px-2 py-0 text-[13px]! font-medium text-nav-fg focus-visible:ring-0 focus-visible:border-transparent [&_[data-slot=select-value]]:min-w-0 [&_[data-slot=select-value]]:truncate [&>svg]:shrink-0"
>
<SelectValue />
</SelectTrigger>
@ -877,7 +881,7 @@ export function ChatSettingsPanel({
animateRadius={false}
icon={ArrowDown01Icon}
iconClassName="size-3.5"
className="grid h-7 w-[120px] min-w-0 grid-cols-[minmax(0,1fr)_auto] items-center gap-1 rounded-[10px] border-transparent bg-black/[0.04] dark:bg-white/[0.05] hover:bg-black/[0.06] dark:hover:bg-white/[0.07] px-2 py-0 text-[13px]! font-medium text-nav-fg focus-visible:ring-0 focus-visible:border-transparent [&_[data-slot=select-value]]:min-w-0 [&_[data-slot=select-value]]:truncate [&>svg]:shrink-0"
className="grid h-7 w-[120px] min-w-0 grid-cols-[minmax(0,1fr)_auto] items-center gap-1 rounded-[10px] border-transparent bg-black/[0.04] dark:bg-white/[0.05] hover:bg-black/[0.06] dark:hover:bg-white/[0.1] px-2 py-0 text-[13px]! font-medium text-nav-fg focus-visible:ring-0 focus-visible:border-transparent [&_[data-slot=select-value]]:min-w-0 [&_[data-slot=select-value]]:truncate [&>svg]:shrink-0"
data-test-id="speculative-type-select"
>
<SelectValue />
@ -928,7 +932,7 @@ export function ChatSettingsPanel({
}}
data-test-id="spec-draft-n-max-input"
aria-label="Speculative decoding draft tokens"
className="h-7 w-[72px] rounded-[10px] border-transparent bg-black/[0.04] dark:bg-white/[0.05] hover:bg-black/[0.06] dark:hover:bg-white/[0.07] px-2 py-0 text-[13px] font-medium text-nav-fg outline-none focus-visible:ring-0"
className="h-7 w-[72px] rounded-[10px] border-transparent bg-black/[0.04] dark:bg-white/[0.05] hover:bg-black/[0.06] dark:hover:bg-white/[0.1] px-2 py-0 text-[13px] font-medium text-nav-fg outline-none focus-visible:ring-0"
/>
</div>
)}
@ -1058,7 +1062,7 @@ export function ChatSettingsPanel({
</DropdownMenuTrigger>
<DropdownMenuContent
align="start"
sideOffset={6}
sideOffset={0}
className="menu-soft-surface ring-0 border-0 rounded-lg p-1.5"
>
{presets.map((p, index) => (
@ -1092,7 +1096,7 @@ export function ChatSettingsPanel({
variant={presetSaveState.isSaveReady ? "default" : "outline"}
size="sm"
className={cn(
"h-9 w-full rounded-[10px] text-[13px] font-medium tracking-nav",
"h-9 w-full rounded-full text-[13px] font-medium tracking-nav",
presetSaveState.isSaveReady &&
"bg-primary text-primary-foreground hover:bg-primary/90",
)}
@ -1107,7 +1111,7 @@ export function ChatSettingsPanel({
disabled={!(settingsHydrated && activeCustomPreset)}
variant="outline"
size="sm"
className="h-9 w-full rounded-[10px] text-[13px] font-medium tracking-nav text-muted-foreground"
className="h-9 w-full rounded-full text-[13px] font-medium tracking-nav text-muted-foreground"
title={
activeCustomPreset
? activeBuiltinPreset

View file

@ -64,7 +64,7 @@ export function ChatSearchDialog() {
<CommandDialog
open={isOpen}
onOpenChange={setOpen}
className="chat-search-surface corner-squircle top-[25%] w-[635px] max-w-[calc(100%-2rem)] gap-0 p-0 sm:max-w-[635px]"
className="chat-search-surface corner-squircle top-[25%] w-[635px] max-w-[calc(100%-2rem)] gap-0 p-0 ring-0 shadow-[0_2px_8px_-2px_rgba(0,0,0,0.16)] dark:shadow-none sm:max-w-[635px]"
overlayClassName="bg-transparent"
>
<Command className="rounded-4xl p-0" filter={chatSearchFilter}>
@ -117,7 +117,7 @@ export function ChatSearchDialog() {
});
close();
}}
className="relative flex cursor-pointer select-none items-center gap-3 rounded-lg px-3 py-2.5 text-sm outline-hidden data-selected:bg-muted data-selected:text-foreground"
className="relative flex cursor-pointer select-none items-center gap-3 rounded-full px-3 py-2.5 text-sm outline-hidden data-selected:bg-muted data-selected:text-foreground"
>
<HugeiconsIcon
icon={Message01Icon}

View file

@ -71,7 +71,7 @@ export function ProjectSwitcher({
<DropdownMenuContent
side="bottom"
align="start"
sideOffset={6}
sideOffset={0}
className="app-user-menu menu-soft-surface ring-0 min-w-56 max-w-72 max-h-72 py-2 font-heading rounded-[14px] border-0"
>
{showLoadingRow ? (

View file

@ -260,7 +260,7 @@ export function McpComposerButton({
<DropdownMenuContent
side={side}
align="start"
sideOffset={2}
sideOffset={0}
avoidCollisions={true}
className="unsloth-plus-menu mcp-menu w-[232px]"
>

View file

@ -258,7 +258,7 @@ export function ProjectsPage() {
value={sortMode}
onValueChange={(v) => setSortMode(v as SortMode)}
>
<SelectTrigger className="h-9 w-[130px]">
<SelectTrigger className="h-9 w-[130px] rounded-full border-none bg-muted shadow-none dark:bg-card">
<SelectValue />
</SelectTrigger>
<SelectContent>
@ -324,15 +324,15 @@ export function ProjectsPage() {
</div>
</div>
<div className="relative mt-6">
<span className="pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground">
<div className="relative mx-auto mt-10 w-full max-w-[720px]">
<span className="pointer-events-none absolute left-5 top-1/2 -translate-y-1/2 text-muted-foreground">
<HugeiconsIcon icon={Search01Icon} strokeWidth={1.75} className="size-icon" />
</span>
<Input
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search projects..."
className="h-11 pl-10"
className="h-14 rounded-full border-none bg-background pl-13 pr-5 shadow-[0_0_20px_0_rgba(0,0,0,0.07)] dark:bg-card dark:shadow-none"
aria-label="Search projects"
/>
</div>
@ -342,7 +342,7 @@ export function ProjectsPage() {
{Array.from({ length: 6 }).map((_, index) => (
<div
key={index}
className="min-h-[160px] rounded-[14px] border border-border/70 bg-card p-5"
className="min-h-[160px] rounded-[14px] bg-card p-5 shadow-[0_2px_8px_-2px_rgba(0,0,0,0.16)] dark:shadow-none"
>
<Skeleton className="h-5 w-2/3 rounded-[6px]" />
<Skeleton className="mt-3 h-4 w-full rounded-[6px]" />
@ -361,7 +361,7 @@ export function ProjectsPage() {
{projects.length === 0 && (
<Button
variant="outline"
className="mt-2"
className="mt-2 border-none bg-background shadow-[0_2px_8px_-2px_rgba(0,0,0,0.16)] dark:bg-card dark:shadow-none"
onClick={() => {
setNameDraft("");
setCreating(true);
@ -402,7 +402,7 @@ export function ProjectsPage() {
openProject(project.id);
}
}}
className="group/project-card relative flex min-h-[160px] cursor-pointer flex-col rounded-[14px] border border-border/70 bg-card p-5 text-left transition-colors hover:border-border hover:bg-accent/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
className="group/project-card relative flex min-h-[160px] cursor-pointer flex-col rounded-[14px] bg-card p-5 text-left shadow-[0_2px_8px_-2px_rgba(0,0,0,0.16)] transition-colors hover:bg-accent/40 dark:shadow-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
>
<div className="flex items-start justify-between gap-2">
<h2 className="truncate pr-2 text-[16px] font-semibold text-foreground">
@ -422,7 +422,7 @@ export function ProjectsPage() {
<DropdownMenuContent
side="bottom"
align="end"
sideOffset={4}
sideOffset={0}
onClick={(e) => e.stopPropagation()}
onKeyDown={(e) => e.stopPropagation()}
className="app-user-menu menu-soft-surface menu-flat-destructive ring-0 w-44 py-2 font-heading rounded-[14px] border-0"

View file

@ -1270,7 +1270,7 @@ export function SharedComposer({
<DropdownMenuContent
side="top"
align="start"
sideOffset={2}
sideOffset={0}
avoidCollisions={true}
className="unsloth-plus-menu w-[212px]"
onCloseAutoFocus={(event) => event.preventDefault()}

View file

@ -75,7 +75,7 @@ const TEMPLATE_CARDS: TemplateCard[] = [
difficulty: "Easy",
learningBadges: ["Seed Dataset", "LLM Text", "Prompting"],
surfaceClassName:
"from-emerald-500/15 via-green-500/5 to-transparent dark:from-emerald-400/30 dark:via-green-400/14 dark:to-emerald-950/16",
"from-emerald-500/15 via-green-500/5 to-transparent dark:from-emerald-400/20 dark:via-green-400/10 dark:to-emerald-950/16",
shineColor: [
"rgb(16 185 129 / 0.45)",
"rgb(34 197 94 / 0.4)",
@ -91,7 +91,7 @@ const TEMPLATE_CARDS: TemplateCard[] = [
difficulty: "Easy",
learningBadges: ["Unstructured", "LLM Text"],
surfaceClassName:
"from-violet-500/15 via-fuchsia-500/5 to-transparent dark:from-violet-400/30 dark:via-fuchsia-400/14 dark:to-violet-950/16",
"from-violet-500/15 via-fuchsia-500/5 to-transparent dark:from-violet-400/20 dark:via-fuchsia-400/10 dark:to-violet-950/16",
shineColor: [
"rgb(139 92 246 / 0.45)",
"rgb(217 70 239 / 0.4)",
@ -107,7 +107,7 @@ const TEMPLATE_CARDS: TemplateCard[] = [
difficulty: "Starter",
learningBadges: ["Vision", "LLM Text", "Image Context"],
surfaceClassName:
"from-lime-500/15 via-emerald-500/5 to-transparent dark:from-lime-400/30 dark:via-emerald-400/14 dark:to-lime-950/16",
"from-lime-500/15 via-emerald-500/5 to-transparent dark:from-lime-400/20 dark:via-emerald-400/10 dark:to-lime-950/16",
shineColor: [
"rgb(132 204 22 / 0.45)",
"rgb(16 185 129 / 0.4)",
@ -123,7 +123,7 @@ const TEMPLATE_CARDS: TemplateCard[] = [
difficulty: "Intermediate",
learningBadges: ["LLM Judge", "LLM Code", "Subcategory", "Category"],
surfaceClassName:
"from-amber-500/15 via-orange-500/5 to-transparent dark:from-amber-400/30 dark:via-orange-400/14 dark:to-amber-950/16",
"from-amber-500/15 via-orange-500/5 to-transparent dark:from-amber-400/20 dark:via-orange-400/10 dark:to-amber-950/16",
shineColor: [
"rgb(245 158 11 / 0.45)",
"rgb(249 115 22 / 0.4)",
@ -139,7 +139,7 @@ const TEMPLATE_CARDS: TemplateCard[] = [
difficulty: "Intermediate",
learningBadges: ["LLM Code", "Prompting", "Drop Columns"],
surfaceClassName:
"from-blue-500/15 via-indigo-500/5 to-transparent dark:from-blue-400/30 dark:via-indigo-400/14 dark:to-blue-950/16",
"from-blue-500/15 via-indigo-500/5 to-transparent dark:from-blue-400/20 dark:via-indigo-400/10 dark:to-blue-950/16",
shineColor: [
"rgb(59 130 246 / 0.45)",
"rgb(99 102 241 / 0.4)",
@ -155,7 +155,7 @@ const TEMPLATE_CARDS: TemplateCard[] = [
difficulty: "Advanced",
learningBadges: ["Structured LLM", "Expression", "Jinja"],
surfaceClassName:
"from-cyan-500/15 via-sky-500/5 to-transparent dark:from-cyan-400/30 dark:via-sky-400/14 dark:to-cyan-950/16",
"from-cyan-500/15 via-sky-500/5 to-transparent dark:from-cyan-400/20 dark:via-sky-400/10 dark:to-cyan-950/16",
shineColor: [
"rgb(6 182 212 / 0.45)",
"rgb(56 189 248 / 0.4)",
@ -171,7 +171,7 @@ const TEMPLATE_CARDS: TemplateCard[] = [
difficulty: "Intermediate",
learningBadges: ["GitHub", "LLM Text", "Structured LLM"],
surfaceClassName:
"from-slate-500/15 via-zinc-500/5 to-transparent dark:from-slate-400/30 dark:via-zinc-400/14 dark:to-slate-950/16",
"from-slate-500/15 via-zinc-500/5 to-transparent dark:from-slate-400/20 dark:via-zinc-400/10 dark:to-slate-950/16",
shineColor: [
"rgb(71 85 105 / 0.45)",
"rgb(100 116 139 / 0.4)",
@ -241,7 +241,7 @@ function LearningRecipeCards({
type="button"
disabled={isDisabled}
onClick={() => onSelect(template)}
className={`group shadow-border relative overflow-hidden rounded-2xl bg-gradient-to-br text-left transition-transform ${template.surfaceClassName} enabled:cursor-pointer enabled:hover:-translate-y-0.5 enabled:hover:shadow-md disabled:cursor-not-allowed disabled:opacity-70`}
className={`group shadow-border relative overflow-hidden rounded-2xl bg-gradient-to-br dark:bg-white/[0.05] text-left transition-transform ${template.surfaceClassName} enabled:cursor-pointer enabled:hover:-translate-y-0.5 disabled:cursor-not-allowed disabled:opacity-70`}
>
<ShineBorder
borderWidth={1.2}
@ -267,7 +267,7 @@ function LearningRecipeCards({
<p className="line-clamp-2 text-sm font-semibold leading-tight text-foreground">
{template.title}
</p>
<p className="line-clamp-2 text-xs text-muted-foreground">
<p className="line-clamp-2 text-xs text-muted-foreground dark:text-zinc-300">
{template.description}
</p>
</div>
@ -280,7 +280,7 @@ function LearningRecipeCards({
<Badge
key={`${template.title}-${badge}`}
variant="outline"
className="h-5 shrink-0 px-1.5 text-[10px]"
className="h-5 shrink-0 px-1.5 text-[10px] dark:text-zinc-300"
>
{badge}
</Badge>
@ -288,7 +288,7 @@ function LearningRecipeCards({
{extraLearningBadgeCount > 0 ? (
<Badge
variant="outline"
className="h-5 shrink-0 px-1.5 text-[10px]"
className="h-5 shrink-0 px-1.5 text-[10px] dark:text-zinc-300"
>
+{extraLearningBadgeCount}
</Badge>
@ -296,7 +296,7 @@ function LearningRecipeCards({
{isReady ? null : (
<Badge
variant="secondary"
className="h-5 shrink-0 px-1.5 text-[10px]"
className="h-5 shrink-0 px-1.5 text-[10px] dark:text-zinc-300"
>
Soon
</Badge>
@ -449,7 +449,7 @@ export function DataRecipesPage(): ReactElement {
</p>
</div>
) : recipes.length === 0 ? (
<Empty className="mt-8 border border-dashed border-border/70">
<Empty className="mt-8 border border-dashed border-border/70 dark:border-none">
<EmptyHeader>
<EmptyMedia variant="icon">
<HugeiconsIcon icon={CookBookIcon} className="size-5" />

View file

@ -539,7 +539,7 @@ export function ExportPage() {
description="Select source, method, and quantization"
accent="emerald"
featured={true}
className="shadow-border ring-1 ring-border"
className="ring-0 shadow-[0_2px_8px_-2px_rgba(0,0,0,0.16)] dark:shadow-none"
>
{/* Loading / error states */}
{loadingCheckpoints && (

View file

@ -58,7 +58,7 @@ export function NetworkErrorState({
<button
type="button"
onClick={onRetry}
className="inline-flex h-8 items-center gap-1.5 rounded-[10px] bg-transparent px-3 text-[12px] font-medium text-foreground transition-colors hover:bg-foreground/[0.04] dark:hover:bg-white/[0.05]"
className="inline-flex h-8 items-center gap-1.5 rounded-[10px] bg-transparent px-3 text-[12px] font-medium text-foreground transition-colors hover:bg-foreground/[0.04] dark:hover:bg-white/[0.1]"
>
<HugeiconsIcon
icon={RefreshIcon}
@ -113,7 +113,7 @@ export function DiscoverFetchMoreState({
type="button"
onClick={onFetchMore}
disabled={isLoadingMore}
className="inline-flex h-8 items-center gap-1.5 rounded-[10px] bg-transparent px-3 text-[12px] font-medium text-foreground transition-colors hover:bg-foreground/[0.04] disabled:cursor-not-allowed disabled:opacity-50 dark:hover:bg-white/[0.05]"
className="inline-flex h-8 items-center gap-1.5 rounded-[10px] bg-transparent px-3 text-[12px] font-medium text-foreground transition-colors hover:bg-foreground/[0.04] disabled:cursor-not-allowed disabled:opacity-50 dark:hover:bg-white/[0.1]"
>
<HugeiconsIcon
icon={RefreshIcon}
@ -191,7 +191,7 @@ export function InventoryErrorState({
<button
type="button"
onClick={onRetry}
className="inline-flex h-8 items-center gap-1.5 rounded-[10px] bg-transparent px-3 text-[12px] font-medium text-foreground transition-colors hover:bg-foreground/[0.04] dark:hover:bg-white/[0.05]"
className="inline-flex h-8 items-center gap-1.5 rounded-[10px] bg-transparent px-3 text-[12px] font-medium text-foreground transition-colors hover:bg-foreground/[0.04] dark:hover:bg-white/[0.1]"
>
<HugeiconsIcon icon={RefreshIcon} strokeWidth={1.75} className="size-3.5" />
Try again

View file

@ -666,7 +666,7 @@ export function GgufDownloadCard({
e.preventDefault();
setOpen((o) => !o);
}}
className="hub-menu-trigger flex h-9 min-w-0 flex-1 cursor-pointer items-center gap-2.5 rounded-[12px] px-3 text-left transition-colors hover:bg-foreground/[0.04] data-[state=open]:bg-foreground/[0.06] dark:hover:bg-white/[0.04] dark:data-[state=open]:bg-white/[0.06]"
className="hub-menu-trigger flex h-9 min-w-0 flex-1 cursor-pointer items-center gap-2.5 rounded-[12px] px-3 text-left transition-colors hover:bg-foreground/[0.04] data-[state=open]:bg-foreground/[0.06] dark:hover:bg-white/[0.1] dark:data-[state=open]:bg-white/[0.06]"
>
{selected ? (
<QuantBadge
@ -723,7 +723,7 @@ export function GgufDownloadCard({
<PopoverContent
align="start"
side="bottom"
sideOffset={8}
sideOffset={0}
avoidCollisions={false}
className="hub-menu-instant menu-soft-surface w-[var(--radix-popover-trigger-width)] min-w-[200px] gap-0 overflow-hidden p-0 py-2 ring-0"
>

View file

@ -181,7 +181,7 @@ export function HubOptionMenu<T extends string>({
<PopoverContent
align={align}
side="bottom"
sideOffset={8}
sideOffset={0}
collisionPadding={12}
onCloseAutoFocus={(event) => event.preventDefault()}
className={cn(

View file

@ -352,7 +352,7 @@ export function LocalOnDeviceCard({
<PopoverContent
align="start"
side="bottom"
sideOffset={8}
sideOffset={0}
avoidCollisions={false}
className="hub-menu-instant menu-soft-surface w-[var(--radix-popover-trigger-width)] min-w-[220px] gap-0 overflow-hidden p-0 py-2 ring-0"
>

View file

@ -139,7 +139,7 @@ function DownloadRow({ jobKey }: { jobKey: string }) {
}
className={cn(
"inline-flex size-6 shrink-0 cursor-pointer items-center justify-center rounded-[7px] text-muted-foreground transition-colors",
"hover:bg-foreground/[0.06] hover:text-foreground disabled:cursor-default disabled:opacity-50 dark:hover:bg-white/[0.06]",
"hover:bg-foreground/[0.06] hover:text-foreground disabled:cursor-default disabled:opacity-50 dark:hover:bg-white/[0.1]",
)}
>
<HugeiconsIcon
@ -234,7 +234,7 @@ export function DownloadManagerPanel() {
type="button"
aria-label="Collapse downloads"
onClick={() => setCollapsed(true)}
className="inline-flex size-6 shrink-0 cursor-pointer items-center justify-center rounded-[7px] text-muted-foreground transition-colors hover:bg-foreground/[0.06] hover:text-foreground dark:hover:bg-white/[0.06]"
className="inline-flex size-6 shrink-0 cursor-pointer items-center justify-center rounded-[7px] text-muted-foreground transition-colors hover:bg-foreground/[0.06] hover:text-foreground dark:hover:bg-white/[0.1]"
>
<HugeiconsIcon
icon={ArrowDown01Icon}

View file

@ -118,7 +118,7 @@ export function ProfilePersonalizationPanel() {
<button
type="button"
onClick={() => fileInputRef.current?.click()}
className="absolute right-0 bottom-0 -translate-x-[15.625%] -translate-y-[15.625%] flex size-8 items-center justify-center rounded-full border border-border bg-background text-foreground shadow-[0_2px_8px_-2px_rgba(27,27,31,0.16)] transition-colors hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"
className="absolute right-0 bottom-0 -translate-x-[15.625%] -translate-y-[15.625%] flex size-8 items-center justify-center rounded-full border border-border bg-background text-foreground shadow-[0_2px_8px_-2px_rgba(0,0,0,0.16)] transition-colors hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"
aria-label={t("settings.profile.changePicture")}
>
<HugeiconsIcon icon={Camera01Icon} className="size-3.5" strokeWidth={2} />

View file

@ -591,7 +591,7 @@ export function LocalRecipeModelSelector({
</PopoverTrigger>
<PopoverContent
align="start"
sideOffset={6}
sideOffset={0}
className="menu-soft-surface nodrag nowheel gap-0 overflow-hidden p-0"
style={{
width:

View file

@ -155,18 +155,18 @@ export function SettingsDialog() {
type="button"
onClick={() => setActiveTab(tab.id)}
className={cn(
"relative flex h-[32px] items-center gap-2.5 rounded-[11px] pl-3 pr-2.5 text-[14.5px] leading-[19px] tracking-nav font-medium transition-colors",
"relative flex h-[32px] items-center gap-2.5 rounded-full pl-3 pr-2.5 text-[14.5px] leading-[19px] tracking-nav font-medium transition-colors",
"max-sm:shrink-0",
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1 focus-visible:ring-offset-background",
active
? "text-black dark:text-white"
: "text-[#383835] dark:text-[#c7c7c4] hover:bg-[#ececec] dark:hover:bg-[#2d2f33] hover:text-black dark:hover:text-white",
: "text-[#383835] dark:text-[#c7c7c4] hover:bg-[#ececec] dark:hover:bg-[#3a3d43] hover:text-black dark:hover:text-white",
)}
>
{active && (
<motion.span
layoutId="settings-active-pill"
className="absolute inset-0 rounded-[11px] bg-[#ececec] dark:bg-[#2d2f33]"
className="absolute inset-0 rounded-full bg-[#ececec] dark:bg-[#3a3d43]"
transition={
reduced
? { duration: 0 }
@ -202,7 +202,7 @@ export function SettingsDialog() {
<button
type="button"
onClick={closeDialog}
className="absolute top-3 right-3 z-10 flex size-7 items-center justify-center rounded-full text-[#383835] dark:text-[#c7c7c4] transition-colors hover:bg-[#ececec] dark:hover:bg-[#2d2f33] hover:text-black dark:hover:text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
className="absolute top-3 right-3 z-10 flex size-7 items-center justify-center rounded-full text-[#383835] dark:text-[#c7c7c4] transition-colors hover:bg-[#ececec] dark:hover:bg-[#3a3d43] hover:text-black dark:hover:text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
aria-label={t("settings.dialog.closeAriaLabel")}
>
<HugeiconsIcon icon={Cancel01Icon} className="size-4" />

View file

@ -332,7 +332,7 @@ export function GuidedTour({
<Button
variant="ghost"
size="icon-sm"
className="text-foreground/60 hover:text-foreground hover:bg-black/[0.05] dark:text-zinc-300/70 dark:hover:text-zinc-100 dark:hover:bg-white/[0.08]"
className="text-foreground/60 hover:text-foreground hover:bg-black/[0.05] dark:text-zinc-300/70 dark:hover:text-zinc-100 dark:hover:bg-white/[0.1]"
onClick={() => requestClose("skip")}
aria-label="Skip tour"
>
@ -343,7 +343,7 @@ export function GuidedTour({
<div className="mt-5 flex items-center justify-between gap-3">
<Button
variant="ghost"
className="text-foreground/60 hover:text-foreground hover:bg-black/[0.05] dark:text-zinc-300/70 dark:hover:text-zinc-100 dark:hover:bg-white/[0.08]"
className="text-foreground/60 hover:text-foreground hover:bg-black/[0.05] dark:text-zinc-300/70 dark:hover:text-zinc-100 dark:hover:bg-white/[0.1]"
onClick={() => requestClose("skip")}
>
Skip

View file

@ -113,7 +113,8 @@
--ease-out-quart: cubic-bezier(0.165, 0.84, 0.44, 1);
--ease-out-cubic: cubic-bezier(0.215, 0.61, 0.355, 1);
--background: oklch(1 0 0);
/* Slightly warm off white; every page (and the chat surface) shares it. */
--background: #fefefd;
--foreground: oklch(0.2686 0 0);
--card: oklch(1 0 0);
--card-foreground: oklch(0.1281 0.0179 169.2764);
@ -137,9 +138,9 @@
--chart-4: oklch(0.6926 0.1112 346.5775);
--chart-5: oklch(0.7497 0.1003 85.0057);
--radius: 1.1rem;
/* Match the page background so the sidebar reads as one surface with the
content; the faint --sidebar-border on its right edge is the separator. */
--sidebar: oklch(1 0 0);
/* White sidebar against the warm off-white page; the tone difference is
the separator now that the right-edge divider is gone. */
--sidebar: #ffffff;
--sidebar-foreground: oklch(0.1281 0.0179 169.2764);
--sidebar-primary: #17b88b;
--sidebar-primary-foreground: oklch(1 0 0);
@ -233,7 +234,8 @@
/* Exact palette from apps/studio/index_chat.html mockup. Using hex so the
rendered surface matches the mockup pixel-for-pixel OKLCH conversion
drifted ~3% darker and shifted the neutral hue. */
--background: #1f2023;
/* Slightly darker than --sidebar so the sidebar reads as its own surface. */
--background: #1a1b1e;
--foreground: #ececee;
--card: #2d2e32;
--card-foreground: #ececee;
@ -245,7 +247,8 @@
--secondary-foreground: #ececee;
--muted: #2e3035;
--muted-foreground: #999999;
--accent: #2e3035;
/* One step lighter than --popover so menu item hover reads clearly. */
--accent: #3a3d43;
--accent-foreground: #ececee;
--destructive: oklch(0.6368 0.2078 25.3313);
/* --border / --input one step lighter than --muted so outlines and form
@ -259,14 +262,14 @@
--chart-3: oklch(0.7554 0.1285 197.339);
--chart-4: oklch(0.7503 0.1199 346.7805);
--chart-5: oklch(0.799 0.1196 84.6633);
/* Match the page background (dark); --sidebar-border is the right-edge separator. */
/* A step lighter than --background; the tone difference is the separator. */
--sidebar: #1f2023;
--sidebar-foreground: #ececee;
--sidebar-primary: #17b88b;
--sidebar-primary-foreground: oklch(1 0 0);
--sidebar-accent: #2f2f31;
--sidebar-accent: #2e3035;
--sidebar-accent-foreground: #ececee;
--sidebar-border: #2d2d2f;
--sidebar-border: #2d2e32;
--sidebar-ring: #17b88b;
--destructive-foreground: oklch(1 0 0);
/* Match light's radius so every rounded-* element is the same in both themes. */
@ -293,7 +296,7 @@
--nav-fg: #c7c7c4;
--nav-fg-muted: #96979b;
--nav-surface-hover: #2e2e30;
--nav-surface-hover: #2e3035;
--nav-icon-idle: #5c5c5c;
--nav-beta-border: #3a3c3f;
--panel-surface-hover: #3a3c42;
@ -305,7 +308,7 @@
--panel-surface: var(--background);
--panel-surface-fg: var(--foreground);
--panel-input-surface: #2a2b2e;
--panel-input-surface-hover: #2e3033;
--panel-input-surface-hover: #34373c;
/* Soft neutral gray for muted text and sliders. Pure-ish #ababab
reads as quiet on the dark panel without going colored. */
--panel-surface-fg-muted: #ababab;
@ -618,7 +621,7 @@
prompt button, and the chat-template textarea so all three read
as one quiet gray family, matching the focused-edit-number tint. */
.panel-input-group {
@apply !h-9 min-h-9 min-w-0 items-stretch gap-0 rounded-[10px] pr-0 transition-colors focus-within:ring-0 focus-within:shadow-none;
@apply !h-9 min-h-9 min-w-0 items-stretch gap-0 rounded-[14px] pr-0 transition-colors focus-within:ring-0 focus-within:shadow-none;
border: 0 !important;
background-color: var(--panel-input-surface);
}
@ -714,13 +717,12 @@
/* Inline numeric input used for slider values and Context Length.
Designed to read as *editable text* rather than a pill button so
it doesn't mirror the Select/dropdown components on the panel.
Default: transparent box, no border, no ring, sized by the
`size` HTML attribute (each consumer picks 6 / 8 / etc. chars).
Hover/focus: very light bg fade-in to signal editability just
enough to read as interactive, not enough to compete with the
slider row's quiet aesthetic. */
Default: transparent box, no border, no ring; the component sets
an inline width that hugs the value. Hover/focus: very light bg
fade-in, just enough to read as interactive without competing
with the slider row's quiet aesthetic. */
.panel-number-input {
@apply h-7 rounded-md border-0 bg-transparent px-1.5 text-right text-[13px]! font-medium tabular-nums text-nav-fg shadow-none transition-colors hover:bg-black/[0.04] focus:bg-black/[0.05] focus-visible:ring-0! focus-visible:outline-none md:text-[13px]!;
@apply h-7 min-w-8 shrink-0 rounded-full border-0 bg-transparent px-2 text-right text-[13px]! font-medium tabular-nums text-nav-fg shadow-none transition-colors hover:bg-black/[0.04] focus:bg-black/[0.05] focus-visible:ring-0! focus-visible:outline-none md:text-[13px]!;
}
.dark .panel-number-input {
@apply hover:bg-white/[0.04] focus:bg-white/[0.06];
@ -779,7 +781,7 @@
height: 36px;
padding: 0 0.75rem !important;
gap: 9.5px !important;
border-radius: 12px;
border-radius: 9999px;
font-weight: 500;
font-size: 15px;
line-height: 20px;
@ -805,6 +807,9 @@
.dark .app-user-menu [data-slot="dropdown-menu-item"]:focus,
.dark .app-user-menu [data-slot="dropdown-menu-sub-trigger"]:focus,
.dark .app-user-menu [data-slot="dropdown-menu-sub-trigger"][data-state="open"] {
/* --nav-surface-hover is tuned for the page bg; on the lighter menu
surface it is invisible, so use the menu hover token instead. */
background-color: var(--accent);
color: #fff;
}
.app-user-menu [data-slot="dropdown-menu-item"]:focus *,
@ -837,14 +842,15 @@
/* Elevated surface shadow (use ring-* for borders) */
.shadow-border {
--tw-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
--tw-shadow-colored: 0 4px 16px var(--tw-shadow-color);
/* Chatbox shadow; dark mode drops it. */
--tw-shadow: 0 2px 8px -2px rgba(0, 0, 0, 0.16);
--tw-shadow-colored: 0 2px 8px -2px var(--tw-shadow-color);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000),
var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
}
.dark .shadow-border {
--tw-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
box-shadow: none;
}
/* Settings dialog: no shadow in dark mode. */
@ -852,6 +858,12 @@
box-shadow: none;
}
/* Lift the dialog off the page bg; halfway to --card keeps the inner
bg-muted / bg-background fills readable. */
.dark .settings-surface {
background-color: color-mix(in srgb, var(--background) 50%, var(--card));
}
/* App-wide: every clickable control shows the hand cursor. Disabled ones
(native :disabled, aria-disabled, Radix data-disabled) keep the default. */
button:not(:disabled):not([aria-disabled="true"]),
@ -893,7 +905,7 @@
both themes; rounded-4xl here and on the inner Command follow this. */
--radius: 0.625rem;
/* Match the chat box: soft elevation in light, none in dark. */
box-shadow: 0 2px 8px -2px rgba(27, 27, 31, 0.16);
box-shadow: 0 2px 8px -2px rgba(0, 0, 0, 0.16);
}
.dark .chat-search-surface {
@ -902,25 +914,29 @@
.menu-soft-surface,
.menu-soft-surface-up {
/* Light: chatbox shadow geometry. */
--menu-soft-edge: rgba(0, 0, 0, 0.14);
--menu-soft-shadow: rgba(0, 0, 0, 0.18);
--menu-soft-offset-y: 8px;
--menu-soft-blur: 28px;
--menu-soft-spread: -6px;
--menu-soft-shadow: rgba(0, 0, 0, 0.16);
--menu-soft-offset-y: 2px;
--menu-soft-blur: 8px;
--menu-soft-spread: -2px;
@apply bg-popover text-popover-foreground;
box-shadow:
inset 0 0 0 1px var(--menu-soft-edge),
0 var(--menu-soft-offset-y) var(--menu-soft-blur)
var(--menu-soft-spread) var(--menu-soft-shadow);
}
.menu-soft-surface-up {
--menu-soft-offset-y: -6px;
--menu-soft-spread: -8px;
}
.dark .menu-soft-surface,
.dark .menu-soft-surface-up {
--menu-soft-edge: rgba(255, 255, 255, 0.07);
--menu-soft-shadow: rgba(0, 0, 0, 0.28);
--menu-soft-offset-y: 8px;
--menu-soft-blur: 28px;
--menu-soft-spread: -6px;
}
.dark .menu-soft-surface-up {
--menu-soft-offset-y: -6px;
--menu-soft-spread: -8px;
}
/* Model selector: drop the inset edge ring, keep the soft drop shadow.
@ -935,6 +951,15 @@
box-shadow: none;
}
/* List menus: one radius in both themes, slightly tighter than --radius-lg
and matching the sidebar buttons. !important beats per-menu rounded-*. */
[data-slot="dropdown-menu-content"],
[data-slot="dropdown-menu-sub-content"],
[data-slot="select-content"],
[data-slot="combobox-content"] {
border-radius: 14px !important;
}
/* Every dropdown/menu/select/popover: borderless, chatbox shadow in light,
none in dark. !important so it also overrides the bespoke menu shadows. */
[data-slot="dropdown-menu-content"],
@ -942,7 +967,7 @@
[data-slot="select-content"],
[data-slot="combobox-content"],
[data-slot="popover-content"] {
box-shadow: 0 2px 8px -2px rgba(27, 27, 31, 0.16) !important;
box-shadow: 0 2px 8px -2px rgba(0, 0, 0, 0.16) !important;
}
/* Dark: keep the chat-box dropdown shadow so menus don't merge into a
same-color surface (e.g. a menu overlapping the composer). */
@ -954,6 +979,15 @@
box-shadow: 0 4px 14px var(--background) !important;
}
/* Model selector: shadow in the page-bg color so it lifts off the
composer it overlaps (chatbox geometry). Account menu stays flat. */
.dark .unsloth-model-selector-menu {
box-shadow: 0 2px 8px -2px var(--background) !important;
}
.dark .app-user-menu.menu-soft-surface-up {
box-shadow: none !important;
}
/* Context menus (chat, run, project): drop the inset edge ring, keep the soft shadow. */
.app-user-menu.menu-soft-surface {
box-shadow: 0 var(--menu-soft-offset-y) var(--menu-soft-blur)
@ -962,7 +996,7 @@
/* Account menu: drop the inset edge ring and use the composer's soft shadow. */
.app-user-menu.menu-soft-surface-up {
box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.09);
box-shadow: 0 2px 8px -2px rgba(0, 0, 0, 0.16);
}
.dark .app-user-menu.menu-soft-surface-up {
box-shadow: 0 8px 28px -6px rgba(0, 0, 0, 0.28);
@ -972,16 +1006,19 @@
@apply relative flex w-full flex-col rounded-[32px] bg-background dark:bg-card px-1 pt-2 outline-none transition-shadow;
font-family: var(--font-sans);
background-clip: padding-box;
box-shadow: 0 2px 8px -2px rgba(27, 27, 31, 0.16);
background-color: #ffffff;
/* Gemini-style composer shadow. */
box-shadow: 0 2px 8px -2px rgba(0, 0, 0, 0.16);
transition: box-shadow 0.1s;
}
.dark .chat-composer-surface {
background-color: #2a2a2c;
background-color: var(--card);
box-shadow: none;
}
.composer-pill-btn {
@apply flex cursor-pointer items-center gap-1.5 rounded-full py-1.5 pl-2 pr-2.5 text-[14px] font-medium text-muted-foreground/70 transition-colors hover:bg-primary/10 dark:hover:bg-white/[0.08] disabled:cursor-not-allowed disabled:opacity-40;
@apply flex cursor-pointer items-center gap-1.5 rounded-full py-1.5 pl-2 pr-2.5 text-[14px] font-medium text-muted-foreground/70 transition-colors hover:bg-primary/10 dark:hover:bg-white/[0.1] disabled:cursor-not-allowed disabled:opacity-40;
}
/* Icon-only (compact) pills drop their label, so make the button a square
@ -1058,16 +1095,18 @@
@apply relative flex w-full flex-col rounded-[32px] bg-background dark:bg-card px-3 py-3 outline-none transition-shadow;
font-family: var(--font-sans);
background-clip: padding-box;
/* Gemini search pill: soft, centered shadow, no downward offset. */
box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.09);
background-color: #ffffff;
/* Gemini-style composer shadow. */
box-shadow: 0 2px 8px -2px rgba(0, 0, 0, 0.16);
transition: box-shadow 0.1s;
}
.unsloth-composer-surface:focus-within {
box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.09);
box-shadow: 0 2px 8px -2px rgba(0, 0, 0, 0.16);
}
.dark .unsloth-composer-surface {
background-color: #2a2a2c;
background-color: var(--card);
box-shadow: none;
}
@ -1177,7 +1216,7 @@
/* Right-side Thinking pill (toggle or dropdown). pl-2 matches the left
pills so the hover X is not pushed in too far. */
.unsloth-thinking-pill {
@apply inline-flex shrink-0 cursor-pointer items-center gap-1 rounded-full py-1.5 pl-2 pr-2.5 text-[14px] font-medium text-muted-foreground transition-colors hover:bg-primary/10 dark:hover:bg-white/[0.08] disabled:cursor-not-allowed disabled:opacity-40;
@apply inline-flex shrink-0 cursor-pointer items-center gap-1 rounded-full py-1.5 pl-2 pr-2.5 text-[14px] font-medium text-muted-foreground transition-colors hover:bg-primary/10 dark:hover:bg-white/[0.1] disabled:cursor-not-allowed disabled:opacity-40;
/* Reserve a text line so the icon-only (inactive) pill matches the
text pills' height instead of collapsing to the icon. */
min-height: calc(1lh + 0.75rem);
@ -1206,9 +1245,7 @@
border-radius: 18px;
padding-top: 0.5rem;
padding-bottom: 0.5rem;
box-shadow:
0 1px 2px oklch(0 0 0 / 0.04),
0 6px 18px oklch(0 0 0 / 0.08);
box-shadow: 0 2px 8px -2px rgba(0, 0, 0, 0.16);
}
/* MCP menu is wider (232px), so nudge the radius up so it reads as round as
@ -1218,7 +1255,7 @@
}
.dark .unsloth-plus-menu[data-slot] {
background-color: #2a2a2c;
background-color: var(--card);
/* Shadow tinted to the page bg so it blends, not a dark halo. */
box-shadow: 0 4px 14px var(--background);
}
@ -1257,7 +1294,7 @@
[data-slot="dropdown-menu-item"],
[data-slot="dropdown-menu-sub-trigger"]
):is(:hover, :focus, :focus-visible, [data-highlighted], [data-state="open"]) {
background-color: rgba(255, 255, 255, 0.08) !important;
background-color: rgba(255, 255, 255, 0.1) !important;
}
/* Hover tints only the background; the item and every descendant (text, icons,
@ -1302,7 +1339,7 @@
white-space: nowrap;
}
.artifact-panel-shell {
box-shadow: 0 2px 8px -2px rgba(27, 27, 31, 0.16);
box-shadow: 0 2px 8px -2px rgba(0, 0, 0, 0.16);
}
.dark .artifact-panel-shell {
@ -1634,7 +1671,7 @@
!important because Sonner injects its base rules at runtime. */
[data-sonner-toast][data-styled='true'] {
padding: 12px 18px !important;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08) !important;
box-shadow: 0 2px 8px -2px rgba(0, 0, 0, 0.16) !important;
}
[data-sonner-toast][data-styled='true']:has([data-close-button]):not(:has([data-cancel])) {
@ -1671,7 +1708,7 @@
/* Boost shadow on dark surfaces; mirrors .shadow-border / .menu-soft-surface pattern. */
.dark [data-sonner-toast][data-styled='true'] {
background-color: #2a2a2c !important;
background-color: var(--card) !important;
box-shadow: none !important;
}
@ -1923,7 +1960,7 @@
/* Keep the (borderless) close button blended with the dark toast surface. */
.dark [data-sonner-toast][data-styled="true"] [data-close-button] {
background: #2a2a2c !important;
background: var(--card) !important;
}
[data-sonner-toast][data-styled="true"] [data-close-button] svg {