import React from "react";
import { Pin } from "lucide-react";
import { NavigationLink } from "#/components/shared/navigation-link";
import { StyledTooltip } from "#/components/shared/buttons/styled-tooltip";
import { useNavigation } from "#/context/navigation-context";
import { hoverRevealActionClassName } from "#/utils/hover-reveal-classes";
import { cn } from "#/utils/utils";
import { SidebarCollapsedIconSlot } from "./sidebar-collapsed-icon-slot";
import {
SIDEBAR_ICON_SLOT_CLASS,
SIDEBAR_ROW_INTERACTIVE_CLASS,
sidebarNavLabelClassName,
sidebarNavRowClassName,
} from "./sidebar-layout";
function isPathActive(currentPath: string, to: string, end: boolean) {
if (to === "/") {
return currentPath === to;
}
if (end) {
return currentPath === to;
}
return currentPath === to || currentPath.startsWith(`${to}/`);
}
export interface SidebarNavLinkPinAction {
pinned: boolean;
onToggle: () => void;
/** Localized aria-label; pin vs unpin variants resolved by the caller. */
label: string;
testId: string;
}
interface SidebarNavLinkProps {
to: string;
label: string;
end?: boolean;
indent?: boolean;
testId?: string;
disabled?: boolean;
icon?: React.ReactElement;
collapsed?: boolean;
hoverContent?: React.ReactNode;
/**
* When true, forces the active style regardless of the current path.
* Useful for links that should appear active for multiple related routes
* (e.g. the Extensions link being active on /mcp and /plugins too).
*/
forceActive?: boolean;
/** Pin-as-home toggle; rendered only when the sidebar is expanded. */
pinAction?: SidebarNavLinkPinAction;
}
export function SidebarNavLink({
to,
label,
end = false,
indent = false,
testId,
disabled = false,
icon,
collapsed = false,
hoverContent,
forceActive = false,
pinAction,
}: SidebarNavLinkProps) {
const { currentPath } = useNavigation();
const active = forceActive || isPathActive(currentPath, to, end);
const showPinAction = !collapsed && pinAction != null;
const link = (
{
if (disabled) {
e.preventDefault();
}
}}
className={cn(
sidebarNavRowClassName({ indent, collapsed }),
!collapsed &&
(active
? SIDEBAR_ROW_INTERACTIVE_CLASS.active
: SIDEBAR_ROW_INTERACTIVE_CLASS.idle),
disabled && "opacity-50",
disabled && "pointer-events-none",
// Constant right reserve so label truncation doesn't reflow on hover.
showPinAction && "pr-9",
)}
>
{icon ? (
collapsed ? (
{icon}
) : (
{icon}
)
) : null}
{label}
);
if (!collapsed) {
if (!pinAction) return link;
// NavigationLink renders an , so the pin toggle is an absolutely
// positioned sibling rather than a nested button. Bare `group` is safe
// here: only collapsed rows use it, and this branch is expanded-only.
return (
{link}
);
}
return (
{link}
);
}