{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"menubar","type":"registry:component","title":"Menubar","description":"Desktop-app menubar (File / Edit / View) on the WAI-ARIA menubar pattern where every menu renders into one shared floating panel, so once a menu is open hovering another title or pressing Left/Right switches instantly while the panel glides and resizes to fit, with Kbd shortcuts, checkbox and radio rows, separators, disabled items, hover-intent submenus, typeahead and a spring-gliding active row.","author":"kara <ztzyrant@gmail.com>","dependencies":["clsx","lucide-react","motion","tailwind-merge"],"registryDependencies":[],"files":[{"path":"components/motion/menubar.tsx","type":"registry:component","target":"@components/motion/menubar.tsx","content":"\"use client\";\n// karaui.kamisama-v0.my.id/components/motion/menubar\n\nimport { Check, ChevronRight } from \"lucide-react\";\nimport { AnimatePresence, motion, useReducedMotion } from \"motion/react\";\nimport {\n  createContext,\n  type MutableRefObject,\n  type FocusEvent as ReactFocusEvent,\n  type KeyboardEvent as ReactKeyboardEvent,\n  type MouseEvent as ReactMouseEvent,\n  type ReactNode,\n  type PointerEvent as ReactPointerEvent,\n  type Ref,\n  useCallback,\n  useContext,\n  useEffect,\n  useId,\n  useLayoutEffect,\n  useMemo,\n  useRef,\n  useState,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { KbdCombo } from \"@/components/motion/kbd\";\nimport { EASE_OUT, SPRING_LAYOUT, SPRING_PANEL } from \"@/lib/ease\";\nimport { useDismiss } from \"@/lib/hooks/use-dismiss\";\nimport { cn } from \"@/lib/utils\";\n\n/* -------------------------------------------------------------------------- */\n/*                                  Constants                                 */\n/* -------------------------------------------------------------------------- */\n\n/** Where focus lands when a panel opens. */\ntype FocusIntent = \"first\" | \"last\" | \"content\" | \"none\";\n\n// Same hover-intent timings as `dropdown-menu`: a pointer passing over a sub\n// trigger shouldn't flash its submenu, and a diagonal move toward an open\n// submenu that clips a neighbouring row shouldn't snap it shut.\nconst SUB_OPEN_DELAY = 100;\nconst SUB_CLOSE_DELAY = 220;\nconst TYPEAHEAD_RESET = 500;\n// A click that lands this soon after hover already switched to that menu is\n// the same gesture finishing, not a request to close it.\nconst HOVER_CLICK_GRACE = 350;\nconst VIEWPORT_MARGIN = 8;\n\nconst ITEM_SELECTOR = '[data-menubar-item]:not([data-disabled=\"true\"])';\nconst TRIGGER_SELECTOR = \"[data-menubar-trigger]\";\n\nfunction getItems(container: HTMLElement | null) {\n  if (!container) return [];\n  return Array.from(container.querySelectorAll<HTMLElement>(ITEM_SELECTOR));\n}\n\nfunction focusByIntent(container: HTMLElement | null, intent: FocusIntent) {\n  if (!container || intent === \"none\") return;\n  const items = getItems(container);\n  const target =\n    intent === \"first\"\n      ? items[0]\n      : intent === \"last\"\n        ? items[items.length - 1]\n        : container;\n  (target ?? container).focus({ preventScroll: true });\n}\n\n\n/* -------------------------------------------------------------------------- */\n/*                                Bar context                                 */\n/* -------------------------------------------------------------------------- */\n\ninterface BarContextValue {\n  baseId: string;\n  active: string | null;\n  reduce: boolean;\n  /** Open `value` (or close with null) and say where focus should land. */\n  openMenu: (value: string, intent: FocusIntent, viaHover?: boolean) => void;\n  closeMenu: (restoreFocus: boolean) => void;\n  /** Open the menu `dir` steps from the active one, focusing its first item. */\n  move: (dir: 1 | -1) => void;\n  /** Roving focus between triggers while every menu is closed. */\n  focusTrigger: (from: string, to: \"next\" | \"prev\" | \"first\" | \"last\") => void;\n  tabStop: string | null;\n  setTabStop: (value: string) => void;\n  /** True when a hover switched to `value` a moment ago. */\n  hoverOpenedRecently: (value: string) => boolean;\n  host: HTMLDivElement | null;\n  triggers: MutableRefObject<Map<string, HTMLElement>>;\n  contents: MutableRefObject<Map<string, HTMLElement>>;\n  /** Content sizes changed — re-measure the shared panel. */\n  remeasure: () => void;\n}\n\nconst BarContext = createContext<BarContextValue | null>(null);\n\nfunction useBar(component: string) {\n  const ctx = useContext(BarContext);\n  if (!ctx) throw new Error(`${component} must be used within <Menubar>`);\n  return ctx;\n}\n\ninterface MenuContextValue {\n  value: string;\n  triggerId: string;\n  contentId: string;\n}\n\nconst MenuContext = createContext<MenuContextValue | null>(null);\n\nfunction useMenu(component: string) {\n  const ctx = useContext(MenuContext);\n  if (!ctx) throw new Error(`${component} must be used within <MenubarMenu>`);\n  return ctx;\n}\n\n/* -------------------------------------------------------------------------- */\n/*                                   Levels                                   */\n/* -------------------------------------------------------------------------- */\n\n/**\n * One open panel: a top-level menu or a submenu. Items, the active-row\n * highlight and keyboard navigation read the nearest level, so a submenu is\n * the same machinery one step down.\n */\ninterface MenuLevel {\n  levelId: string;\n  open: boolean;\n  isSub: boolean;\n  triggerRef: MutableRefObject<HTMLElement | null>;\n  contentRef: MutableRefObject<HTMLElement | null>;\n  activeId: string | null;\n  setActiveId: (id: string | null) => void;\n  openSub: string | null;\n  setOpenSub: (id: string | null) => void;\n  scheduleSubClose: () => void;\n  cancelSubClose: () => void;\n  /** Close this level (a submenu) — or, at the top, the whole menubar. */\n  close: (restoreFocus: boolean) => void;\n}\n\nconst LevelContext = createContext<MenuLevel | null>(null);\n\nfunction useLevel(component: string) {\n  const level = useContext(LevelContext);\n  if (!level) throw new Error(`${component} must be used within <MenubarContent>`);\n  return level;\n}\n\nfunction useLevelCore(open: boolean) {\n  const [activeId, setActiveId] = useState<string | null>(null);\n  const [openSub, setOpenSubState] = useState<string | null>(null);\n  const subCloseTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n  const cancelSubClose = useCallback(() => {\n    if (subCloseTimer.current) clearTimeout(subCloseTimer.current);\n    subCloseTimer.current = null;\n  }, []);\n\n  const setOpenSub = useCallback(\n    (id: string | null) => {\n      cancelSubClose();\n      setOpenSubState(id);\n    },\n    [cancelSubClose],\n  );\n\n  const scheduleSubClose = useCallback(() => {\n    cancelSubClose();\n    subCloseTimer.current = setTimeout(() => {\n      subCloseTimer.current = null;\n      setOpenSubState(null);\n    }, SUB_CLOSE_DELAY);\n  }, [cancelSubClose]);\n\n  // Reset the highlight when the panel opens, not when it closes: dropping a\n  // layoutId highlight mid-exit can stall the exit (see dropdown-menu).\n  useEffect(() => {\n    if (open) {\n      setActiveId(null);\n      return;\n    }\n    cancelSubClose();\n    setOpenSubState(null);\n  }, [open, cancelSubClose]);\n\n  useEffect(() => cancelSubClose, [cancelSubClose]);\n\n  return useMemo(\n    () => ({ activeId, setActiveId, openSub, setOpenSub, scheduleSubClose, cancelSubClose }),\n    [activeId, openSub, setOpenSub, scheduleSubClose, cancelSubClose],\n  );\n}\n\n/* -------------------------------------------------------------------------- */\n/*                                    Root                                    */\n/* -------------------------------------------------------------------------- */\n\nexport interface MenubarProps {\n  children: ReactNode;\n  /** Controlled open menu (`MenubarMenu` value), or null when closed. */\n  value?: string | null;\n  defaultValue?: string | null;\n  onValueChange?: (value: string | null) => void;\n  /** Gap between the bar and the panel, in px. Default 6. */\n  sideOffset?: number;\n  \"aria-label\"?: string;\n  className?: string;\n  /** Extra classes for the shared floating panel. */\n  panelClassName?: string;\n}\n\ninterface PanelGeometry {\n  top: number;\n  left: number;\n  x: number;\n  width: number;\n  height: number;\n  /** First frame after opening: jump into place instead of gliding. */\n  snap: boolean;\n}\n\n/**\n * Desktop-app menubar (File / Edit / View …) following the WAI-ARIA menubar\n * pattern. Every menu renders into one shared floating panel: once a menu is\n * open, hovering another title (or ArrowLeft/ArrowRight) switches instantly\n * and the panel glides and resizes to the new menu.\n */\nexport function Menubar({\n  children,\n  value: controlledValue,\n  defaultValue = null,\n  onValueChange,\n  sideOffset = 6,\n  \"aria-label\": ariaLabel,\n  className,\n  panelClassName,\n}: MenubarProps) {\n  const [internal, setInternal] = useState<string | null>(defaultValue);\n  const controlled = controlledValue !== undefined;\n  const active = controlled ? controlledValue : internal;\n  const open = active !== null;\n  const baseId = useId();\n  const reduce = useReducedMotion() ?? false;\n  const barRef = useRef<HTMLDivElement>(null);\n  const triggers = useRef(new Map<string, HTMLElement>());\n  const contents = useRef(new Map<string, HTMLElement>());\n  const intent = useRef<FocusIntent>(\"content\");\n  const hoverOpened = useRef<{ value: string; at: number } | null>(null);\n  const lastActive = useRef<string | null>(active);\n  const opened = useRef(false);\n  const [tabStop, setTabStop] = useState<string | null>(null);\n  const [host, setHost] = useState<HTMLDivElement | null>(null);\n  const [mounted, setMounted] = useState(false);\n  const [geo, setGeo] = useState<PanelGeometry | null>(null);\n  const [measureTick, setMeasureTick] = useState(0);\n\n  useEffect(() => setMounted(true), []);\n  if (active !== null) lastActive.current = active;\n\n  const setActive = useCallback(\n    (next: string | null) => {\n      if (!controlled) setInternal(next);\n      onValueChange?.(next);\n    },\n    [controlled, onValueChange],\n  );\n\n  const orderedValues = useCallback(() => {\n    const nodes = barRef.current?.querySelectorAll<HTMLElement>(\n      `${TRIGGER_SELECTOR}:not(:disabled)`,\n    );\n    return Array.from(nodes ?? []).map((node) => node.dataset.value ?? \"\");\n  }, []);\n\n  const openMenu = useCallback(\n    (value: string, nextIntent: FocusIntent, viaHover = false) => {\n      intent.current = nextIntent;\n      hoverOpened.current = viaHover ? { value, at: performance.now() } : null;\n      setTabStop(value);\n      if (value === active) {\n        focusByIntent(contents.current.get(value) ?? null, nextIntent);\n        return;\n      }\n      setActive(value);\n    },\n    [active, setActive],\n  );\n\n  const closeMenu = useCallback(\n    (restoreFocus: boolean) => {\n      const was = lastActive.current;\n      setActive(null);\n      if (restoreFocus && was) {\n        triggers.current.get(was)?.focus({ preventScroll: true });\n      }\n    },\n    [setActive],\n  );\n\n  const move = useCallback(\n    (dir: 1 | -1) => {\n      const values = orderedValues();\n      if (values.length === 0) return;\n      const index = active ? values.indexOf(active) : -1;\n      const next = values[(index + dir + values.length) % values.length];\n      if (next !== undefined) openMenu(next, \"first\");\n    },\n    [active, openMenu, orderedValues],\n  );\n\n  const focusTrigger = useCallback(\n    (from: string, to: \"next\" | \"prev\" | \"first\" | \"last\") => {\n      const values = orderedValues();\n      if (values.length === 0) return;\n      const index = values.indexOf(from);\n      const target =\n        to === \"first\"\n          ? values[0]\n          : to === \"last\"\n            ? values[values.length - 1]\n            : values[(index + (to === \"next\" ? 1 : -1) + values.length) % values.length];\n      if (target === undefined) return;\n      setTabStop(target);\n      triggers.current.get(target)?.focus();\n    },\n    [orderedValues],\n  );\n\n  const hoverOpenedRecently = useCallback((value: string) => {\n    const record = hoverOpened.current;\n    return Boolean(\n      record && record.value === value && performance.now() - record.at < HOVER_CLICK_GRACE,\n    );\n  }, []);\n\n  const remeasure = useCallback(() => setMeasureTick((n) => n + 1), []);\n\n  // The first enabled trigger is the bar's tab stop until another is focused.\n  useLayoutEffect(() => {\n    if (tabStop !== null) return;\n    const first = orderedValues()[0];\n    if (first !== undefined) setTabStop(first);\n  }, [tabStop, orderedValues]);\n\n  const dismiss = useCallback(() => setActive(null), [setActive]);\n  const insideBar = useCallback(\n    (target: Element) => Boolean(target.closest(`[data-menubar=\"${baseId}\"]`)),\n    [baseId],\n  );\n  // Escape is answered by the focused panel (a submenu closes one level).\n  useDismiss(open, dismiss, null, { escape: false, ignore: insideBar });\n\n  // Measure the active menu and place the shared panel under its trigger.\n  // `measureTick` re-runs this when any menu's content resizes.\n  // biome-ignore lint/correctness/useExhaustiveDependencies: measureTick is a deliberate re-measure signal\n  useLayoutEffect(() => {\n    if (active === null) {\n      opened.current = false;\n      return;\n    }\n    const measure = () => {\n      const bar = barRef.current;\n      const trigger = triggers.current.get(active);\n      const content = contents.current.get(active);\n      if (!bar || !trigger || !content) return;\n      const barRect = bar.getBoundingClientRect();\n      const triggerRect = trigger.getBoundingClientRect();\n      // +2 for the panel's 1px border (border-box sizing).\n      const width = content.offsetWidth + 2;\n      const height = content.offsetHeight + 2;\n      const maxX = window.innerWidth - VIEWPORT_MARGIN - width - barRect.left;\n      const minX = VIEWPORT_MARGIN - barRect.left;\n      const x = Math.max(minX, Math.min(triggerRect.left - barRect.left, maxX));\n      const snap = !opened.current;\n      opened.current = true;\n      setGeo({ top: barRect.bottom + sideOffset, left: barRect.left, x, width, height, snap });\n    };\n    measure();\n    window.addEventListener(\"resize\", measure);\n    window.addEventListener(\"scroll\", measure, true);\n    return () => {\n      window.removeEventListener(\"resize\", measure);\n      window.removeEventListener(\"scroll\", measure, true);\n    };\n  }, [active, sideOffset, measureTick]);\n\n  // Focus moves into the newly active menu: its first/last item for keyboard\n  // opens, the menu itself for pointer opens so arrows work immediately.\n  useEffect(() => {\n    if (active === null) return;\n    const frame = requestAnimationFrame(() =>\n      focusByIntent(contents.current.get(active) ?? null, intent.current),\n    );\n    return () => cancelAnimationFrame(frame);\n  }, [active]);\n\n  const ctx = useMemo<BarContextValue>(\n    () => ({\n      baseId,\n      active,\n      reduce,\n      openMenu,\n      closeMenu,\n      move,\n      focusTrigger,\n      tabStop,\n      setTabStop,\n      hoverOpenedRecently,\n      host,\n      triggers,\n      contents,\n      remeasure,\n    }),\n    [\n      baseId,\n      active,\n      reduce,\n      openMenu,\n      closeMenu,\n      move,\n      focusTrigger,\n      tabStop,\n      hoverOpenedRecently,\n      host,\n      remeasure,\n    ],\n  );\n\n  const shown = open && geo !== null;\n  const glide = geo?.snap || reduce ? { duration: 0 } : SPRING_LAYOUT;\n\n  return (\n    <BarContext.Provider value={ctx}>\n      <div\n        ref={barRef}\n        role=\"menubar\"\n        aria-label={ariaLabel}\n        aria-orientation=\"horizontal\"\n        data-menubar={baseId}\n        className={cn(\n          \"inline-flex items-center gap-0.5 rounded-xl border border-border bg-card p-1\",\n          className,\n        )}\n      >\n        {children}\n      </div>\n      {mounted\n        ? createPortal(\n            <motion.div\n              data-menubar={baseId}\n              inert={!open}\n              initial={false}\n              animate={{\n                opacity: shown ? 1 : 0,\n                scale: shown || reduce ? 1 : 0.96,\n                // Flips at the start of the close, so a fading panel can never\n                // be hovered or clicked through.\n                pointerEvents: shown ? \"auto\" : \"none\",\n                x: geo?.x ?? 0,\n                // Sizing is animated on purpose: the panel morphing between\n                // menus is this component's layout.\n                width: geo?.width ?? 0,\n                height: geo?.height ?? 0,\n              }}\n              transition={{\n                opacity: shown\n                  ? { duration: 0.16, ease: EASE_OUT }\n                  : { duration: 0.12, ease: EASE_OUT },\n                scale: reduce ? { duration: 0 } : shown ? SPRING_PANEL : { duration: 0.12, ease: EASE_OUT },\n                pointerEvents: { duration: 0 },\n                x: glide,\n                width: glide,\n                height: glide,\n              }}\n              style={{\n                top: geo?.top ?? 0,\n                left: geo?.left ?? 0,\n                transformOrigin: \"0% 0%\",\n              }}\n              className={cn(\n                \"fixed z-[100] overflow-hidden rounded-xl border border-border bg-card text-foreground shadow-lg\",\n                panelClassName,\n              )}\n            >\n              <div ref={setHost} className=\"relative\" />\n            </motion.div>,\n            document.body,\n          )\n        : null}\n    </BarContext.Provider>\n  );\n}\n\n/* -------------------------------------------------------------------------- */\n/*                               Menu + trigger                               */\n/* -------------------------------------------------------------------------- */\n\nexport interface MenubarMenuProps {\n  /** Identifies the menu for `value`/`onValueChange`. */\n  value: string;\n  children: ReactNode;\n}\n\nexport function MenubarMenu({ value, children }: MenubarMenuProps) {\n  const id = useId();\n  const ctx = useMemo<MenuContextValue>(\n    () => ({ value, triggerId: `${id}-trigger`, contentId: `${id}-content` }),\n    [value, id],\n  );\n  return <MenuContext.Provider value={ctx}>{children}</MenuContext.Provider>;\n}\n\nexport interface MenubarTriggerProps {\n  children: ReactNode;\n  disabled?: boolean;\n  className?: string;\n}\n\nexport function MenubarTrigger({ children, disabled = false, className }: MenubarTriggerProps) {\n  const bar = useBar(\"MenubarTrigger\");\n  const menu = useMenu(\"MenubarTrigger\");\n  const isOpen = bar.active === menu.value;\n  const anyOpen = bar.active !== null;\n  const { triggers } = bar;\n  const { value } = menu;\n\n  const ref = useCallback(\n    (node: HTMLButtonElement | null) => {\n      if (node) triggers.current.set(value, node);\n      else triggers.current.delete(value);\n    },\n    [triggers, value],\n  );\n\n  return (\n    <button\n      ref={ref}\n      type=\"button\"\n      role=\"menuitem\"\n      id={menu.triggerId}\n      disabled={disabled}\n      tabIndex={bar.tabStop === value ? 0 : -1}\n      aria-haspopup=\"menu\"\n      aria-expanded={isOpen}\n      aria-controls={menu.contentId}\n      data-menubar-trigger=\"\"\n      data-value={value}\n      data-state={isOpen ? \"open\" : \"closed\"}\n      onFocus={() => bar.setTabStop(value)}\n      onPointerEnter={(event: ReactPointerEvent<HTMLButtonElement>) => {\n        if (event.pointerType === \"touch\" || disabled) return;\n        if (anyOpen && !isOpen) bar.openMenu(value, \"content\", true);\n      }}\n      onClick={(event: ReactMouseEvent<HTMLButtonElement>) => {\n        // `detail` is 0 for a click synthesized by the keyboard; keys are\n        // handled in onKeyDown.\n        if (event.detail === 0) return;\n        if (isOpen) {\n          if (!bar.hoverOpenedRecently(value)) bar.closeMenu(false);\n          return;\n        }\n        bar.openMenu(value, \"content\");\n      }}\n      onKeyDown={(event: ReactKeyboardEvent<HTMLButtonElement>) => {\n        const { key } = event;\n        if (key === \"ArrowDown\" || key === \"Enter\" || key === \" \") {\n          event.preventDefault();\n          bar.openMenu(value, \"first\");\n        } else if (key === \"ArrowUp\") {\n          event.preventDefault();\n          bar.openMenu(value, \"last\");\n        } else if (key === \"ArrowRight\" || key === \"ArrowLeft\") {\n          event.preventDefault();\n          if (anyOpen) bar.move(key === \"ArrowRight\" ? 1 : -1);\n          else bar.focusTrigger(value, key === \"ArrowRight\" ? \"next\" : \"prev\");\n        } else if (key === \"Home\" || key === \"End\") {\n          event.preventDefault();\n          bar.focusTrigger(value, key === \"Home\" ? \"first\" : \"last\");\n        }\n      }}\n      className={cn(\n        \"relative inline-flex h-7 select-none items-center rounded-lg px-2.5 text-[13px] font-medium text-foreground outline-none transition-colors\",\n        \"hover:bg-foreground/[0.06] focus-visible:ring-2 focus-visible:ring-ring\",\n        \"data-[state=open]:bg-foreground/[0.08] disabled:pointer-events-none disabled:opacity-40\",\n        className,\n      )}\n    >\n      {children}\n    </button>\n  );\n}\n\n/* -------------------------------------------------------------------------- */\n/*                                   Panels                                   */\n/* -------------------------------------------------------------------------- */\n\nfunction usePanelKeys(level: MenuLevel) {\n  const bar = useBar(\"MenubarContent\");\n  const typeahead = useRef(\"\");\n  const typeaheadTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n  useEffect(\n    () => () => {\n      if (typeaheadTimer.current) clearTimeout(typeaheadTimer.current);\n    },\n    [],\n  );\n\n  return (event: ReactKeyboardEvent<HTMLElement>) => {\n    // React bubbles events through portals: a submenu's keys would reach its\n    // parent panel too. Only answer keys pressed inside this panel's DOM.\n    if (!event.currentTarget.contains(event.target as Node)) return;\n    if (event.defaultPrevented) return;\n    const { key } = event;\n    const container = level.contentRef.current;\n\n    if (key === \"Escape\") {\n      event.preventDefault();\n      level.close(true);\n      return;\n    }\n    if (key === \"ArrowLeft\") {\n      event.preventDefault();\n      if (level.isSub) level.close(true);\n      else bar.move(-1);\n      return;\n    }\n    if (key === \"ArrowRight\") {\n      // A sub trigger already claimed this key (preventDefault) to open.\n      event.preventDefault();\n      bar.move(1);\n      return;\n    }\n    if (key === \"Tab\") {\n      event.preventDefault();\n      bar.closeMenu(true);\n      return;\n    }\n    if (key === \"ArrowDown\" || key === \"ArrowUp\") {\n      event.preventDefault();\n      const items = getItems(container);\n      if (items.length === 0) return;\n      const current = items.indexOf(document.activeElement as HTMLElement);\n      const dir = key === \"ArrowDown\" ? 1 : -1;\n      const next =\n        current < 0\n          ? dir === 1\n            ? 0\n            : items.length - 1\n          : (current + dir + items.length) % items.length;\n      items[next]?.focus();\n      return;\n    }\n    if (key === \"Home\" || key === \"End\") {\n      event.preventDefault();\n      const items = getItems(container);\n      items[key === \"Home\" ? 0 : items.length - 1]?.focus();\n      return;\n    }\n    const printable = key.length === 1 && !event.ctrlKey && !event.metaKey && !event.altKey;\n    // A leading space is activation, not a search character.\n    if (!printable || (key === \" \" && typeahead.current === \"\")) return;\n    event.preventDefault();\n    typeahead.current += key.toLocaleLowerCase();\n    if (typeaheadTimer.current) clearTimeout(typeaheadTimer.current);\n    typeaheadTimer.current = setTimeout(() => {\n      typeahead.current = \"\";\n    }, TYPEAHEAD_RESET);\n    const items = getItems(container);\n    const start = items.indexOf(document.activeElement as HTMLElement);\n    // Search from the row after the current one so repeating a letter cycles.\n    const ordered =\n      typeahead.current.length === 1 && start >= 0\n        ? [...items.slice(start + 1), ...items.slice(0, start + 1)]\n        : items;\n    ordered\n      .find((item) =>\n        (item.dataset.label ?? item.textContent ?? \"\")\n          .trim()\n          .toLocaleLowerCase()\n          .startsWith(typeahead.current),\n      )\n      ?.focus();\n  };\n}\n\nexport interface MenubarContentProps {\n  children: ReactNode;\n  /** Accessible name; defaults to the trigger's text via aria-labelledby. */\n  ariaLabel?: string;\n  className?: string;\n}\n\n/**\n * The menu's items. Rendered into the menubar's shared panel; every menu stays\n * mounted (inert while inactive) so the panel can measure and morph to it.\n */\nexport function MenubarContent({ children, ariaLabel, className }: MenubarContentProps) {\n  const bar = useBar(\"MenubarContent\");\n  const menu = useMenu(\"MenubarContent\");\n  const open = bar.active === menu.value;\n  const core = useLevelCore(open);\n  const triggerRef = useRef<HTMLElement | null>(null);\n  const contentRef = useRef<HTMLElement | null>(null);\n  const { closeMenu, contents, remeasure, triggers } = bar;\n  const { value } = menu;\n\n  triggerRef.current = triggers.current.get(value) ?? null;\n\n  const level = useMemo<MenuLevel>(\n    () => ({\n      levelId: `${menu.contentId}-level`,\n      open,\n      isSub: false,\n      triggerRef,\n      contentRef,\n      ...core,\n      close: closeMenu,\n    }),\n    [menu.contentId, open, core, closeMenu],\n  );\n\n  const onKeyDown = usePanelKeys(level);\n\n  // Register for measurement and focus; re-measure whenever the menu resizes.\n  const setNode = useCallback(\n    (node: HTMLDivElement | null) => {\n      contentRef.current = node;\n      if (node) contents.current.set(value, node);\n      else contents.current.delete(value);\n    },\n    [contents, value],\n  );\n\n  // The content node only exists once the shared panel's host has mounted.\n  const { host } = bar;\n  useEffect(() => {\n    const node = contentRef.current;\n    if (!host || !node || typeof ResizeObserver === \"undefined\") return;\n    const observer = new ResizeObserver(() => remeasure());\n    observer.observe(node);\n    return () => observer.disconnect();\n  }, [remeasure, host]);\n\n  if (!host) return null;\n\n  return createPortal(\n    <LevelContext.Provider value={level}>\n      <motion.div\n        ref={setNode}\n        id={menu.contentId}\n        role=\"menu\"\n        aria-label={ariaLabel}\n        aria-labelledby={ariaLabel ? undefined : menu.triggerId}\n        aria-orientation=\"vertical\"\n        tabIndex={-1}\n        inert={!open}\n        initial={false}\n        animate={{ opacity: open ? 1 : 0 }}\n        transition={{ duration: open ? 0.16 : 0.1, ease: EASE_OUT }}\n        onKeyDown={onKeyDown}\n        onFocus={(event: ReactFocusEvent<HTMLDivElement>) => {\n          if (event.target === event.currentTarget) core.setActiveId(null);\n        }}\n        className={cn(\n          \"absolute left-0 top-0 w-max min-w-52 max-w-[calc(100vw-2rem)] p-1.5 outline-none\",\n          !open && \"pointer-events-none\",\n          className,\n        )}\n      >\n        {children}\n      </motion.div>\n    </LevelContext.Provider>,\n    host,\n  );\n}\n\n/* -------------------------------------------------------------------------- */\n/*                                   Items                                    */\n/* -------------------------------------------------------------------------- */\n\ntype ItemVariant = \"default\" | \"destructive\";\n\ninterface ItemBaseProps {\n  children: ReactNode;\n  itemId?: string;\n  role?: \"menuitem\" | \"menuitemcheckbox\" | \"menuitemradio\";\n  ariaChecked?: boolean;\n  disabled?: boolean;\n  variant?: ItemVariant;\n  inset?: boolean;\n  textValue?: string;\n  className?: string;\n  closeOnSelect: boolean;\n  onActivate?: (event: ReactMouseEvent<HTMLButtonElement>) => void;\n  isSubTrigger?: boolean;\n  extraProps?: React.ButtonHTMLAttributes<HTMLButtonElement> & {\n    ref?: Ref<HTMLButtonElement>;\n    [key: `data-${string}`]: string | undefined;\n  };\n}\n\nfunction ItemBase({\n  children,\n  itemId,\n  role = \"menuitem\",\n  ariaChecked,\n  disabled = false,\n  variant = \"default\",\n  inset = false,\n  textValue,\n  className,\n  closeOnSelect,\n  onActivate,\n  isSubTrigger = false,\n  extraProps,\n}: ItemBaseProps) {\n  const level = useLevel(\"MenubarItem\");\n  const bar = useBar(\"MenubarItem\");\n  const autoId = useId();\n  const id = itemId ?? autoId;\n  const highlighted = level.activeId === id;\n  const checkedProps = role === \"menuitem\" ? {} : { \"aria-checked\": ariaChecked ?? false };\n  const { onPointerEnter, onPointerLeave, onKeyDown, ref, ...rest } = extraProps ?? {};\n\n  return (\n    <button\n      {...rest}\n      ref={ref}\n      type=\"button\"\n      id={id}\n      role={role}\n      {...checkedProps}\n      disabled={disabled}\n      tabIndex={-1}\n      data-menubar-item=\"\"\n      data-disabled={disabled ? \"true\" : undefined}\n      data-highlighted={highlighted ? \"\" : undefined}\n      data-label={textValue}\n      onFocus={() => level.setActiveId(id)}\n      onPointerEnter={(event: ReactPointerEvent<HTMLButtonElement>) => {\n        onPointerEnter?.(event);\n        if (!isSubTrigger && event.pointerType !== \"touch\") level.scheduleSubClose();\n      }}\n      onPointerMove={(event: ReactPointerEvent<HTMLButtonElement>) => {\n        if (disabled || event.pointerType === \"touch\") return;\n        if (document.activeElement !== event.currentTarget) {\n          event.currentTarget.focus({ preventScroll: true });\n        }\n      }}\n      onPointerLeave={(event: ReactPointerEvent<HTMLButtonElement>) => {\n        onPointerLeave?.(event);\n        if (event.pointerType === \"touch\" || isSubTrigger) return;\n        // Focus back to the panel drops the highlight, like native menus.\n        if (document.activeElement === event.currentTarget) {\n          level.contentRef.current?.focus({ preventScroll: true });\n        }\n      }}\n      onKeyDown={onKeyDown}\n      onClick={(event) => {\n        if (disabled) return;\n        onActivate?.(event);\n        if (closeOnSelect && !event.defaultPrevented) bar.closeMenu(true);\n      }}\n      className={cn(\n        \"relative isolate flex w-full select-none items-center gap-2.5 rounded-lg px-2.5 py-1.5 text-left text-[13px] outline-none\",\n        \"disabled:pointer-events-none disabled:opacity-40\",\n        \"[&_svg]:size-4 [&_svg]:shrink-0\",\n        inset && \"pl-8\",\n        variant === \"destructive\"\n          ? \"text-destructive [&_svg]:text-destructive\"\n          : \"text-foreground [&_svg]:text-muted-foreground\",\n        className,\n      )}\n    >\n      {/* Its own presence boundary: a layoutId element registers with the\n          nearest AnimatePresence, and an exiting submenu would otherwise wait\n          on the highlight and never unmount (leaving a clickable ghost). */}\n      <AnimatePresence initial={false}>\n        {highlighted ? (\n          <motion.span\n            aria-hidden=\"true\"\n            layoutId={`${level.levelId}-highlight`}\n            className={cn(\n              \"pointer-events-none absolute inset-0 -z-10 rounded-lg\",\n              variant === \"destructive\" ? \"bg-destructive/10\" : \"bg-foreground/[0.07]\",\n            )}\n            transition={bar.reduce ? { duration: 0 } : SPRING_LAYOUT}\n          />\n        ) : null}\n      </AnimatePresence>\n      {children}\n    </button>\n  );\n}\n\n/** Key tokens (`[\"mod\", \"s\"]`) render as `KbdCombo`; anything else as given. */\nexport type MenubarShortcut = string[] | ReactNode;\n\nfunction Shortcut({ shortcut }: { shortcut: MenubarShortcut }) {\n  return (\n    <span aria-hidden=\"true\" className=\"ml-auto inline-flex shrink-0 pl-6\">\n      {Array.isArray(shortcut) &&\n      shortcut.every((key): key is string => typeof key === \"string\") ? (\n        <KbdCombo\n          keys={shortcut}\n          size=\"sm\"\n          className=\"[&_kbd]:border-b [&_kbd]:bg-transparent [&_kbd]:text-[10.5px]\"\n        />\n      ) : (\n        <span className=\"font-mono text-[11px] tracking-wide text-muted-foreground\">\n          {shortcut}\n        </span>\n      )}\n    </span>\n  );\n}\n\nexport interface MenubarItemProps {\n  children: ReactNode;\n  /** Leading icon (sized to 16px). */\n  icon?: ReactNode;\n  /** Trailing shortcut hint: key tokens for `KbdCombo` or any node. Decorative. */\n  shortcut?: MenubarShortcut;\n  variant?: ItemVariant;\n  disabled?: boolean;\n  /** Indent to line up with checkbox/radio rows. */\n  inset?: boolean;\n  /** Text used by typeahead when children aren't plain text. */\n  textValue?: string;\n  /** Call `event.preventDefault()` to keep the menu open. */\n  onSelect?: (event: ReactMouseEvent<HTMLButtonElement>) => void;\n  /** Default true. */\n  closeOnSelect?: boolean;\n  className?: string;\n}\n\nexport function MenubarItem({\n  children,\n  icon,\n  shortcut,\n  onSelect,\n  closeOnSelect = true,\n  ...props\n}: MenubarItemProps) {\n  return (\n    <ItemBase {...props} closeOnSelect={closeOnSelect} onActivate={onSelect}>\n      {icon}\n      <span className=\"min-w-0 flex-1 truncate\">{children}</span>\n      {shortcut ? <Shortcut shortcut={shortcut} /> : null}\n    </ItemBase>\n  );\n}\n\nfunction Indicator({\n  checked,\n  kind,\n  reduce,\n}: {\n  checked: boolean;\n  kind: \"check\" | \"dot\";\n  reduce: boolean;\n}) {\n  return (\n    <span aria-hidden=\"true\" className=\"absolute left-2.5 flex size-4 items-center justify-center\">\n      <AnimatePresence initial={false}>\n        {checked ? (\n          <motion.span\n            key={kind}\n            initial={reduce ? { opacity: 0 } : { opacity: 0, scale: 0.6 }}\n            animate={{ opacity: 1, scale: 1 }}\n            exit={reduce ? { opacity: 0 } : { opacity: 0, scale: 0.6 }}\n            transition={reduce ? { duration: 0.08 } : SPRING_PANEL}\n            className=\"flex items-center justify-center text-foreground\"\n          >\n            {kind === \"check\" ? (\n              <Check className=\"size-3.5! text-foreground!\" strokeWidth={2.6} />\n            ) : (\n              <span className=\"size-1.5 rounded-full bg-current\" />\n            )}\n          </motion.span>\n        ) : null}\n      </AnimatePresence>\n    </span>\n  );\n}\n\nexport interface MenubarCheckboxItemProps\n  extends Omit<MenubarItemProps, \"onSelect\" | \"inset\" | \"icon\"> {\n  checked?: boolean;\n  defaultChecked?: boolean;\n  onCheckedChange?: (checked: boolean) => void;\n}\n\n/** A toggle row. Stays open on select by default so several can be flipped. */\nexport function MenubarCheckboxItem({\n  children,\n  checked: controlledChecked,\n  defaultChecked = false,\n  onCheckedChange,\n  shortcut,\n  closeOnSelect = false,\n  ...props\n}: MenubarCheckboxItemProps) {\n  const bar = useBar(\"MenubarCheckboxItem\");\n  const [internal, setInternal] = useState(defaultChecked);\n  const checked = controlledChecked ?? internal;\n  return (\n    <ItemBase\n      {...props}\n      inset\n      role=\"menuitemcheckbox\"\n      ariaChecked={checked}\n      closeOnSelect={closeOnSelect}\n      onActivate={() => {\n        if (controlledChecked === undefined) setInternal(!checked);\n        onCheckedChange?.(!checked);\n      }}\n    >\n      <Indicator checked={checked} kind=\"check\" reduce={bar.reduce} />\n      <span className=\"min-w-0 flex-1 truncate\">{children}</span>\n      {shortcut ? <Shortcut shortcut={shortcut} /> : null}\n    </ItemBase>\n  );\n}\n\ninterface RadioGroupContextValue {\n  value: string | undefined;\n  select: (value: string) => void;\n}\n\nconst RadioGroupContext = createContext<RadioGroupContextValue | null>(null);\n\nexport interface MenubarRadioGroupProps {\n  children: ReactNode;\n  value?: string;\n  defaultValue?: string;\n  onValueChange?: (value: string) => void;\n  /** Accessible name for the group. */\n  ariaLabel?: string;\n  className?: string;\n}\n\nexport function MenubarRadioGroup({\n  children,\n  value: controlledValue,\n  defaultValue,\n  onValueChange,\n  ariaLabel,\n  className,\n}: MenubarRadioGroupProps) {\n  const [internal, setInternal] = useState(defaultValue);\n  const value = controlledValue ?? internal;\n  const ctx = useMemo<RadioGroupContextValue>(\n    () => ({\n      value,\n      select: (next) => {\n        if (controlledValue === undefined) setInternal(next);\n        onValueChange?.(next);\n      },\n    }),\n    [value, controlledValue, onValueChange],\n  );\n  return (\n    <RadioGroupContext.Provider value={ctx}>\n      <fieldset aria-label={ariaLabel} className={cn(\"m-0 min-w-0 border-0 p-0\", className)}>\n        {children}\n      </fieldset>\n    </RadioGroupContext.Provider>\n  );\n}\n\nexport interface MenubarRadioItemProps\n  extends Omit<MenubarItemProps, \"onSelect\" | \"inset\" | \"icon\"> {\n  value: string;\n}\n\n/** One choice of a `MenubarRadioGroup`; stays open on select by default. */\nexport function MenubarRadioItem({\n  children,\n  value,\n  shortcut,\n  closeOnSelect = false,\n  ...props\n}: MenubarRadioItemProps) {\n  const bar = useBar(\"MenubarRadioItem\");\n  const group = useContext(RadioGroupContext);\n  if (!group) throw new Error(\"MenubarRadioItem must be used within <MenubarRadioGroup>\");\n  const checked = group.value === value;\n  return (\n    <ItemBase\n      {...props}\n      inset\n      role=\"menuitemradio\"\n      ariaChecked={checked}\n      closeOnSelect={closeOnSelect}\n      onActivate={() => group.select(value)}\n    >\n      <Indicator checked={checked} kind=\"dot\" reduce={bar.reduce} />\n      <span className=\"min-w-0 flex-1 truncate\">{children}</span>\n      {shortcut ? <Shortcut shortcut={shortcut} /> : null}\n    </ItemBase>\n  );\n}\n\nexport interface MenubarLabelProps {\n  children: ReactNode;\n  inset?: boolean;\n  className?: string;\n}\n\nexport function MenubarLabel({ children, inset = false, className }: MenubarLabelProps) {\n  return (\n    <div\n      className={cn(\n        \"px-2.5 pb-1 pt-1.5 text-[11px] font-medium text-muted-foreground\",\n        inset && \"pl-8\",\n        className,\n      )}\n    >\n      {children}\n    </div>\n  );\n}\n\nexport function MenubarSeparator({ className }: { className?: string }) {\n  return <hr className={cn(\"-mx-1.5 my-1.5 h-px border-0 bg-border\", className)} />;\n}\n\n/* -------------------------------------------------------------------------- */\n/*                                  Submenus                                  */\n/* -------------------------------------------------------------------------- */\n\ninterface SubContextValue {\n  level: MenuLevel;\n  parent: MenuLevel;\n  triggerItemId: string;\n  contentId: string;\n  openTimer: MutableRefObject<ReturnType<typeof setTimeout> | null>;\n  openWith: (intent: FocusIntent) => void;\n}\n\nconst SubContext = createContext<SubContextValue | null>(null);\n\nfunction useSub(component: string) {\n  const sub = useContext(SubContext);\n  if (!sub) throw new Error(`${component} must be used within <MenubarSub>`);\n  return sub;\n}\n\nexport interface MenubarSubProps {\n  children: ReactNode;\n}\n\n/** One level of nested menu, opened from a `MenubarSubTrigger` row. */\nexport function MenubarSub({ children }: MenubarSubProps) {\n  const parent = useLevel(\"MenubarSub\");\n  const subId = useId();\n  const open = parent.open && parent.openSub === subId;\n  const triggerRef = useRef<HTMLElement | null>(null);\n  const contentRef = useRef<HTMLElement | null>(null);\n  const focusIntent = useRef<FocusIntent>(\"none\");\n  const openTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n  const core = useLevelCore(open);\n  const triggerItemId = `${subId}-trigger`;\n\n  useEffect(\n    () => () => {\n      if (openTimer.current) clearTimeout(openTimer.current);\n    },\n    [],\n  );\n\n  // Submenus take focus only when asked to (keyboard); a hover open leaves\n  // it on the trigger row.\n  useEffect(() => {\n    if (!open) return;\n    const frame = requestAnimationFrame(() =>\n      focusByIntent(contentRef.current, focusIntent.current),\n    );\n    return () => cancelAnimationFrame(frame);\n  }, [open]);\n\n  const { setOpenSub } = parent;\n  const close = useCallback(\n    (restoreFocus: boolean) => {\n      setOpenSub(null);\n      if (restoreFocus) triggerRef.current?.focus({ preventScroll: true });\n    },\n    [setOpenSub],\n  );\n\n  const openWith = useCallback(\n    (intent: FocusIntent) => {\n      if (openTimer.current) clearTimeout(openTimer.current);\n      openTimer.current = null;\n      focusIntent.current = intent;\n      if (open) focusByIntent(contentRef.current, intent);\n      else setOpenSub(subId);\n    },\n    [open, setOpenSub, subId],\n  );\n\n  const level = useMemo<MenuLevel>(\n    () => ({\n      levelId: `${subId}-level`,\n      open,\n      isSub: true,\n      triggerRef,\n      contentRef,\n      ...core,\n      close,\n    }),\n    [subId, open, core, close],\n  );\n\n  const ctx = useMemo<SubContextValue>(\n    () => ({\n      level,\n      parent,\n      triggerItemId,\n      contentId: `${subId}-content`,\n      openTimer,\n      openWith,\n    }),\n    [level, parent, triggerItemId, subId, openWith],\n  );\n\n  return <SubContext.Provider value={ctx}>{children}</SubContext.Provider>;\n}\n\nexport interface MenubarSubTriggerProps {\n  children: ReactNode;\n  icon?: ReactNode;\n  disabled?: boolean;\n  inset?: boolean;\n  textValue?: string;\n  className?: string;\n}\n\nexport function MenubarSubTrigger({ children, icon, ...props }: MenubarSubTriggerProps) {\n  const { level, parent, triggerItemId, contentId, openTimer, openWith } =\n    useSub(\"MenubarSubTrigger\");\n\n  const clearOpenTimer = () => {\n    if (openTimer.current) clearTimeout(openTimer.current);\n    openTimer.current = null;\n  };\n\n  return (\n    <ItemBase\n      {...props}\n      itemId={triggerItemId}\n      isSubTrigger\n      closeOnSelect={false}\n      onActivate={(event) => openWith(event.detail === 0 ? \"first\" : \"none\")}\n      extraProps={{\n        ref: (node: HTMLButtonElement | null) => {\n          level.triggerRef.current = node;\n        },\n        \"aria-haspopup\": \"menu\",\n        \"aria-expanded\": level.open,\n        \"aria-controls\": level.open ? contentId : undefined,\n        \"data-state\": level.open ? \"open\" : \"closed\",\n        onPointerEnter: (event) => {\n          if (event.pointerType === \"touch\" || props.disabled) return;\n          parent.cancelSubClose();\n          if (level.open) return;\n          clearOpenTimer();\n          openTimer.current = setTimeout(() => {\n            openTimer.current = null;\n            openWith(\"none\");\n          }, SUB_OPEN_DELAY);\n        },\n        onPointerLeave: (event) => {\n          if (event.pointerType === \"touch\") return;\n          clearOpenTimer();\n          parent.scheduleSubClose();\n        },\n        onKeyDown: (event) => {\n          if (event.key === \"ArrowRight\") {\n            event.preventDefault();\n            openWith(\"first\");\n          }\n        },\n      }}\n    >\n      {icon}\n      <span className=\"min-w-0 flex-1 truncate\">{children}</span>\n      <ChevronRight aria-hidden=\"true\" className=\"-mr-1 ml-auto size-3.5! text-muted-foreground\" />\n    </ItemBase>\n  );\n}\n\nexport interface MenubarSubContentProps {\n  children: ReactNode;\n  ariaLabel?: string;\n  className?: string;\n}\n\ninterface SubPosition {\n  left: number;\n  top: number;\n  side: \"right\" | \"left\";\n}\n\nexport function MenubarSubContent({ children, ariaLabel, className }: MenubarSubContentProps) {\n  const bar = useBar(\"MenubarSubContent\");\n  const { level, parent, triggerItemId, contentId } = useSub(\"MenubarSubContent\");\n  const onKeyDown = usePanelKeys(level);\n  const [pos, setPos] = useState<SubPosition | null>(null);\n  const { cancelSubClose, setActiveId } = parent;\n  const { open, triggerRef, contentRef } = level;\n\n  // While the pointer or focus is in the submenu, its trigger row keeps the\n  // parent's highlight and the pending close is called off.\n  const onPanelEnter = useCallback(() => {\n    cancelSubClose();\n    setActiveId(triggerItemId);\n  }, [cancelSubClose, setActiveId, triggerItemId]);\n\n  // Beside the trigger row, flipping left when the viewport lacks room; the\n  // first row lines up with the trigger (panel padding + border = 7px).\n  useLayoutEffect(() => {\n    // Keep the last position while closed so the exit plays in place.\n    if (!open) return;\n    const place = () => {\n      const trigger = triggerRef.current;\n      const content = contentRef.current;\n      if (!trigger || !content) return;\n      const t = trigger.getBoundingClientRect();\n      const w = content.offsetWidth;\n      const h = content.offsetHeight;\n      const gap = 8;\n      const fitsRight = t.right + gap + w <= window.innerWidth - VIEWPORT_MARGIN;\n      const left = fitsRight ? t.right + gap : Math.max(VIEWPORT_MARGIN, t.left - gap - w);\n      const top = Math.max(\n        VIEWPORT_MARGIN,\n        Math.min(t.top - 7, window.innerHeight - VIEWPORT_MARGIN - h),\n      );\n      setPos({ left, top, side: fitsRight ? \"right\" : \"left\" });\n    };\n    place();\n    window.addEventListener(\"resize\", place);\n    window.addEventListener(\"scroll\", place, true);\n    return () => {\n      window.removeEventListener(\"resize\", place);\n      window.removeEventListener(\"scroll\", place, true);\n    };\n  }, [open, triggerRef, contentRef]);\n\n  const fromX = pos?.side === \"left\" ? 6 : -6;\n\n  return createPortal(\n    <AnimatePresence>\n      {open ? (\n        <LevelContext.Provider key=\"sub\" value={level}>\n          <motion.div\n            ref={(node: HTMLDivElement | null) => {\n              contentRef.current = node;\n            }}\n            id={contentId}\n            role=\"menu\"\n            aria-label={ariaLabel}\n            aria-labelledby={ariaLabel ? undefined : triggerItemId}\n            aria-orientation=\"vertical\"\n            tabIndex={-1}\n            data-menubar={bar.baseId}\n            initial={bar.reduce ? { opacity: 0 } : { opacity: 0, x: fromX, scale: 0.97 }}\n            animate={{ opacity: 1, x: 0, scale: 1 }}\n            // pointerEvents flips at the start of the exit, so a closing\n            // submenu can never be hovered or clicked through.\n            exit={{\n              opacity: 0,\n              ...(bar.reduce ? {} : { scale: 0.97 }),\n              pointerEvents: \"none\",\n              transition: { duration: 0.1, ease: EASE_OUT },\n            }}\n            transition={\n              bar.reduce\n                ? { duration: 0.12, ease: EASE_OUT }\n                : { ...SPRING_PANEL, opacity: { duration: 0.14, ease: EASE_OUT } }\n            }\n            style={{\n              left: pos?.left ?? 0,\n              top: pos?.top ?? 0,\n              transformOrigin: pos?.side === \"left\" ? \"100% 0%\" : \"0% 0%\",\n              visibility: pos ? \"visible\" : \"hidden\",\n            }}\n            onKeyDown={onKeyDown}\n            onFocus={(event: ReactFocusEvent<HTMLDivElement>) => {\n              if (event.target === event.currentTarget) level.setActiveId(null);\n              else onPanelEnter();\n            }}\n            onPointerEnter={onPanelEnter}\n            className={cn(\n              \"fixed z-[101] min-w-44 rounded-xl border border-border bg-card p-1.5 text-foreground shadow-lg outline-none\",\n              className,\n            )}\n          >\n            {children}\n          </motion.div>\n        </LevelContext.Provider>\n      ) : null}\n    </AnimatePresence>,\n    document.body,\n  );\n}\n"},{"path":"components/motion/kbd.tsx","type":"registry:component","target":"@components/motion/kbd.tsx","content":"\"use client\";\n\nimport { motion, useReducedMotion } from \"motion/react\";\nimport { Fragment, type ReactNode, useEffect, useState } from \"react\";\nimport { SPRING_PRESS } from \"@/lib/ease\";\nimport { cn } from \"@/lib/utils\";\n\n/* -------------------------------------------------------------------------- */\n/*                                  Platform                                  */\n/* -------------------------------------------------------------------------- */\n\nfunction detectApple() {\n  const nav = navigator as Navigator & {\n    userAgentData?: { platform?: string };\n  };\n  const platform = nav.userAgentData?.platform ?? nav.platform ?? \"\";\n  return /mac|iphone|ipad|ipod/i.test(platform);\n}\n\n/**\n * True on Apple platforms. Always `true` on the server and the first client\n * render (so markup matches), then corrected after mount — `Mod` therefore\n * renders ⌘ first and swaps to Ctrl on other platforms.\n */\nexport function useIsApple() {\n  const [apple, setApple] = useState(true);\n  useEffect(() => setApple(detectApple()), []);\n  return apple;\n}\n\ntype KeyLabel = { glyph: string; name: string };\n\n/** Display glyph + spoken name for a key token like \"mod\", \"shift\", \"k\". */\nexport function keyLabel(key: string, apple: boolean): KeyLabel {\n  const k = key.toLowerCase();\n  switch (k) {\n    case \"mod\":\n      return apple\n        ? { glyph: \"⌘\", name: \"Command\" }\n        : { glyph: \"Ctrl\", name: \"Control\" };\n    case \"cmd\":\n    case \"command\":\n    case \"meta\":\n      return apple\n        ? { glyph: \"⌘\", name: \"Command\" }\n        : { glyph: \"Win\", name: \"Windows\" };\n    case \"ctrl\":\n    case \"control\":\n      return apple\n        ? { glyph: \"⌃\", name: \"Control\" }\n        : { glyph: \"Ctrl\", name: \"Control\" };\n    case \"shift\":\n      return apple\n        ? { glyph: \"⇧\", name: \"Shift\" }\n        : { glyph: \"Shift\", name: \"Shift\" };\n    case \"alt\":\n    case \"option\":\n      return apple\n        ? { glyph: \"⌥\", name: \"Option\" }\n        : { glyph: \"Alt\", name: \"Alt\" };\n    case \"enter\":\n    case \"return\":\n      return { glyph: \"↵\", name: \"Enter\" };\n    case \"esc\":\n    case \"escape\":\n      return { glyph: \"Esc\", name: \"Escape\" };\n    case \"backspace\":\n      return { glyph: \"⌫\", name: \"Backspace\" };\n    case \"delete\":\n      return { glyph: \"⌦\", name: \"Delete\" };\n    case \"tab\":\n      return { glyph: \"⇥\", name: \"Tab\" };\n    case \"space\":\n      return { glyph: \"Space\", name: \"Space\" };\n    case \"up\":\n    case \"arrowup\":\n      return { glyph: \"↑\", name: \"Up arrow\" };\n    case \"down\":\n    case \"arrowdown\":\n      return { glyph: \"↓\", name: \"Down arrow\" };\n    case \"left\":\n    case \"arrowleft\":\n      return { glyph: \"←\", name: \"Left arrow\" };\n    case \"right\":\n    case \"arrowright\":\n      return { glyph: \"→\", name: \"Right arrow\" };\n    default:\n      return key.length === 1\n        ? { glyph: key.toUpperCase(), name: key.toUpperCase() }\n        : { glyph: key, name: key };\n  }\n}\n\n/* -------------------------------------------------------------------------- */\n/*                               Key listening                                */\n/* -------------------------------------------------------------------------- */\n\n/** Does this keyboard event's key correspond to the token? */\nfunction eventMatches(token: string, event: KeyboardEvent, apple: boolean) {\n  const k = token.toLowerCase();\n  const key = event.key.toLowerCase();\n  switch (k) {\n    case \"mod\":\n      return key === (apple ? \"meta\" : \"control\");\n    case \"cmd\":\n    case \"command\":\n    case \"meta\":\n      return key === \"meta\";\n    case \"ctrl\":\n    case \"control\":\n      return key === \"control\";\n    case \"alt\":\n    case \"option\":\n      return key === \"alt\";\n    case \"esc\":\n      return key === \"escape\";\n    case \"return\":\n      return key === \"enter\";\n    case \"space\":\n      return event.code === \"Space\";\n    case \"up\":\n    case \"down\":\n    case \"left\":\n    case \"right\":\n      return key === `arrow${k}`;\n    default:\n      // Letters and digits by physical key, so Shift/Option variants\n      // (\"P\" with Shift, \"π\" with Option) still match.\n      if (/^[a-z]$/.test(k)) return event.code === `Key${k.toUpperCase()}`;\n      if (/^[0-9]$/.test(k)) return event.code === `Digit${k}`;\n      return key === k;\n  }\n}\n\nconst MODIFIERS = new Set([\"meta\", \"control\", \"shift\", \"alt\"]);\n\n/**\n * Tracks which of `keys` are physically held. Listens on window without\n * preventing default, so it never steals the shortcut from the page.\n */\nexport function useKeysPressed(keys: string[], enabled = true) {\n  const apple = useIsApple();\n  const [held, setHeld] = useState<boolean[]>(() => keys.map(() => false));\n  const signature = keys.join(\"+\");\n\n  useEffect(() => {\n    const tokens = signature.split(\"+\");\n    setHeld(tokens.map(() => false));\n    if (!enabled) return;\n    const down = new Set<number>();\n    const sync = () => setHeld(tokens.map((_, i) => down.has(i)));\n\n    const onDown = (event: KeyboardEvent) => {\n      let changed = false;\n      tokens.forEach((token, i) => {\n        if (!down.has(i) && eventMatches(token, event, apple)) {\n          down.add(i);\n          changed = true;\n        }\n      });\n      if (changed) sync();\n    };\n    const onUp = (event: KeyboardEvent) => {\n      // macOS swallows keyup for other keys while ⌘ is held, so releasing a\n      // modifier releases everything.\n      if (MODIFIERS.has(event.key.toLowerCase()) && apple) {\n        down.clear();\n        sync();\n        return;\n      }\n      let changed = false;\n      tokens.forEach((token, i) => {\n        if (down.has(i) && eventMatches(token, event, apple)) {\n          down.delete(i);\n          changed = true;\n        }\n      });\n      if (changed) sync();\n    };\n    const reset = () => {\n      down.clear();\n      sync();\n    };\n    window.addEventListener(\"keydown\", onDown);\n    window.addEventListener(\"keyup\", onUp);\n    window.addEventListener(\"blur\", reset);\n    return () => {\n      window.removeEventListener(\"keydown\", onDown);\n      window.removeEventListener(\"keyup\", onUp);\n      window.removeEventListener(\"blur\", reset);\n    };\n  }, [signature, enabled, apple]);\n\n  return held;\n}\n\n/* -------------------------------------------------------------------------- */\n/*                                    Kbd                                     */\n/* -------------------------------------------------------------------------- */\n\ntype KbdSize = \"sm\" | \"md\";\n\nconst SIZE: Record<KbdSize, string> = {\n  sm: \"h-5 min-w-5 px-1 text-[11px] rounded-[5px] border-b-[2px]\",\n  md: \"h-6 min-w-6 px-1.5 text-xs rounded-md border-b-[3px]\",\n};\n\n// How far the key drops when pressed, in px — matches the thicker bottom edge\n// so the cap visibly sinks into its base.\nconst DIP: Record<KbdSize, number> = { sm: 1.5, md: 2 };\n\nexport interface KbdProps {\n  /** A key token (\"mod\", \"shift\", \"k\", \"enter\"…) or any custom content. */\n  children: ReactNode;\n  size?: KbdSize;\n  /** Show the key pressed (spring dip). */\n  pressed?: boolean;\n  /** Dip when the real key is held. Needs `children` to be a key token string. */\n  listen?: boolean;\n  className?: string;\n}\n\n/**\n * A single keyboard key. String children are treated as key tokens, so\n * `<Kbd>mod</Kbd>` renders ⌘ on Apple platforms and Ctrl elsewhere.\n */\nexport function Kbd({\n  children,\n  size = \"md\",\n  pressed = false,\n  listen = false,\n  className,\n}: KbdProps) {\n  const apple = useIsApple();\n  const token = typeof children === \"string\" ? children : null;\n  const [held] = useKeysPressed(token ? [token] : [], listen && token !== null);\n  const label = token ? keyLabel(token, apple) : null;\n\n  return (\n    <KeyCap\n      size={size}\n      pressed={pressed || Boolean(held)}\n      title={label?.name}\n      className={className}\n    >\n      {label ? label.glyph : children}\n    </KeyCap>\n  );\n}\n\nfunction KeyCap({\n  children,\n  size,\n  pressed,\n  title,\n  className,\n}: {\n  children: ReactNode;\n  size: KbdSize;\n  pressed: boolean;\n  title?: string;\n  className?: string;\n}) {\n  const reduce = useReducedMotion() ?? false;\n  return (\n    <motion.kbd\n      title={title}\n      data-pressed={pressed ? \"\" : undefined}\n      initial={false}\n      animate={\n        reduce\n          ? { opacity: pressed ? 0.7 : 1 }\n          : { y: pressed ? DIP[size] : 0, scale: pressed ? 0.96 : 1 }\n      }\n      transition={reduce ? { duration: 0.1 } : SPRING_PRESS}\n      className={cn(\n        \"inline-flex select-none items-center justify-center border border-border border-b-border-strong bg-muted font-sans font-medium leading-none text-muted-foreground\",\n        \"data-[pressed]:border-b-border data-[pressed]:bg-foreground/[0.08] data-[pressed]:text-foreground\",\n        SIZE[size],\n        className,\n      )}\n    >\n      {children}\n    </motion.kbd>\n  );\n}\n\nexport interface KbdComboProps {\n  /** Key tokens, e.g. [\"mod\", \"shift\", \"p\"]. */\n  keys: string[];\n  size?: KbdSize;\n  /** Show every key pressed. */\n  pressed?: boolean;\n  /** Dip each key while the real key is held. */\n  listen?: boolean;\n  /** Text between keys. Default none on Apple, \"+\" elsewhere. */\n  separator?: ReactNode;\n  className?: string;\n}\n\n/**\n * A key combination: one chip per key, platform-aware, with a spoken label\n * (\"Command + Shift + P\") for assistive tech.\n */\nexport function KbdCombo({\n  keys,\n  size = \"md\",\n  pressed = false,\n  listen = false,\n  separator,\n  className,\n}: KbdComboProps) {\n  const apple = useIsApple();\n  const held = useKeysPressed(keys, listen);\n  const labels = keys.map((key) => keyLabel(key, apple));\n  const sep = separator === undefined ? (apple ? null : \"+\") : separator;\n\n  return (\n    <kbd\n      className={cn(\n        \"inline-flex items-center gap-1 font-sans text-muted-foreground\",\n        className,\n      )}\n    >\n      <span className=\"sr-only\">{labels.map((l) => l.name).join(\" + \")}</span>\n      <span aria-hidden=\"true\" className=\"inline-flex items-center gap-1\">\n        {labels.map((label, i) => (\n          <Fragment key={keys[i]}>\n            {i > 0 && sep !== null ? (\n              <span className=\"text-[10px] text-muted-foreground/70\">{sep}</span>\n            ) : null}\n            <KeyCap size={size} pressed={pressed || Boolean(held[i])}>\n              {label.glyph}\n            </KeyCap>\n          </Fragment>\n        ))}\n      </span>\n    </kbd>\n  );\n}\n"},{"path":"lib/ease.ts","type":"registry:lib","target":"@lib/ease.ts","content":"// Shared motion tokens. Easing curves mirror the CSS custom properties in\n// globals.css; springs are the canonical physics used across components.\n// Strong custom variants — defaults like `ease-in`/`ease-out` feel weak.\n\nexport const EASE_OUT = [0.16, 1, 0.3, 1] as const;\nexport const EASE_IN_OUT = [0.77, 0, 0.175, 1] as const;\nexport const EASE_DRAWER = [0.32, 0.72, 0, 1] as const;\n\n/** CSS string form of EASE_OUT for inline style transitions. */\nexport const EASE_OUT_CSS = \"cubic-bezier(0.16, 1, 0.3, 1)\";\n\n/** Press feedback on buttons and other tappable surfaces. */\nexport const SPRING_PRESS = {\n  type: \"spring\",\n  stiffness: 500,\n  damping: 30,\n  mass: 0.6,\n} as const;\n\n/** Content swaps — label/icon slots trading places inside a control. */\nexport const SPRING_SWAP = {\n  type: \"spring\",\n  stiffness: 460,\n  damping: 30,\n  mass: 0.55,\n} as const;\n\n/** Overlay panel entrances — modals and sheets summoned by pointer. */\nexport const SPRING_PANEL = {\n  type: \"spring\",\n  stiffness: 420,\n  damping: 40,\n  mass: 0.5,\n} as const;\n\n/** Shared-layout glides — pills, indicators and panels morphing between positions. */\nexport const SPRING_LAYOUT = {\n  type: \"spring\",\n  stiffness: 360,\n  damping: 32,\n  mass: 0.6,\n} as const;\n\n/** Cursor-follow physics for decorative mouse tracking (magnetic, tilt, dock). */\nexport const SPRING_MOUSE = {\n  stiffness: 200,\n  damping: 15,\n  mass: 0.3,\n} as const;\n\n/** Dragged handles and fills (sliders) — critically damped `useSpring` config,\n * so the value follows the pointer butterily and never rebounds off an end. */\nexport const SPRING_GLIDE = {\n  stiffness: 700,\n  damping: 50,\n  mass: 0.5,\n} as const;\n"},{"path":"lib/hooks/use-dismiss.ts","type":"registry:hook","target":"@lib/hooks/use-dismiss.ts","content":"\"use client\";\n\nimport { type RefObject, useEffect } from \"react\";\n\n/**\n * What the dismissing gesture does to the control it landed on.\n *\n * `\"pass-through\"` is the platform norm (native popover light-dismiss): the\n * tap closes the overlay *and* activates whatever was under it. Use\n * `\"consume\"` where the open overlay sits over or beside controls that would\n * be costly to trigger by accident — the dismissal then swallows the\n * activation too, so the gesture only closes.\n */\nexport type DismissBehavior = \"pass-through\" | \"consume\";\n\nexport interface DismissOptions {\n  /** Default `\"pass-through\"`. */\n  behavior?: DismissBehavior;\n  /** Dismiss on Escape as well. Default true. */\n  escape?: boolean;\n  /** Return true for an outside target that should *not* dismiss. Must be stable. */\n  ignore?: (target: Element) => boolean;\n}\n\n/**\n * What every currently open dismiss scope counts as inside itself. A consumed\n * dismissal reads this to tell a stray gesture from one that belongs to an\n * overlay in front of it: overlays have no shared z-order to consult, but the\n * one the gesture landed in has said as much by registering it.\n */\nconst openScopes = new Set<(target: Element) => boolean>();\n\nfunction claimedByAnotherScope(\n  self: (target: Element) => boolean,\n  target: Element,\n) {\n  for (const scope of openScopes) {\n    if (scope !== self && scope(target)) return true;\n  }\n  return false;\n}\n\n// preventDefault on pointerdown does not suppress the click that follows, so\n// consuming a gesture means swallowing that click itself. The swallower\n// deliberately outlives the effect that installed it — the dismissal it\n// belongs to has already unmounted or re-rendered by the time the click lands.\n// It releases on that click, or on the next gesture if the pointer is dragged\n// away and no click ever arrives, so it can never eat a later one. A keydown\n// releases it too: a gesture that ends with neither a click nor a cancel would\n// otherwise leave it armed, and the click Enter synthesizes on some focused\n// control is not the one this dismissal was owed.\nfunction consumeActivation(source: Event) {\n  const swallow = (event: MouseEvent) => {\n    event.preventDefault();\n    event.stopPropagation();\n    release();\n  };\n  const restart = (event: Event) => {\n    if (event !== source) release();\n  };\n  const release = () => {\n    window.removeEventListener(\"click\", swallow, true);\n    window.removeEventListener(\"pointerdown\", restart, true);\n    window.removeEventListener(\"pointercancel\", restart, true);\n    window.removeEventListener(\"keydown\", release, true);\n  };\n  window.addEventListener(\"click\", swallow, true);\n  window.addEventListener(\"pointerdown\", restart, true);\n  window.addEventListener(\"pointercancel\", restart, true);\n  window.addEventListener(\"keydown\", release, true);\n}\n\n/**\n * Close an open overlay on Escape or a pointerdown outside `ref`. Pass `null`\n * for `ref` when what counts as inside isn't one element, and say so with\n * `ignore` instead.\n *\n * The pointerdown listener is capture-phase: a bubble-phase one is blinded by\n * any handler in between that stops propagation, and an overlay cannot know\n * what it is layered over. `onDismiss` and `ignore` must be stable (wrap in\n * useCallback) so the listeners aren't re-bound every render while open.\n */\nexport function useDismiss(\n  open: boolean,\n  onDismiss: () => void,\n  ref: RefObject<HTMLElement | null> | null,\n  {\n    behavior = \"pass-through\",\n    escape: dismissOnEscape = true,\n    ignore,\n  }: DismissOptions = {},\n) {\n  useEffect(() => {\n    if (!open) return;\n    const inside = (target: Element) =>\n      Boolean(ref?.current?.contains(target)) || Boolean(ignore?.(target));\n    const onKey = (event: KeyboardEvent) => {\n      if (dismissOnEscape && event.key === \"Escape\") onDismiss();\n    };\n    const onPointer = (event: PointerEvent) => {\n      const target = event.target as Element | null;\n      if (!target || inside(target)) return;\n      // Outside this overlay, but inside one that is also open: the gesture is\n      // that overlay's to answer, and swallowing its click from behind would\n      // cost the user the control they actually aimed at.\n      if (behavior === \"consume\" && !claimedByAnotherScope(inside, target)) {\n        consumeActivation(event);\n      }\n      onDismiss();\n    };\n    openScopes.add(inside);\n    window.addEventListener(\"keydown\", onKey);\n    window.addEventListener(\"pointerdown\", onPointer, true);\n    return () => {\n      openScopes.delete(inside);\n      window.removeEventListener(\"keydown\", onKey);\n      window.removeEventListener(\"pointerdown\", onPointer, true);\n    };\n  }, [open, onDismiss, ref, behavior, dismissOnEscape, ignore]);\n}\n"},{"path":"lib/utils.ts","type":"registry:lib","target":"@lib/utils.ts","content":"import { clsx, type ClassValue } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n\nexport function cn(...inputs: ClassValue[]) {\n  return twMerge(clsx(inputs))\n}\n"}]}