chore: navbar refinements

This commit is contained in:
Lluis Agusti
2025-07-02 16:37:23 +04:00
parent 8a8d897e3b
commit 18ab6fd298
13 changed files with 461 additions and 441 deletions

View File

@@ -1,15 +1,15 @@
"use client";
import Link from "next/link";
import { Alert, AlertDescription } from "@/components/ui/alert";
import {
ArrowBottomRightIcon,
QuestionMarkCircledIcon,
} from "@radix-ui/react-icons";
import { Alert, AlertDescription } from "@/components/ui/alert";
import { LibraryPageStateProvider } from "./components/state-provider";
import LibraryActionHeader from "./components/LibraryActionHeader/LibraryActionHeader";
import LibraryAgentList from "./components/LibraryAgentList/LibraryAgentList";
import { LibraryPageStateProvider } from "./components/state-provider";
/**
* LibraryPage Component
@@ -17,7 +17,7 @@ import LibraryAgentList from "./components/LibraryAgentList/LibraryAgentList";
*/
export default function LibraryPage() {
return (
<main className="container min-h-screen space-y-4 pb-20 sm:px-8 md:px-12">
<main className="pt-160sm:px-8 container min-h-screen space-y-4 pb-20 pt-16 md:px-12">
<LibraryPageStateProvider>
<LibraryActionHeader />
<LibraryAgentList />

View File

@@ -1,82 +1,78 @@
import { cn } from "@/lib/utils";
import { CircleNotchIcon } from "@phosphor-icons/react/dist/ssr";
import { cva, type VariantProps } from "class-variance-authority";
import Link, { type LinkProps } from "next/link";
import React from "react";
import { ButtonProps, extendedButtonVariants } from "./helpers";
// Extended button variants based on our design system
const extendedButtonVariants = cva(
"inline-flex items-center justify-center whitespace-nowrap font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-neutral-950 disabled:pointer-events-none disabled:opacity-50 font-sans leading-snug border",
{
variants: {
variant: {
primary:
"bg-zinc-800 border-zinc-800 text-white hover:bg-zinc-900 hover:border-zinc-900 rounded-full disabled:text-white disabled:bg-zinc-200 disabled:border-zinc-200 disabled:opacity-1",
secondary:
"bg-zinc-100 border-zinc-100 text-black hover:bg-zinc-300 hover:border-zinc-300 rounded-full disabled:text-zinc-300 disabled:bg-zinc-50 disabled:border-zinc-50 disabled:opacity-1",
destructive:
"bg-red-500 border-red-500 text-white hover:bg-red-600 hover:border-red-600 rounded-full disabled:text-white disabled:bg-zinc-200 disabled:border-zinc-200 disabled:opacity-1",
outline:
"bg-transparent border-zinc-700 text-black hover:bg-zinc-100 hover:border-zinc-700 rounded-full disabled:border-zinc-200 disabled:text-zinc-200 disabled:opacity-1",
ghost:
"bg-transparent border-transparent text-black hover:bg-zinc-50 hover:border-zinc-50 rounded-full disabled:text-zinc-200 disabled:opacity-1",
icon: "bg-white text-black border border-zinc-600 hover:bg-zinc-100 rounded-[96px] disabled:opacity-1",
},
size: {
small: "px-3 py-2 text-sm gap-1.5 h-[2.25rem]",
large: "min-w-20 px-4 py-3 text-sm gap-2",
icon: "p-3",
},
},
defaultVariants: {
variant: "primary",
size: "large",
},
},
);
export function Button(props: ButtonProps) {
const {
className,
variant,
size,
loading = false,
leftIcon,
rightIcon,
children,
as = "button",
...restProps
} = props;
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof extendedButtonVariants> {
loading?: boolean;
leftIcon?: React.ReactNode;
rightIcon?: React.ReactNode;
asChild?: boolean;
}
function Button({
className,
variant,
size,
loading = false,
leftIcon,
rightIcon,
children,
disabled,
...props
}: ButtonProps) {
const disabled = "disabled" in props ? props.disabled : false;
const isDisabled = disabled;
const buttonContent = (
<>
{loading && (
<CircleNotchIcon className="h-4 w-4 animate-spin" weight="bold" />
)}
{!loading && leftIcon}
{children}
{!loading && rightIcon}
</>
);
if (loading) {
return variant === "ghost" ? (
<button
const loadingClassName =
variant === "ghost"
? cn(
extendedButtonVariants({ variant, size, className }),
"pointer-events-none",
)
: cn(
extendedButtonVariants({ variant: "primary", size, className }),
"pointer-events-none border-zinc-500 bg-zinc-500 text-white",
);
return as === "NextLink" ? (
<Link
{...(restProps as LinkProps)}
className={loadingClassName}
aria-disabled="true"
>
<CircleNotchIcon className="h-4 w-4 animate-spin" weight="bold" />
{children}
</Link>
) : (
<button className={loadingClassName} disabled>
<CircleNotchIcon className="h-4 w-4 animate-spin" weight="bold" />
{children}
</button>
);
}
if (as === "NextLink") {
return (
<Link
{...(restProps as LinkProps)}
className={cn(
extendedButtonVariants({ variant, size, className }),
"pointer-events-none",
loading && "pointer-events-none",
isDisabled && "pointer-events-none opacity-50",
)}
aria-disabled={isDisabled}
>
<CircleNotchIcon className="h-4 w-4 animate-spin" weight="bold" />
{children}
</button>
) : (
<button
className={cn(
extendedButtonVariants({ variant: "primary", size, className }),
"pointer-events-none border-zinc-500 bg-zinc-500 text-white",
)}
>
<CircleNotchIcon className="h-4 w-4 animate-spin" weight="bold" />
{children}
</button>
{buttonContent}
</Link>
);
}
@@ -87,18 +83,9 @@ function Button({
loading && "pointer-events-none",
)}
disabled={isDisabled}
{...props}
{...(restProps as React.ButtonHTMLAttributes<HTMLButtonElement>)}
>
{loading && (
<CircleNotchIcon className="h-4 w-4 animate-spin" weight="bold" />
)}
{!loading && leftIcon}
{children}
{!loading && rightIcon}
{buttonContent}
</button>
);
}
Button.displayName = "Button";
export { Button, extendedButtonVariants };

View File

@@ -0,0 +1,55 @@
import { cva, VariantProps } from "class-variance-authority";
import { LinkProps } from "next/link";
// Extended button variants based on our design system
export const extendedButtonVariants = cva(
"inline-flex items-center justify-center whitespace-nowrap font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-neutral-950 disabled:pointer-events-none disabled:opacity-50 font-sans leading-snug border",
{
variants: {
variant: {
primary:
"bg-zinc-800 border-zinc-800 text-white hover:bg-zinc-900 hover:border-zinc-900 rounded-full disabled:text-white disabled:bg-zinc-200 disabled:border-zinc-200 disabled:opacity-1",
secondary:
"bg-zinc-100 border-zinc-100 text-black hover:bg-zinc-300 hover:border-zinc-300 rounded-full disabled:text-zinc-300 disabled:bg-zinc-50 disabled:border-zinc-50 disabled:opacity-1",
destructive:
"bg-red-500 border-red-500 text-white hover:bg-red-600 hover:border-red-600 rounded-full disabled:text-white disabled:bg-zinc-200 disabled:border-zinc-200 disabled:opacity-1",
outline:
"bg-transparent border-zinc-700 text-black hover:bg-zinc-100 hover:border-zinc-700 rounded-full disabled:border-zinc-200 disabled:text-zinc-200 disabled:opacity-1",
ghost:
"bg-transparent border-transparent text-black hover:bg-zinc-50 hover:border-zinc-50 rounded-full disabled:text-zinc-200 disabled:opacity-1",
icon: "bg-white text-black border border-zinc-600 hover:bg-zinc-100 rounded-[96px] disabled:opacity-1",
},
size: {
small: "px-3 py-2 text-sm gap-1.5 h-[2.25rem]",
large: "min-w-20 px-4 py-3 text-sm gap-2",
icon: "p-3",
},
},
defaultVariants: {
variant: "primary",
size: "large",
},
},
);
type BaseButtonProps = {
loading?: boolean;
leftIcon?: React.ReactNode;
rightIcon?: React.ReactNode;
asChild?: boolean;
} & VariantProps<typeof extendedButtonVariants>;
type ButtonAsButton = BaseButtonProps &
React.ButtonHTMLAttributes<HTMLButtonElement> & {
as?: "button";
href?: never;
};
type ButtonAsLink = BaseButtonProps &
Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, keyof LinkProps> &
LinkProps & {
as: "NextLink";
disabled?: boolean;
};
export type ButtonProps = ButtonAsButton | ButtonAsLink;

View File

@@ -1,16 +1,14 @@
import { IconAutoGPTLogo, IconType } from "@/components/ui/icons";
import { ProfileDetails } from "@/lib/autogpt-server-api/types";
import Link from "next/link";
import { MobileNavBar } from "../../agptui/MobileNavBar";
import Wallet from "../../agptui/Wallet";
import { AccountMenu } from "./components/AccountMenu/AccountMenu";
import { MobileNavBar } from "./components/MobileNavbar/MobileNavBar";
import { NavbarLink } from "./components/NavbarLink";
import { ProfilePopoutMenu } from "./components/ProfilePopoutMenu";
import BackendAPI from "@/lib/autogpt-server-api";
import { getServerUser } from "@/lib/supabase/server/getServerUser";
import { SignInIcon } from "@phosphor-icons/react/dist/ssr";
import { Button } from "../../atoms/Button/Button";
import { accountMeunItems, loggedInLinks } from "./helpers";
import { LoginButton } from "./components/LoginButton";
import { accountMenuItems, loggedInLinks, loggedOutLinks } from "./helpers";
async function getProfileData() {
const api = new BackendAPI();
@@ -31,12 +29,16 @@ export async function Navbar() {
return (
<>
<nav className="sticky top-0 z-40 mx-[16px] hidden h-16 items-center rounded-bl-2xl rounded-br-2xl border border-white/50 bg-white/5 py-3 pl-6 pr-3 backdrop-blur-[26px] dark:border-gray-700 dark:bg-gray-900 md:inline-flex">
<nav className="sticky top-0 z-40 mx-[16px] hidden h-16 items-center rounded-bl-2xl rounded-br-2xl border border-white/50 bg-white/5 p-3 backdrop-blur-[26px] dark:border-gray-700 dark:bg-gray-900 md:inline-flex">
{/* Left section */}
<div className="flex flex-1 items-center gap-6">
{loggedInLinks.map((link) => (
<NavbarLink key={link.name} name={link.name} href={link.href} />
))}
{isLoggedIn
? loggedInLinks.map((link) => (
<NavbarLink key={link.name} name={link.name} href={link.href} />
))
: loggedOutLinks.map((link) => (
<NavbarLink key={link.name} name={link.name} href={link.href} />
))}
</div>
{/* Centered logo */}
@@ -49,24 +51,15 @@ export async function Navbar() {
{isLoggedIn ? (
<div className="flex items-center gap-4">
{profile && <Wallet />}
<ProfilePopoutMenu
menuItemGroups={accountMeunItems}
<AccountMenu
userName={profile?.username}
userEmail={profile?.name}
avatarSrc={profile?.avatar_url}
menuItemGroups={accountMenuItems}
/>
</div>
) : (
<Link href="/login">
<Button
size="small"
className="flex items-center justify-end space-x-2"
leftIcon={<SignInIcon className="h-5 w-5" />}
variant="secondary"
>
Log In
</Button>
</Link>
<LoginButton />
)}
{/* <ThemeToggle /> */}
</div>
@@ -80,7 +73,7 @@ export async function Navbar() {
menuItemGroups={[
{
groupName: "Navigation",
items: links.map((link) => ({
items: loggedInLinks.map((link) => ({
icon:
link.name === "Marketplace"
? IconType.Marketplace
@@ -95,27 +88,13 @@ export async function Navbar() {
href: link.href,
})),
},
...menuItemGroups,
...accountMenuItems,
]}
userEmail={profile?.name}
avatarSrc={profile?.avatar_url}
/>
</div>
) : (
<Link
href="/login"
className="fixed right-4 top-4 z-50 mt-4 inline-flex h-8 items-center justify-end rounded-lg pr-4 md:hidden"
>
<Button
size="small"
className="flex items-center justify-end space-x-2"
leftIcon={<SignInIcon className="h-5 w-5" />}
variant="secondary"
>
Log In
</Button>
</Link>
)}
) : null}
</>
</>
);

View File

@@ -6,22 +6,11 @@ import {
} from "@/components/ui/popover";
import Link from "next/link";
import * as React from "react";
import { ProfilePopoutMenuLogoutButton } from "../../../agptui/ProfilePopoutMenuLogoutButton";
import { PublishAgentPopout } from "../../../agptui/composite/PublishAgentPopout";
import {
IconBuilder,
IconEdit,
IconLayoutDashboard,
IconLibrary,
IconLogOut,
IconMarketplace,
IconRefresh,
IconSettings,
IconType,
IconUploadCloud,
} from "../../../ui/icons";
import { ProfilePopoutMenuLogoutButton } from "../../../../agptui/ProfilePopoutMenuLogoutButton";
import { PublishAgentPopout } from "../../../../agptui/composite/PublishAgentPopout";
import { getAccountMenuOptionIcon, MenuItemGroup } from "../../helpers";
interface ProfilePopoutMenuProps {
interface Props {
userName?: string;
userEmail?: string;
avatarSrc?: string;
@@ -29,38 +18,14 @@ interface ProfilePopoutMenuProps {
menuItemGroups: MenuItemGroup[];
}
export function ProfilePopoutMenu({
export function AccountMenu({
userName,
userEmail,
avatarSrc,
menuItemGroups,
}: ProfilePopoutMenuProps) {
}: Props) {
const popupId = React.useId();
const getIcon = (icon: IconType) => {
const iconClass = "w-6 h-6";
switch (icon) {
case IconType.LayoutDashboard:
return <IconLayoutDashboard className={iconClass} />;
case IconType.UploadCloud:
return <IconUploadCloud className={iconClass} />;
case IconType.Edit:
return <IconEdit className={iconClass} />;
case IconType.Settings:
return <IconSettings className={iconClass} />;
case IconType.LogOut:
return <IconLogOut className={iconClass} />;
case IconType.Marketplace:
return <IconMarketplace className={iconClass} />;
case IconType.Library:
return <IconLibrary className={iconClass} />;
case IconType.Builder:
return <IconBuilder className={iconClass} />;
default:
return <IconRefresh className={iconClass} />;
}
};
return (
<Popover>
<PopoverTrigger asChild>
@@ -119,7 +84,7 @@ export function ProfilePopoutMenu({
className="inline-flex w-full items-center justify-start gap-2.5"
>
<div className="relative h-6 w-6">
{getIcon(item.icon)}
{getAccountMenuOptionIcon(item.icon)}
</div>
<div className="font-sans text-base font-medium leading-normal text-neutral-800 dark:text-neutral-200">
{item.text}
@@ -135,7 +100,7 @@ export function ProfilePopoutMenu({
trigger={
<div className="inline-flex w-full items-center justify-start gap-2.5">
<div className="relative h-6 w-6">
{getIcon(item.icon)}
{getAccountMenuOptionIcon(item.icon)}
</div>
<div className="font-sans text-base font-medium leading-normal text-neutral-800 dark:text-neutral-200">
{item.text}
@@ -157,7 +122,7 @@ export function ProfilePopoutMenu({
tabIndex={0}
>
<div className="relative h-6 w-6">
{getIcon(item.icon)}
{getAccountMenuOptionIcon(item.icon)}
</div>
<div className="font-sans text-base font-medium leading-normal text-neutral-800 dark:text-neutral-200">
{item.text}

View File

@@ -0,0 +1,25 @@
"use client";
import { Button } from "@/components/atoms/Button/Button";
import { SignInIcon } from "@phosphor-icons/react/dist/ssr";
import { usePathname } from "next/navigation";
export function LoginButton() {
const pathname = usePathname();
const isLoginPage = pathname.includes("/login");
if (isLoginPage) return null;
return (
<Button
as="NextLink"
href="/login"
size="small"
className="flex items-center justify-end space-x-2"
leftIcon={<SignInIcon className="h-5 w-5" />}
variant="secondary"
>
Log In
</Button>
);
}

View File

@@ -1,45 +1,26 @@
"use client";
import * as React from "react";
import Link from "next/link";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Button } from "@/components/ui/button";
import {
Popover,
PopoverTrigger,
PopoverContent,
PopoverPortal,
PopoverTrigger,
} from "@/components/ui/popover";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Separator } from "@/components/ui/separator";
import {
IconType,
IconMenu,
IconChevronUp,
IconEdit,
IconLayoutDashboard,
IconUploadCloud,
IconSettings,
IconLogOut,
IconMarketplace,
IconLibrary,
IconBuilder,
} from "../ui/icons";
import { AnimatePresence, motion } from "framer-motion";
import { Button } from "@/components/ui/button";
import { usePathname } from "next/navigation";
import * as React from "react";
import { IconChevronUp, IconMenu } from "../../../../ui/icons";
import { MenuItemGroup } from "../../helpers";
import { MobileNavbarMenuItem } from "./components/MobileNavbarMenuItem";
interface MobileNavBarProps {
userName?: string;
userEmail?: string;
avatarSrc?: string;
menuItemGroups: {
groupName?: string;
items: {
icon: IconType;
text: string;
href?: string;
onClick?: () => void;
}[];
}[];
menuItemGroups: MenuItemGroup[];
}
const Overlay = React.forwardRef<HTMLDivElement, { children: React.ReactNode }>(
@@ -49,76 +30,15 @@ const Overlay = React.forwardRef<HTMLDivElement, { children: React.ReactNode }>(
</div>
),
);
Overlay.displayName = "Overlay";
const PopoutMenuItem: React.FC<{
icon: IconType;
isActive: boolean;
text: React.ReactNode;
href?: string;
onClick?: () => void;
}> = ({ icon, isActive, text, href, onClick }) => {
const getIcon = (iconType: IconType) => {
const iconClass = "w-6 h-6 relative";
switch (iconType) {
case IconType.Marketplace:
return <IconMarketplace className={iconClass} />;
case IconType.Library:
return <IconLibrary className={iconClass} />;
case IconType.Builder:
return <IconBuilder className={iconClass} />;
case IconType.Edit:
return <IconEdit className={iconClass} />;
case IconType.LayoutDashboard:
return <IconLayoutDashboard className={iconClass} />;
case IconType.UploadCloud:
return <IconUploadCloud className={iconClass} />;
case IconType.Settings:
return <IconSettings className={iconClass} />;
case IconType.LogOut:
return <IconLogOut className={iconClass} />;
default:
return null;
}
};
const content = (
<div className="inline-flex w-full items-center justify-start gap-4 hover:rounded hover:bg-[#e0e0e0] dark:hover:bg-[#3a3a3a]">
{getIcon(icon)}
<div className="relative">
<div
className={`font-sans text-base font-normal leading-7 text-[#474747] dark:text-[#cfcfcf] ${isActive ? "font-semibold text-[#272727] dark:text-[#ffffff]" : "text-[#474747] dark:text-[#cfcfcf]"}`}
>
{text}
</div>
{isActive && (
<div className="absolute bottom-[-4px] left-0 h-[2px] w-full bg-[#272727] dark:bg-[#ffffff]"></div>
)}
</div>
</div>
);
if (onClick)
return (
<div className="w-full" onClick={onClick}>
{content}
</div>
);
if (href)
return (
<Link href={href} className="w-full">
{content}
</Link>
);
return content;
};
export const MobileNavBar: React.FC<MobileNavBarProps> = ({
export function MobileNavBar({
userName,
userEmail,
avatarSrc,
menuItemGroups,
}) => {
}: MobileNavBarProps) {
const [isOpen, setIsOpen] = React.useState(false);
const pathname = usePathname();
const parts = pathname.split("/");
@@ -173,7 +93,7 @@ export const MobileNavBar: React.FC<MobileNavBarProps> = ({
{menuItemGroups.map((group, groupIndex) => (
<React.Fragment key={groupIndex}>
{group.items.map((item, itemIndex) => (
<PopoutMenuItem
<MobileNavbarMenuItem
key={itemIndex}
icon={item.icon}
isActive={item.href === activeLink}
@@ -194,4 +114,4 @@ export const MobileNavBar: React.FC<MobileNavBarProps> = ({
</AnimatePresence>
</Popover>
);
};
}

View File

@@ -0,0 +1,55 @@
import { IconType } from "@/components/ui/icons";
import { cn } from "@/lib/utils";
import Link from "next/link";
import { getAccountMenuOptionIcon } from "../../../helpers";
interface Props {
icon: IconType;
isActive: boolean;
text: string;
href?: string;
onClick?: () => void;
}
export function MobileNavbarMenuItem({
icon,
isActive,
text,
href,
onClick,
}: Props) {
const content = (
<div className="inline-flex w-full items-center justify-start gap-4 hover:rounded hover:bg-[#e0e0e0] dark:hover:bg-[#3a3a3a]">
{getAccountMenuOptionIcon(icon)}
<div className="relative">
<div
className={cn(
"font-sans text-base font-normal leading-7",
isActive
? "font-semibold text-[#272727] dark:text-[#ffffff]"
: "text-[#474747] dark:text-[#cfcfcf]",
)}
>
{text}
</div>
{isActive && (
<div className="absolute bottom-[-4px] left-0 h-[2px] w-full bg-[#272727] dark:bg-[#ffffff]"></div>
)}
</div>
</div>
);
if (onClick)
return (
<div className="w-full" onClick={onClick}>
{content}
</div>
);
if (href)
return (
<Link href={href} className="w-full">
{content}
</Link>
);
return content;
}

View File

@@ -1,5 +1,7 @@
"use client";
import { IconLaptop } from "@/components/ui/icons";
import { cn } from "@/lib/utils";
import {
CubeIcon,
HouseIcon,
@@ -9,15 +11,16 @@ import Link from "next/link";
import { usePathname } from "next/navigation";
import { Text } from "../../../atoms/Text/Text";
interface NavbarLinkProps {
interface Props {
name: string;
href: string;
}
export const NavbarLink = ({ name, href }: NavbarLinkProps) => {
export function NavbarLink({ name, href }: Props) {
const pathname = usePathname();
const parts = pathname.split("/");
const activeLink = "/" + (parts.length > 2 ? parts[2] : parts[1]);
const isActive = activeLink === href;
return (
<Link
@@ -26,43 +29,42 @@ export const NavbarLink = ({ name, href }: NavbarLinkProps) => {
className="font-poppins text-[20px] leading-[28px]"
>
<div
className={`p-2 ${
activeLink === href
? "rounded-2xl bg-neutral-800 dark:bg-neutral-200"
: ""
} flex items-center justify-start gap-1`}
className={cn(
"flex items-center justify-start gap-1 p-2",
isActive &&
"rounded-small bg-neutral-800 p-2 transition-all duration-300 dark:bg-neutral-200",
)}
>
{href === "/marketplace" && (
<StorefrontIcon
className={`h-6 w-6 ${activeLink === href ? "text-white dark:text-black" : ""}`}
className={cn("h-6 w-6", isActive && "text-white dark:text-black")}
/>
)}
{href === "/build" && (
<CubeIcon
className={`h-6 w-6 ${activeLink === href ? "text-white dark:text-black" : ""}`}
className={cn("h-6 w-6", isActive && "text-white dark:text-black")}
/>
)}
{href === "/monitor" && (
<IconLaptop
className={`h-6 w-6 ${activeLink === href ? "text-white dark:text-black" : ""}`}
className={cn("h-6 w-6", isActive && "text-white dark:text-black")}
/>
)}
{href === "/library" && (
<HouseIcon
className={`h-6 w-6 ${activeLink === href ? "text-white dark:text-black" : ""}`}
className={cn("h-6 w-6", isActive && "text-white dark:text-black")}
/>
)}
<Text
variant="body-medium"
className={`hidden lg:block ${
activeLink === href
? "text-neutral-50 dark:text-neutral-900"
: "text-neutral-900 dark:text-neutral-50"
}`}
className={cn(
"hidden lg:block",
isActive ? "!text-white" : "!text-black",
)}
>
{name}
</Text>
</div>
</Link>
);
};
}

View File

@@ -1,86 +0,0 @@
import { Icon } from "@phosphor-icons/react";
import {
CloudArrowUp,
GearIcon,
SignOut,
SquaresFourIcon,
UserSquareIcon,
} from "@phosphor-icons/react/dist/ssr";
type Link = {
name: string;
href: string;
};
export const loggedInLinks: Link[] = [
{
name: "Home",
href: "/library",
},
{
name: "Marketplace",
href: "/marketplace",
},
{
name: "Build",
href: "/build",
},
];
export const loggedOutLinks: Link[] = [
{
name: "Marketplace",
href: "/marketplace",
},
];
type MenuItemGroup = {
items: {
icon: Icon;
text: string;
href?: string;
onClick?: () => void;
}[];
};
export const accountMeunItems: MenuItemGroup[] = [
{
items: [
{
icon: UserSquareIcon,
text: "Edit profile",
href: "/profile",
},
],
},
{
items: [
{
icon: SquaresFourIcon,
text: "Creator Dashboard",
href: "/profile/dashboard",
},
{
icon: CloudArrowUp,
text: "Publish an agent",
},
],
},
{
items: [
{
icon: GearIcon,
text: "Settings",
href: "/profile/settings",
},
],
},
{
items: [
{
icon: SignOut,
text: "Log out",
},
],
},
];

View File

@@ -0,0 +1,115 @@
import {
IconBuilder,
IconEdit,
IconLayoutDashboard,
IconLibrary,
IconLogOut,
IconMarketplace,
IconRefresh,
IconSettings,
IconType,
IconUploadCloud,
} from "@/components/ui/icons";
type Link = {
name: string;
href: string;
};
export const loggedInLinks: Link[] = [
{
name: "Marketplace",
href: "/marketplace",
},
{
name: "Library",
href: "/library",
},
{
name: "Build",
href: "/build",
},
];
export const loggedOutLinks: Link[] = [
{
name: "Marketplace",
href: "/marketplace",
},
];
export type MenuItemGroup = {
groupName?: string;
items: {
icon: IconType;
text: string;
href?: string;
onClick?: () => void;
}[];
};
export const accountMenuItems: MenuItemGroup[] = [
{
items: [
{
icon: IconType.Edit,
text: "Edit profile",
href: "/profile",
},
],
},
{
items: [
{
icon: IconType.LayoutDashboard,
text: "Creator Dashboard",
href: "/profile/dashboard",
},
{
icon: IconType.UploadCloud,
text: "Publish an agent",
},
],
},
{
items: [
{
icon: IconType.Settings,
text: "Settings",
href: "/profile/settings",
},
],
},
{
items: [
{
icon: IconType.LogOut,
text: "Log out",
},
],
},
];
export function getAccountMenuOptionIcon(icon: IconType) {
const iconClass = "w-6 h-6";
switch (icon) {
case IconType.LayoutDashboard:
return <IconLayoutDashboard className={iconClass} />;
case IconType.UploadCloud:
return <IconUploadCloud className={iconClass} />;
case IconType.Edit:
return <IconEdit className={iconClass} />;
case IconType.Settings:
return <IconSettings className={iconClass} />;
case IconType.LogOut:
return <IconLogOut className={iconClass} />;
case IconType.Marketplace:
return <IconMarketplace className={iconClass} />;
case IconType.Library:
return <IconLibrary className={iconClass} />;
case IconType.Builder:
return <IconBuilder className={iconClass} />;
default:
return <IconRefresh className={iconClass} />;
}
}

View File

@@ -14,30 +14,30 @@ const meta: Meta = {
export default meta;
// Border radius scale data based on Figma design tokens
// Custom naming convention: xs, s, m, l, xl, 2xl, full
// Custom naming convention: xsmall, small, medium, large, xlarge, 2xlarge, full
const borderRadiusScale = [
{
name: "xs",
name: "xsmall",
value: "0.25rem",
rem: "0.25rem",
px: "4px",
class: "rounded-xs",
class: "rounded-xsmall",
description: "Extra small - for subtle rounding",
},
{
name: "s",
name: "small",
value: "0.5rem",
rem: "0.5rem",
px: "8px",
class: "rounded-s",
class: "rounded-small",
description: "Small - for cards and containers",
},
{
name: "m",
name: "medium",
value: "0.75rem",
rem: "0.75rem",
px: "12px",
class: "rounded-m",
class: "rounded-medium",
description: "Medium - for buttons and inputs",
},
{
@@ -49,19 +49,19 @@ const borderRadiusScale = [
description: "Large - for panels and modals",
},
{
name: "xl",
name: "xlarge",
value: "1.25rem",
rem: "1.25rem",
px: "20px",
class: "rounded-xl",
class: "rounded-xlarge",
description: "Extra large - for hero sections",
},
{
name: "2xl",
name: "2xlarge",
value: "1.5rem",
rem: "1.5rem",
px: "24px",
class: "rounded-2xl",
class: "rounded-2xlarge",
description: "2X large - for major containers",
},
{
@@ -84,10 +84,11 @@ export function AllVariants() {
Border Radius
</Text>
<Text variant="large" className="text-zinc-600">
Our border radius system uses a simplified naming convention (xs, s,
m, large, xl, 2xl, full) based on our Figma design tokens. This
creates visual hierarchy and maintains design consistency across all
components.
Our border radius system uses a descriptive naming convention
(xsmall, small, medium, large, xlarge, 2xlarge, full) based on our
Figma design tokens. This creates visual hierarchy and maintains
design consistency across all components while avoiding conflicts
with Tailwind&apos;s built-in classes.
</Text>
</div>
@@ -137,9 +138,10 @@ export function AllVariants() {
</div>
<Text variant="body" className="mb-4 text-zinc-600">
We use a custom border radius system based on our Figma design
tokens, with simplified naming (xs, s, m, large, xl, 2xl, full)
that provides consistent radius values optimized for our design
system.
tokens, with descriptive naming (xsmall, small, medium, large,
xlarge, 2xlarge, full) that provides consistent radius values
optimized for our design system while avoiding conflicts with
Tailwind&apos;s built-in classes.
</Text>
</div>
</div>
@@ -188,7 +190,8 @@ export function AllVariants() {
<Text variant="body" className="mb-6 text-zinc-600">
All border radius values from our Figma design tokens. Each value
can be applied to all corners or specific corners/sides using our
simplified naming convention (xs, s, m, large, xl, 2xl, full).
descriptive naming convention (xsmall, small, medium, large, xlarge,
2xlarge, full).
</Text>
</div>
@@ -232,30 +235,30 @@ export function AllVariants() {
<StoryCode
code={`// Border radius examples - Design System Tokens
<div className="rounded-xs">Extra small rounding (4px)</div>
<div className="rounded-s">Small rounding (8px)</div>
<div className="rounded-m">Medium rounding (12px)</div>
<div className="rounded-xsmall">Extra small rounding (4px)</div>
<div className="rounded-small">Small rounding (8px)</div>
<div className="rounded-medium">Medium rounding (12px)</div>
<div className="rounded-large">Large rounding (16px)</div>
<div className="rounded-xl">Extra large rounding (20px)</div>
<div className="rounded-2xl">2X large rounding (24px)</div>
<div className="rounded-xlarge">Extra large rounding (20px)</div>
<div className="rounded-2xlarge">2X large rounding (24px)</div>
<div className="rounded-full">Pill buttons (circular)</div>
// Directional rounding (works with all sizes)
<div className="rounded-t-m">Top corners only</div>
<div className="rounded-r-m">Right corners only</div>
<div className="rounded-b-m">Bottom corners only</div>
<div className="rounded-l-m">Left corners only</div>
<div className="rounded-t-medium">Top corners only</div>
<div className="rounded-r-medium">Right corners only</div>
<div className="rounded-b-medium">Bottom corners only</div>
<div className="rounded-l-medium">Left corners only</div>
// Individual corners
<div className="rounded-tl-m">Top-left corner</div>
<div className="rounded-tr-m">Top-right corner</div>
<div className="rounded-bl-m">Bottom-left corner</div>
<div className="rounded-br-m">Bottom-right corner</div>
<div className="rounded-tl-medium">Top-left corner</div>
<div className="rounded-tr-medium">Top-right corner</div>
<div className="rounded-bl-medium">Bottom-left corner</div>
<div className="rounded-br-medium">Bottom-right corner</div>
// Usage recommendations
<button className="rounded-full">Pill Button</button>
<div className="rounded-m">Card Container</div>
<input className="rounded-s">Input Field</input>`}
<div className="rounded-medium">Card Container</div>
<input className="rounded-small">Input Field</input>`}
/>
</div>
</div>

View File

@@ -73,56 +73,56 @@ const config = {
},
spacing: {
// Tailwind spacing + custom sizes
0: "0rem",
0.5: "0.125rem",
1: "0.25rem",
1.5: "0.375rem",
2: "0.5rem",
2.5: "0.625rem",
3: "0.75rem",
3.5: "0.875rem",
4: "1rem",
5: "1.25rem",
6: "1.5rem",
7: "1.75rem",
7.5: "1.875rem",
8: "2rem",
8.5: "2.125rem",
9: "2.25rem",
10: "2.5rem",
11: "2.75rem",
12: "3rem",
14: "3.5rem",
16: "4rem",
18: "4.5rem",
20: "5rem",
24: "6rem",
28: "7rem",
32: "8rem",
36: "9rem",
40: "10rem",
44: "11rem",
48: "12rem",
52: "13rem",
56: "14rem",
60: "15rem",
64: "16rem",
68: "17rem",
70: "17.5rem",
71: "17.75rem",
72: "18rem",
76: "19rem",
80: "20rem",
96: "24rem",
0: "0rem", // 0px
0.5: "0.125rem", // 2px
1: "0.25rem", // 4px
1.5: "0.375rem", // 6px
2: "0.5rem", // 8px
2.5: "0.625rem", // 10px
3: "0.75rem", // 12px
3.5: "0.875rem", // 14px
4: "1rem", // 16px
5: "1.25rem", // 20px
6: "1.5rem", // 24px
7: "1.75rem", // 28px
7.5: "1.875rem", // 30px
8: "2rem", // 32px
8.5: "2.125rem", // 34px
9: "2.25rem", // 36px
10: "2.5rem", // 40px
11: "2.75rem", // 44px
12: "3rem", // 48px
14: "3.5rem", // 56px
16: "4rem", // 64px
18: "4.5rem", // 72px
20: "5rem", // 80px
24: "6rem", // 96px
28: "7rem", // 112px
32: "8rem", // 128px
36: "9rem", // 144px
40: "10rem", // 160px
44: "11rem", // 176px
48: "12rem", // 192px
52: "13rem", // 208px
56: "14rem", // 224px
60: "15rem", // 240px
64: "16rem", // 256px
68: "17rem", // 272px
70: "17.5rem", // 280px
71: "17.75rem", // 284px
72: "18rem", // 288px
76: "19rem", // 304px
80: "20rem", // 320px
96: "24rem", // 384px
},
borderRadius: {
// Design system border radius tokens from Figma
xs: "0.25rem", // 4px
s: "0.5rem", // 8px
m: "0.75rem", // 12px
large: "1rem", // 16px (renamed from 'l' to avoid conflict with directional 'left')
xl: "1.25rem", // 20px
"2xl": "1.5rem", // 24px
xsmall: "0.25rem", // 4px
small: "0.5rem", // 8px
medium: "0.75rem", // 12px
large: "1rem", // 16px
xlarge: "1.25rem", // 20px
"2xlarge": "1.5rem", // 24px
full: "9999px", // For pill buttons
// Legacy values - kept for backward compatibility