mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-01 12:02:05 +00:00
fix(web): polish dashboard sidebar chrome and model card menus
Use momentum easing for sidebar transitions, switch sidebar typography to sans-serif, replace the profile native select with the DS Select, and stop clipping the Models page Use-as dropdown inside model cards. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
10374bb7a2
commit
1abf0c6cbf
7 changed files with 52 additions and 39 deletions
|
|
@ -544,13 +544,13 @@ export default function App() {
|
|||
id="app-sidebar"
|
||||
aria-label={t.app.navigation}
|
||||
className={cn(
|
||||
"fixed top-0 left-0 z-50 flex h-dvh max-h-dvh w-64 min-h-0 flex-col",
|
||||
"fixed top-0 left-0 z-50 flex h-dvh max-h-dvh w-64 min-h-0 flex-col font-sans",
|
||||
"border-r border-current/20",
|
||||
"bg-background-base",
|
||||
"transition-[transform] duration-200 ease-out",
|
||||
"transition-[transform] duration-200 ease-[cubic-bezier(0.23,1,0.32,1)]",
|
||||
mobileOpen ? "translate-x-0" : "-translate-x-full",
|
||||
"lg:sticky lg:top-0 lg:translate-x-0 lg:shrink-0 lg:overflow-hidden",
|
||||
"lg:transition-[width] lg:duration-[600ms] lg:ease-[cubic-bezier(0.33,1.35,0.62,1)]",
|
||||
"lg:transition-[width] lg:duration-300 lg:ease-[cubic-bezier(0.23,1,0.32,1)]",
|
||||
collapsed && "lg:w-14",
|
||||
)}
|
||||
style={{
|
||||
|
|
@ -636,7 +636,7 @@ export default function App() {
|
|||
<span
|
||||
className={cn(
|
||||
"px-5 pt-2.5 pb-1",
|
||||
"font-mondwest text-display text-xs tracking-[0.12em] text-text-tertiary",
|
||||
"font-sans text-display text-xs tracking-[0.12em] text-text-tertiary",
|
||||
isDesktopCollapsed && "lg:hidden",
|
||||
)}
|
||||
id="hermes-sidebar-plugin-nav-heading"
|
||||
|
|
@ -843,7 +843,7 @@ function SidebarNavLink({
|
|||
cn(
|
||||
"group/nav relative flex items-center gap-3",
|
||||
"px-5 py-2.5",
|
||||
"font-mondwest text-display uppercase text-sm tracking-[0.12em]",
|
||||
"font-sans text-display uppercase text-sm tracking-[0.12em]",
|
||||
"whitespace-nowrap transition-colors cursor-pointer",
|
||||
"focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-midground",
|
||||
isActive
|
||||
|
|
@ -939,7 +939,7 @@ function SidebarSystemActions({
|
|||
<span
|
||||
className={cn(
|
||||
"px-5 pt-0.5 pb-0.5",
|
||||
"font-mondwest text-display text-xs tracking-[0.12em] text-text-tertiary",
|
||||
"font-sans text-display text-xs tracking-[0.12em] text-text-tertiary",
|
||||
collapsed && "lg:hidden",
|
||||
)}
|
||||
>
|
||||
|
|
@ -1009,7 +1009,7 @@ function SystemActionButton({
|
|||
className={cn(
|
||||
"group/action relative flex w-full items-center gap-3",
|
||||
"px-5 py-2.5",
|
||||
"font-mondwest text-display text-xs tracking-[0.1em]",
|
||||
"font-sans text-display text-xs tracking-[0.1em]",
|
||||
"whitespace-nowrap transition-colors cursor-pointer",
|
||||
"focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-midground",
|
||||
busy
|
||||
|
|
@ -1183,7 +1183,7 @@ function SidebarTooltip({ anchor, label, warmRef }: SidebarTooltipProps) {
|
|||
"fixed z-[100] pointer-events-none",
|
||||
"px-2 py-1",
|
||||
"bg-background-base border border-current/20 shadow-lg",
|
||||
"font-mondwest text-display text-xs tracking-[0.1em] text-midground uppercase",
|
||||
"font-sans text-display text-xs tracking-[0.1em] text-midground uppercase",
|
||||
)}
|
||||
style={{
|
||||
top: rect.top + rect.height / 2,
|
||||
|
|
|
|||
|
|
@ -78,7 +78,6 @@ export function LanguageSwitcher({ collapsed = false, dropUp = false }: Language
|
|||
>
|
||||
<span className="inline-flex items-center gap-1.5">
|
||||
<Typography
|
||||
mondwest
|
||||
className="hidden sm:inline text-display tracking-wide text-xs"
|
||||
>
|
||||
{locale === "en" ? "EN" : current.name}
|
||||
|
|
@ -151,7 +150,7 @@ function LanguageSwitcherOptions({
|
|||
aria-selected={selected}
|
||||
className={cn(
|
||||
"w-full text-left px-3 py-1.5 flex items-center gap-2 cursor-pointer",
|
||||
"font-mondwest text-display text-xs tracking-[0.08em]",
|
||||
"font-sans text-display text-xs tracking-[0.08em]",
|
||||
"hover:bg-accent hover:text-accent-foreground transition-colors",
|
||||
selected ? "font-semibold text-foreground" : "text-muted-foreground",
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,9 @@
|
|||
import { useMemo } from "react";
|
||||
import { Users } from "lucide-react";
|
||||
import {
|
||||
Select,
|
||||
SelectOption,
|
||||
} from "@nous-research/ui/ui/components/select";
|
||||
import { useProfileScope } from "@/contexts/useProfileScope";
|
||||
import { useI18n } from "@/i18n";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
|
@ -10,14 +15,24 @@ import { cn } from "@/lib/utils";
|
|||
* Keys, Skills, MCP, Models) reads/writes the selected profile via the
|
||||
* fetchJSON ?profile= injection. Hidden when only one profile exists.
|
||||
*/
|
||||
export function ProfileSwitcher({ collapsed }: { collapsed?: boolean }) {
|
||||
export function ProfileSwitcher({ collapsed }: ProfileSwitcherProps) {
|
||||
const { profile, currentProfile, profiles, setProfile } = useProfileScope();
|
||||
const { t } = useI18n();
|
||||
|
||||
const currentDashboardLabel = useMemo(
|
||||
() =>
|
||||
(t.app.currentProfileOption ?? "this dashboard ({name})").replace(
|
||||
"{name}",
|
||||
currentProfile || "default",
|
||||
),
|
||||
[currentProfile, t.app.currentProfileOption],
|
||||
);
|
||||
|
||||
if (profiles.length < 2) return null;
|
||||
|
||||
const managed = profile || currentProfile || "default";
|
||||
const isOther = !!profile && profile !== currentProfile;
|
||||
const managingLabel = t.app.managingProfile ?? "Managing profile";
|
||||
|
||||
return (
|
||||
<div
|
||||
|
|
@ -25,7 +40,7 @@ export function ProfileSwitcher({ collapsed }: { collapsed?: boolean }) {
|
|||
"flex items-center gap-2 border-b border-current/10 px-3 py-2",
|
||||
collapsed && "lg:justify-center lg:px-0",
|
||||
)}
|
||||
title={t.app.managingProfile ?? "Managing profile"}
|
||||
title={managingLabel}
|
||||
>
|
||||
<Users
|
||||
className={cn(
|
||||
|
|
@ -33,35 +48,38 @@ export function ProfileSwitcher({ collapsed }: { collapsed?: boolean }) {
|
|||
isOther ? "text-amber-300" : "text-text-tertiary",
|
||||
)}
|
||||
/>
|
||||
<select
|
||||
aria-label={t.app.managingProfile ?? "Managing profile"}
|
||||
|
||||
<Select
|
||||
className={cn(
|
||||
"h-7 w-full min-w-0 rounded-none border bg-background px-1 text-xs",
|
||||
isOther
|
||||
? "border-amber-500/50 text-amber-300"
|
||||
: "border-border text-text-secondary",
|
||||
"min-w-0 flex-1",
|
||||
collapsed && "lg:hidden",
|
||||
"[&_button]:h-7 [&_button]:border-border [&_button]:bg-background [&_button]:px-2 [&_button]:text-xs",
|
||||
"[&_button]:font-sans [&_button]:normal-case [&_button]:tracking-normal",
|
||||
"[&_[role=listbox]>div]:font-sans [&_[role=listbox]>div]:text-xs",
|
||||
"[&_[role=listbox]>div]:normal-case [&_[role=listbox]>div]:tracking-normal",
|
||||
isOther &&
|
||||
"[&_button]:border-amber-500/50 [&_button]:text-amber-300",
|
||||
)}
|
||||
id="hermes-profile-switcher"
|
||||
onValueChange={setProfile}
|
||||
value={profile}
|
||||
onChange={(e) => setProfile(e.target.value)}
|
||||
>
|
||||
<option value="">
|
||||
{(t.app.currentProfileOption ?? "this dashboard ({name})").replace(
|
||||
"{name}",
|
||||
currentProfile || "default",
|
||||
)}
|
||||
</option>
|
||||
<SelectOption value="">{currentDashboardLabel}</SelectOption>
|
||||
|
||||
{profiles
|
||||
.filter((name) => name !== currentProfile)
|
||||
.map((name) => (
|
||||
<option key={name} value={name}>
|
||||
<SelectOption key={name} value={name}>
|
||||
{name}
|
||||
</option>
|
||||
</SelectOption>
|
||||
))}
|
||||
</select>
|
||||
{collapsed && (
|
||||
<span className="sr-only">{managed}</span>
|
||||
)}
|
||||
</Select>
|
||||
|
||||
{collapsed && <span className="sr-only">{managed}</span>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface ProfileSwitcherProps {
|
||||
collapsed?: boolean;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ export function SidebarFooter({ status }: SidebarFooterProps) {
|
|||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className={cn(
|
||||
"font-mondwest text-display text-xs tracking-[0.12em] text-midground",
|
||||
"font-sans text-display text-xs tracking-[0.12em] text-midground",
|
||||
"transition-opacity hover:opacity-90",
|
||||
"focus-visible:rounded-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-midground/40",
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ export function SidebarStatusStrip({ status }: SidebarStatusStripProps) {
|
|||
"focus-visible:ring-inset",
|
||||
)}
|
||||
>
|
||||
<div className="flex flex-col gap-1 font-mondwest text-xs leading-snug tracking-[0.08em]">
|
||||
<div className="flex flex-col gap-1 font-sans text-xs leading-snug tracking-[0.08em]">
|
||||
<p className="break-words">
|
||||
<span className="text-text-tertiary">{gatewayStatusLabel}</span>{" "}
|
||||
<span className={cn("font-medium", gw.tone)}>{gw.label}</span>
|
||||
|
|
|
|||
|
|
@ -81,7 +81,6 @@ export function ThemeSwitcher({ collapsed = false, dropUp = false }: ThemeSwitch
|
|||
|
||||
{!collapsed && (
|
||||
<Typography
|
||||
mondwest
|
||||
className="hidden sm:inline text-display tracking-wide text-xs"
|
||||
>
|
||||
{label}
|
||||
|
|
@ -134,7 +133,6 @@ export function ThemeSwitcher({ collapsed = false, dropUp = false }: ThemeSwitch
|
|||
>
|
||||
<div className="border-b border-current/20 px-3 py-2">
|
||||
<Typography
|
||||
mondwest
|
||||
className="text-display text-xs tracking-[0.12em] text-text-tertiary"
|
||||
>
|
||||
{sheetTitle}
|
||||
|
|
@ -192,7 +190,6 @@ function ThemeSwitcherOptions({
|
|||
|
||||
<div className="flex min-w-0 flex-1 flex-col gap-0.5">
|
||||
<Typography
|
||||
mondwest
|
||||
className="truncate text-display text-xs tracking-wide"
|
||||
>
|
||||
{th.label}
|
||||
|
|
@ -235,7 +232,6 @@ function FontSection({ fontChoices, fontId, setFont }: FontSectionProps) {
|
|||
<span className="inline-flex items-center gap-1.5">
|
||||
<Type className="h-3 w-3 text-text-tertiary" />
|
||||
<Typography
|
||||
mondwest
|
||||
className="text-display text-xs tracking-[0.12em] text-text-tertiary"
|
||||
>
|
||||
{t.theme?.fontTitle ?? "Font"}
|
||||
|
|
|
|||
|
|
@ -268,7 +268,7 @@ function UseAsMenu({
|
|||
}, [open]);
|
||||
|
||||
return (
|
||||
<div className="relative" data-use-as-menu>
|
||||
<div className={cn("relative", open && "z-20")} data-use-as-menu>
|
||||
<Button
|
||||
size="sm"
|
||||
outlined
|
||||
|
|
@ -392,7 +392,7 @@ function ModelCard({
|
|||
|
||||
return (
|
||||
<Card
|
||||
className={`min-w-0 max-w-full overflow-hidden${isMain ? " ring-1 ring-primary/40" : ""}`}
|
||||
className={cn("min-w-0 max-w-full", isMain && "ring-1 ring-primary/40")}
|
||||
>
|
||||
<CardHeader className="pb-3">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue