style(studio): polish chat navigation and composer

This commit is contained in:
wasimysaid 2026-05-30 21:02:49 +02:00
commit 14384cd4e5
5 changed files with 119 additions and 70 deletions

View file

@ -525,53 +525,6 @@ export function AppSidebar() {
</SidebarGroupContent>
</SidebarGroup>
<SidebarGroup
data-tour="navbar"
className="group-data-[collapsible=icon]:px-0 px-2 pt-[9px] pb-[20px] shrink-0"
>
<SidebarGroupContent>
<SidebarMenu>
<NavItem
icon={TestTubeOutlineIcon}
label={t("shell.navigation.train")}
active={
pathname === "/studio" || pathname.startsWith("/studio/")
}
disabled={chatOnly}
onClick={() => {
if (chatOnly) return;
navigate({ to: "/studio" });
closeMobileIfOpen();
}}
/>
<NavItem
icon={ChefHatIcon}
label={t("shell.navigation.recipes")}
active={isRecipesRoute}
onClick={() => {
navigate({ to: "/data-recipes" });
closeMobileIfOpen();
}}
/>
<NavItem
icon={DownloadSquare01Icon}
label={t("shell.navigation.export")}
active={
pathname === "/export" || pathname.startsWith("/export/")
}
disabled={chatOnly}
onClick={() => {
if (chatOnly) return;
navigate({ to: "/export" });
closeMobileIfOpen();
}}
/>
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
<SidebarContent
ref={scrollRef}
className={cn(
@ -580,6 +533,53 @@ export function AppSidebar() {
canScrollDown && "can-scroll-down",
)}
>
<SidebarGroup
data-tour="navbar"
className="group-data-[collapsible=icon]:px-0 px-2 pt-[9px] pb-[20px]"
>
<SidebarGroupContent>
<SidebarMenu>
<NavItem
icon={TestTubeOutlineIcon}
label={t("shell.navigation.train")}
active={
pathname === "/studio" || pathname.startsWith("/studio/")
}
disabled={chatOnly}
onClick={() => {
if (chatOnly) return;
navigate({ to: "/studio" });
closeMobileIfOpen();
}}
/>
<NavItem
icon={ChefHatIcon}
label={t("shell.navigation.recipes")}
active={isRecipesRoute}
onClick={() => {
navigate({ to: "/data-recipes" });
closeMobileIfOpen();
}}
/>
<NavItem
icon={DownloadSquare01Icon}
label={t("shell.navigation.export")}
active={
pathname === "/export" || pathname.startsWith("/export/")
}
disabled={chatOnly}
onClick={() => {
if (chatOnly) return;
navigate({ to: "/export" });
closeMobileIfOpen();
}}
/>
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
{!isStudioRoute && chatItems.length > 0 && (
<Collapsible
key={isChatRoute ? "chat-route" : "non-chat-route"}
@ -795,7 +795,12 @@ export function AppSidebar() {
)}
</SidebarContent>
<SidebarFooter className="border-t border-sidebar-border group-data-[collapsible=icon]:border-transparent group-data-[collapsible=icon]:px-0">
<SidebarFooter
className={cn(
"app-sidebar-footer group-data-[collapsible=icon]:px-0",
canScrollDown && "can-scroll-down",
)}
>
<SidebarMenu>
<SidebarMenuItem>
<DropdownMenu>

View file

@ -1476,7 +1476,7 @@ const ComposerToolsMenu: FC<{ side?: "top" | "bottom" }> = ({
aria-label="Tools and attachments"
className="unsloth-composer-plus"
>
<PlusIcon className="size-6 stroke-[1.75px]" />
<PlusIcon className="size-[22px] stroke-2" />
</button>
</DropdownMenuTrigger>
<DropdownMenuContent
@ -1649,10 +1649,10 @@ const ComposerRightControls: FC<{
event.preventDefault();
}
}}
className="aui-composer-send size-8 rounded-full disabled:bg-transparent disabled:text-foreground/40 disabled:opacity-100 disabled:pointer-events-none"
className="aui-composer-send composer-send-enter size-8 rounded-full disabled:bg-transparent disabled:text-foreground/40 disabled:opacity-100 disabled:pointer-events-none"
aria-label="Send message"
>
<ArrowUpIcon className="aui-composer-send-icon size-6 stroke-[1.75px]" />
<ArrowUpIcon className="aui-composer-send-icon size-[22px] stroke-2" />
</TooltipIconButton>
</ComposerPrimitive.Send>
</AuiIf>

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",
"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

@ -1078,7 +1078,7 @@ export function SharedComposer({
aria-label="Tools and attachments"
className="unsloth-composer-plus"
>
<PlusIcon className="size-6 stroke-[1.75px]" />
<PlusIcon className="size-[22px] stroke-2" />
</button>
</DropdownMenuTrigger>
<DropdownMenuContent
@ -1542,12 +1542,12 @@ export function SharedComposer({
side="bottom"
variant="default"
size="icon"
className="size-8 rounded-full disabled:bg-transparent disabled:text-foreground/40 disabled:opacity-100 disabled:pointer-events-none"
className="composer-send-enter size-8 rounded-full disabled:bg-transparent disabled:text-foreground/40 disabled:opacity-100 disabled:pointer-events-none"
onClick={send}
disabled={!canSend}
aria-label="Send message"
>
<ArrowUpIcon className="size-6 stroke-[1.75px]" />
<ArrowUpIcon className="size-[22px] stroke-2" />
</TooltipIconButton>
) : null}
</div>

View file

@ -537,26 +537,30 @@
}
.sidebar-sticky-label {
@apply sticky top-0 z-20 rounded-none bg-sidebar pt-0 pb-1.5 pl-[18px] pr-4 text-[13px]! font-medium normal-case tracking-[0.04em] text-nav-fg-muted focus-visible:ring-0! focus-visible:outline-none shadow-[0_-8px_0_0_var(--sidebar)] transition-shadow duration-150;
}
.sidebar-sticky-label.is-scrolled {
@apply shadow-[0_-8px_0_0_var(--sidebar),0_0.5px_0_0_var(--sidebar-border)];
@apply rounded-none bg-sidebar pt-0 pb-1.5 pl-[18px] pr-4 text-[13px]! font-medium normal-case tracking-[0.04em] text-nav-fg-muted focus-visible:ring-0! focus-visible:outline-none;
}
.app-sidebar-top-actions {
position: relative;
z-index: 25;
transition: box-shadow var(--duration-fast) ease-out;
background: var(--sidebar);
}
.app-sidebar-top-actions.is-scrolled {
box-shadow: 0 0.5px 0 0 var(--sidebar-border), 0 10px 18px -18px
rgba(0, 0, 0, 0.42);
.app-sidebar-top-actions::after {
content: "";
position: absolute;
left: 0;
right: 0;
bottom: -16px;
height: 16px;
pointer-events: none;
opacity: 0;
background: linear-gradient(to bottom, var(--sidebar) 0, transparent 100%);
transition: opacity var(--duration-fast) ease-out;
}
.dark .app-sidebar-top-actions.is-scrolled {
box-shadow: 0 0.5px 0 0 var(--sidebar-border), 0 12px 20px -18px
rgba(0, 0, 0, 0.72);
.app-sidebar-top-actions.is-scrolled::after {
opacity: 1;
}
.app-sidebar-scroll-region {
@ -588,6 +592,23 @@
);
}
.app-sidebar-footer {
position: relative;
z-index: 25;
background: var(--sidebar);
}
.app-sidebar-footer.can-scroll-down::before {
content: "";
position: absolute;
left: 0;
right: 0;
top: -24px;
height: 24px;
pointer-events: none;
background: linear-gradient(to bottom, transparent 0, var(--sidebar) 100%);
}
/* Neutral panel input surface sidesteps the green cast on
`--input` / `--border` (both have a small chroma at hue ~165 in
light mode). Same value drives the preset input pill, the system
@ -891,6 +912,28 @@
display: inline-flex;
}
.composer-send-enter {
animation: composer-send-enter 120ms var(--ease-out-quart) both;
}
@keyframes composer-send-enter {
from {
opacity: 0;
transform: scale(0.92);
}
to {
opacity: 1;
transform: scale(1);
}
}
@media (prefers-reduced-motion: reduce) {
.composer-send-enter {
animation: none;
}
}
.composer-input {
@apply mb-1 min-h-10 w-full resize-none overflow-y-auto bg-transparent px-1.5 py-2 text-[15px] font-[450] leading-6 outline-none placeholder:text-muted-foreground focus-visible:ring-0;
}
@ -989,13 +1032,16 @@
}
.unsloth-composer-plus[data-state="open"] {
@apply bg-muted-foreground/15;
/* Radix's modal menu sets body pointer-events:none; re-enable on the open
trigger so the cursor and click-to-close work. */
pointer-events: auto;
cursor: pointer;
}
.unsloth-composer-plus[data-state="open"]:hover {
background-color: transparent;
}
.unsloth-composer-plus[data-state="open"] svg {
transform: rotate(35deg);
}
@ -1055,8 +1101,7 @@
) {
@apply gap-3 pl-4 pr-3 py-2 text-[14px];
cursor: pointer;
/* Pin hover-box radius so dark matches light (same as the container). */
border-radius: 1.1rem;
border-radius: 12px;
}
.unsloth-plus-menu [data-slot="dropdown-menu-label"] {