"use client" /** * UtilityBarSlot — persistent shell row for global utility actions * (Search, Notifications, Help, Settings, Ask Leo, Profile, and — in the * "utility-bar" shell layout variant — the product switcher too). * * Three shell layout variants, driven by `useShellLayout()`: * - "sidebar-classic": not mounted — actions live in the sidebar. * - "utility-sidebar": mounted inside `[data-app-shell-workspace]`, * spanning the secondary rail + main canvas only. * - "utility-bar": mounted as a full-width row ABOVE the sidebar+workspace * row. Sidebar expand/collapse + product switcher on the left; avatar-only * school/scope sits in the trailing identity cluster next to profile. * * At mobile / high-zoom (`useUtilityBarCompact`), Search, Ask Leo, Help, and * Settings collapse into a single More menu; Notifications + profile stay visible. */ import * as React from "react" import { Link, useLocation } from "react-router" import { Button } from "@/components/ui/button" import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu" import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip" import { Kbd, KbdGroup } from "@/components/ui/kbd" import { SidebarTrigger, useSidebar } from "@/components/ui/sidebar" import { useModKeyLabel } from "@/hooks/use-mod-key-label" import { useIsMobile } from "@/hooks/use-mobile" import { useSidebarReflowZoom } from "@/hooks/use-sidebar-reflow-zoom" import { requestOpenCommandMenu } from "@/components/command-menu" import { AskLeoShortcutKbds, useAskLeo } from "@/components/ask-leo-context" import { AskLeoToggle } from "@/components/ask-leo-sidebar" import { ExxatProductMark } from "@/components/exxat-product-logo" import { NotificationBell } from "@/components/notification-bell" import { UtilityUserMenu } from "@/components/utility-user-menu" import { UtilityBarProductSwitcher } from "@/components/utility-bar-product-switcher" import { UtilityBarSchoolSwitcher } from "@/components/utility-bar-school-switcher" import { useProduct } from "@/contexts/product-context" import { useShellLayout, isFullWidthUtilityBar } from "@/contexts/shell-layout-context" import { getSecondaryNavForProduct } from "@/lib/mock/navigation" import { isProductsHomePath } from "@/lib/product-home" import { cn } from "@/lib/utils" import { utilityBarActionButtonClass } from "@/components/utility-bar-chrome" const SUPPRESS_UTILITY_BAR_PATHS: ReadonlyArray = ["/builder/onboarding"] /** * Which registry entry supplies the home mark. The choice is cosmetic — the * round mark is brand-level and every product registers the same corporate * pink gradient for it — but naming the flagship keeps the mark off whatever * product the store happens to still be holding from the last session. */ const HOME_BRAND_MARK_PRODUCT = "exxat-prism" as const function useUtilityBarCompact() { const isMobile = useIsMobile() const reflowZoom = useSidebarReflowZoom() return isMobile || reflowZoom } export function UtilityBarSlot() { const location = useLocation() const { variant } = useShellLayout() const fullWidth = isFullWidthUtilityBar(variant) const compact = useUtilityBarCompact() const [leoGlowIntroActive, setLeoGlowIntroActive] = React.useState(true) if (SUPPRESS_UTILITY_BAR_PATHS.some(path => location.pathname.startsWith(path))) return null // The products home has no product and no sidebar, so most of this bar has // nothing to act on: search scopes to a product, Settings and Ask Leo belong // to one, the scope picker needs one, and the sidebar toggle would toggle a // sidebar that is not rendered. What is left is genuinely global. const productsHome = isProductsHomePath(location.pathname) return ( ) } /** * Exxat mark on the products home. * * The leading cluster is suppressed there (no sidebar to toggle, no product to * name), which left the row with no brand on it at all. The mark is the one * piece of that cluster that still holds on a page about every product: it is * drawn from the brand registry, and every registered brand paints it in the * same corporate pink, so it says "Exxat" rather than naming a product the * user has not picked yet. * * Sits at the start of the row, where the product mark sits inside a product, * so the brand stays in one place as you move between home and a product. * Decorative, and deliberately not a link — the page it would navigate to is * the page it is on. */ function HomeBrandMark() { return ( ) } /** Expand / collapse + product — toggle centered on the inset icon rail. */ function UtilityBarLeadingCluster() { const { isNavFlyout } = useSidebar() const { pathname } = useLocation() // On the products home the page itself is the switcher, and no product is // selected yet — a trigger reading "Prism" there would claim a context the // user has not chosen. const productsHome = pathname === "/home" || pathname.startsWith("/home/") return (
{productsHome ? null : }
) } /** Expand / collapse primary sidebar — leading control before the product mark. */ function UtilityBarSidebarToggle() { const { state, isMobile } = useSidebar() const mod = useModKeyLabel() const collapsed = state === "collapsed" && !isMobile const label = collapsed ? "Expand sidebar" : "Collapse sidebar" return ( {label} {mod} B ) } function UtilityBarMoreMenu() { const mod = useModKeyLabel() const { product } = useProduct() const [settings] = getSecondaryNavForProduct(product) const { toggle: toggleAskLeo, open: askLeoOpen } = useAskLeo() return (