{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "dependencies": [],
  "description": "Base-UI-shaped APG context menu on the native Popover API. A semantics-neutral context target opens a lazy role=menu at physical pointer coordinates, keyboard center, or a 500ms long press. Controlled state is authoritative and cancellable; native light dismiss is reconciled without duplicate callbacks. Disabled items remain focusable, typeahead and direction-aware submenus are supported, and popup geometry is viewport-contained.",
  "files": [
    {
      "content": "\"use client\";\n\n/**\n * heidi-ui ContextMenu — pointer/keyboard context menu on the native Popover\n * API. The browser owns top-layer stacking and light dismiss; Heidi owns the\n * APG menu keyboard model, touch long-press intent, controlled-state contract,\n * and collision-safe CSS anchor.\n *\n * A context target is not a menu button. Trigger therefore preserves the\n * consumer's native focus/ARIA semantics instead of forcing tabIndex,\n * aria-haspopup, aria-controls, or aria-expanded onto arbitrary content.\n */\n\nimport {\n  type ComponentPropsWithoutRef,\n  createContext,\n  type CSSProperties,\n  type KeyboardEvent,\n  type MouseEvent,\n  type PointerEvent,\n  type ReactNode,\n  type Ref,\n  type SyntheticEvent,\n  type TouchEvent,\n  useCallback,\n  useContext,\n  useEffect,\n  useId,\n  useMemo,\n  useRef,\n  useState\n} from \"react\";\nimport { ariaDisabledAttrs } from \"../_internal/disabled\";\nimport {\n  closeDescendantSubmenus,\n  menuItemsIn\n} from \"../_internal/menu-items\";\nimport {\n  composeHeidiEventHandlers,\n  mergeHeidiRefs,\n  renderHeidiElement,\n  type HeidiIntrinsicHostProps\n} from \"../_internal/render-element\";\nimport {\n  pointInTriangle,\n  pointerBridgeEdge\n} from \"../_internal/safe-triangle\";\n// Aliased: `safeId` is an established local variable name in Root/SubRoot.\nimport { safeId as toSafeId } from \"../_internal/safe-id\";\nimport { useHeidiLayoutEffect } from \"../_internal/use-layout-effect\";\nimport { CONTEXT_MENU_CLASSES } from \"./context-menu.classes.generated\";\n\ntype Point = { x: number; y: number };\n\ntype NativeHostProps<Tag extends keyof HTMLElementTagNameMap> = Omit<\n  ComponentPropsWithoutRef<Tag>,\n  \"children\" | \"className\" | \"ref\" | \"style\"\n>;\n\nexport type ContextMenuRootChangeEventReason =\n  | \"escape-key\"\n  | \"focus-out\"\n  | \"item-press\"\n  | \"none\"\n  | \"outside-press\"\n  | \"trigger-press\";\n\nexport type ContextMenuRootChangeEventDetails = {\n  /** Opts a nested ContextMenu back into bubbling its opening gesture. */\n  allowPropagation: () => void;\n  /** Cancels Heidi's requested state change. */\n  cancel: () => void;\n  /** Native event which requested the change. */\n  event: Event;\n  readonly isCanceled: boolean;\n  readonly isPropagationAllowed: boolean;\n  reason: ContextMenuRootChangeEventReason;\n  trigger: Element | undefined;\n};\n\nfunction createChangeEventDetails(\n  reason: ContextMenuRootChangeEventReason,\n  event: Event,\n  trigger?: Element\n): ContextMenuRootChangeEventDetails {\n  let canceled = false;\n  let propagationAllowed = false;\n  return {\n    allowPropagation: () => {\n      propagationAllowed = true;\n    },\n    cancel: () => {\n      canceled = true;\n    },\n    event,\n    get isCanceled() {\n      return canceled;\n    },\n    get isPropagationAllowed() {\n      return propagationAllowed;\n    },\n    reason,\n    trigger\n  };\n}\n\nfunction dataState(open: boolean): \"closed\" | \"open\" {\n  return open ? \"open\" : \"closed\";\n}\n\nfunction openStateAttributes(open: boolean): Record<string, true | undefined> {\n  return open\n    ? { \"data-closed\": undefined, \"data-open\": true }\n    : { \"data-closed\": true, \"data-open\": undefined };\n}\n\ntype ContextMenuContextValue = {\n  anchorName: string;\n  clearReleaseGuard: () => void;\n  completeOpenChange: (open: boolean) => void;\n  contentId: string;\n  disabled: boolean;\n  getOpen: () => boolean;\n  highlightItemOnHover: boolean;\n  isReleaseGuarded: () => boolean;\n  /** True while a pointer is travelling from a sub-trigger to its submenu. */\n  isSubmenuPointerGraceActive: () => boolean;\n  loopFocus: boolean;\n  open: boolean;\n  consumeReinvocation: () => boolean;\n  openAt: (\n    point: Point,\n    event: Event,\n    trigger?: Element\n  ) => ContextMenuRootChangeEventDetails | null;\n  point: Point | null;\n  requestOpen: (\n    open: boolean,\n    reason: ContextMenuRootChangeEventReason,\n    event: Event,\n    trigger?: Element\n  ) => ContextMenuRootChangeEventDetails | null;\n  restoreFocus: () => void;\n  /** Hold/release the pointer-intent grace for one submenu, by id. */\n  setSubmenuPointerGrace: (id: string, active: boolean) => void;\n  setTriggerElement: (element: HTMLElement | null) => void;\n  submenuCloseDelay: number;\n  submenuOpenDelay: number;\n};\n\nconst ContextMenuContext = createContext<ContextMenuContextValue | null>(null);\n\nfunction useContextMenuContext(part: string): ContextMenuContextValue {\n  const context = useContext(ContextMenuContext);\n  if (!context) {\n    throw new Error(`ContextMenu.${part} must be rendered inside ContextMenu.Root.`);\n  }\n  return context;\n}\n\nexport type ContextMenuRootProps = {\n  children?: ReactNode;\n  defaultOpen?: boolean;\n  disabled?: boolean;\n  /** Focus the pointed item while a mouse moves over it. */\n  highlightItemOnHover?: boolean;\n  /** Wrap ArrowUp/ArrowDown at the menu boundaries. */\n  loopFocus?: boolean;\n  onOpenChange?: (\n    open: boolean,\n    details: ContextMenuRootChangeEventDetails\n  ) => void;\n  onOpenChangeComplete?: (open: boolean) => void;\n  open?: boolean;\n  submenuCloseDelay?: number;\n  submenuOpenDelay?: number;\n};\n\nexport function ContextMenuRoot({\n  children,\n  defaultOpen = false,\n  disabled = false,\n  highlightItemOnHover = true,\n  loopFocus = true,\n  onOpenChange,\n  onOpenChangeComplete,\n  open: openProp,\n  submenuCloseDelay = 300,\n  submenuOpenDelay = 100\n}: ContextMenuRootProps) {\n  const safeId = toSafeId(useId());\n  const [internalOpen, setInternalOpen] = useState(defaultOpen);\n  const [point, setPoint] = useState<Point | null>(null);\n  const controlled = openProp !== undefined;\n  const open = openProp ?? internalOpen;\n  const openRef = useRef(open);\n  openRef.current = open;\n  const triggerRef = useRef<HTMLElement | null>(null);\n  const returnFocusRef = useRef<HTMLElement | null>(null);\n  const restoreFocusOnCloseRef = useRef(false);\n  const releaseGuardRef = useRef(false);\n  const reinvocationUntilRef = useRef(0);\n  const completedStateRef = useRef(open);\n\n  const submenuPointerGraceRef = useRef(new Set<string>());\n\n  const getOpen = useCallback(() => openRef.current, []);\n  // A SET, not a flag: sibling submenus can each be mid-travel, and one\n  // finishing must not clear the other's hold.\n  const isSubmenuPointerGraceActive = useCallback(\n    () => submenuPointerGraceRef.current.size > 0,\n    []\n  );\n  const setSubmenuPointerGrace = useCallback((id: string, active: boolean) => {\n    if (active) {\n      submenuPointerGraceRef.current.add(id);\n    } else {\n      submenuPointerGraceRef.current.delete(id);\n    }\n  }, []);\n  const clearReleaseGuard = useCallback(() => {\n    releaseGuardRef.current = false;\n  }, []);\n  const isReleaseGuarded = useCallback(() => releaseGuardRef.current, []);\n  const consumeReinvocation = useCallback(() => {\n    const active = Date.now() <= reinvocationUntilRef.current;\n    reinvocationUntilRef.current = 0;\n    return active;\n  }, []);\n\n  const requestOpen = useCallback(\n    (\n      next: boolean,\n      reason: ContextMenuRootChangeEventReason,\n      event: Event,\n      trigger?: Element\n    ) => {\n      if ((next && disabled) || next === openRef.current) {\n        return null;\n      }\n      const details = createChangeEventDetails(reason, event, trigger);\n      onOpenChange?.(next, details);\n      if (details.isCanceled) {\n        return details;\n      }\n      if (next) {\n        const active = document.activeElement;\n        returnFocusRef.current = active instanceof HTMLElement ? active : null;\n        restoreFocusOnCloseRef.current = false;\n      } else {\n        restoreFocusOnCloseRef.current =\n          reason === \"escape-key\" || reason === \"item-press\";\n      }\n      if (!controlled) {\n        openRef.current = next;\n        setInternalOpen(next);\n      }\n      return details;\n    },\n    [controlled, disabled, onOpenChange]\n  );\n\n  const openAt = useCallback(\n    (nextPoint: Point, event: Event, trigger?: Element) => {\n      if (disabled) {\n        return null;\n      }\n      if (openRef.current) {\n        // A second right-click first light-dismisses the open native popover.\n        // The queued toggle follows this contextmenu event, so briefly mark it\n        // as a re-invocation rather than an outside-close request.\n        reinvocationUntilRef.current = Date.now() + 250;\n      }\n      setPoint(nextPoint);\n      releaseGuardRef.current = true;\n      return requestOpen(true, \"trigger-press\", event, trigger);\n    },\n    [disabled, requestOpen]\n  );\n\n  const completeOpenChange = useCallback(\n    (next: boolean) => {\n      if (completedStateRef.current === next) {\n        return;\n      }\n      completedStateRef.current = next;\n      onOpenChangeComplete?.(next);\n    },\n    [onOpenChangeComplete]\n  );\n\n  const restoreFocus = useCallback(() => {\n    if (!restoreFocusOnCloseRef.current) {\n      return;\n    }\n    restoreFocusOnCloseRef.current = false;\n    const remembered = returnFocusRef.current;\n    const target = remembered?.isConnected ? remembered : triggerRef.current;\n    target?.focus({ preventScroll: true });\n  }, []);\n\n  const setTriggerElement = useCallback((element: HTMLElement | null) => {\n    triggerRef.current = element;\n  }, []);\n\n  useHeidiLayoutEffect(() => {\n    if (!open || point !== null) {\n      return;\n    }\n    const rect = triggerRef.current?.getBoundingClientRect();\n    setPoint(\n      rect\n        ? { x: rect.left + rect.width / 2, y: rect.top + rect.height / 2 }\n        : { x: window.innerWidth / 2, y: window.innerHeight / 2 }\n    );\n  }, [open, point]);\n\n  const value = useMemo<ContextMenuContextValue>(\n    () => ({\n      anchorName: `--hui-context-menu-anchor-${safeId}`,\n      clearReleaseGuard,\n      completeOpenChange,\n      consumeReinvocation,\n      contentId: `hui-context-menu-${safeId}`,\n      disabled,\n      getOpen,\n      highlightItemOnHover,\n      isReleaseGuarded,\n      isSubmenuPointerGraceActive,\n      loopFocus,\n      open,\n      openAt,\n      point,\n      requestOpen,\n      restoreFocus,\n      setSubmenuPointerGrace,\n      setTriggerElement,\n      submenuCloseDelay,\n      submenuOpenDelay\n    }),\n    [\n      clearReleaseGuard,\n      completeOpenChange,\n      consumeReinvocation,\n      disabled,\n      getOpen,\n      highlightItemOnHover,\n      isReleaseGuarded,\n      isSubmenuPointerGraceActive,\n      loopFocus,\n      open,\n      openAt,\n      point,\n      requestOpen,\n      restoreFocus,\n      safeId,\n      setSubmenuPointerGrace,\n      setTriggerElement,\n      submenuCloseDelay,\n      submenuOpenDelay\n    ]\n  );\n\n  return (\n    <ContextMenuContext value={value}>\n      <span\n        aria-hidden=\"true\"\n        data-hui-part=\"context-menu-anchor\"\n        style={\n          {\n            anchorName: value.anchorName,\n            blockSize: 0,\n            inlineSize: 0,\n            left: point?.x ?? 0,\n            pointerEvents: \"none\",\n            position: \"fixed\",\n            top: point?.y ?? 0\n          } as CSSProperties\n        }\n      />\n      {children}\n    </ContextMenuContext>\n  );\n}\n\nexport type ContextMenuTriggerState = { disabled: boolean; open: boolean };\n\ntype ContextMenuTriggerNativeProps = Omit<\n  NativeHostProps<\"div\">,\n  \"onContextMenu\" | \"onKeyDown\" | \"onTouchCancel\" | \"onTouchEnd\" | \"onTouchMove\" | \"onTouchStart\"\n>;\n\nexport type ContextMenuTriggerProps = HeidiIntrinsicHostProps<\n  ContextMenuTriggerState,\n  \"div\"\n> &\n  ContextMenuTriggerNativeProps & {\n    children?: ReactNode;\n    longPressDelay?: number;\n    onContextMenu?: ComponentPropsWithoutRef<\"div\">[\"onContextMenu\"];\n    onKeyDown?: ComponentPropsWithoutRef<\"div\">[\"onKeyDown\"];\n    onTouchCancel?: ComponentPropsWithoutRef<\"div\">[\"onTouchCancel\"];\n    onTouchEnd?: ComponentPropsWithoutRef<\"div\">[\"onTouchEnd\"];\n    onTouchMove?: ComponentPropsWithoutRef<\"div\">[\"onTouchMove\"];\n    onTouchStart?: ComponentPropsWithoutRef<\"div\">[\"onTouchStart\"];\n    ref?: Ref<HTMLDivElement>;\n  };\n\nexport function ContextMenuTrigger({\n  children,\n  className,\n  longPressDelay = 500,\n  onContextMenu,\n  onKeyDown,\n  onTouchCancel,\n  onTouchEnd,\n  onTouchMove,\n  onTouchStart,\n  ref,\n  render,\n  style,\n  ...nativeProps\n}: ContextMenuTriggerProps) {\n  const context = useContextMenuContext(\"Trigger\");\n  const localRef = useRef<HTMLDivElement | null>(null);\n  const longPressTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n  const touchStartRef = useRef<Point | null>(null);\n  const touchEventRef = useRef<Event | null>(null);\n  const setTriggerRef = useCallback(\n    (element: HTMLDivElement | null) => context.setTriggerElement(element),\n    [context]\n  );\n\n  const cancelLongPress = useCallback(() => {\n    if (longPressTimerRef.current !== null) {\n      clearTimeout(longPressTimerRef.current);\n      longPressTimerRef.current = null;\n    }\n    touchStartRef.current = null;\n    touchEventRef.current = null;\n  }, []);\n\n  useEffect(() => () => cancelLongPress(), [cancelLongPress]);\n\n  const handleContextMenu = composeHeidiEventHandlers(\n    onContextMenu,\n    (event: MouseEvent<HTMLDivElement>) => {\n      if (context.disabled) {\n        return;\n      }\n      event.preventDefault();\n      const details = context.openAt(\n        { x: event.clientX, y: event.clientY },\n        event.nativeEvent,\n        event.currentTarget\n      );\n      if (!details?.isPropagationAllowed) {\n        event.stopPropagation();\n      }\n    }\n  );\n\n  const handleKeyDown = composeHeidiEventHandlers(\n    onKeyDown,\n    (event: KeyboardEvent<HTMLDivElement>) => {\n      if (\n        context.disabled ||\n        (event.key !== \"ContextMenu\" && !(event.key === \"F10\" && event.shiftKey))\n      ) {\n        return;\n      }\n      event.preventDefault();\n      const rect = event.currentTarget.getBoundingClientRect();\n      const details = context.openAt(\n        { x: rect.left + rect.width / 2, y: rect.top + rect.height / 2 },\n        event.nativeEvent,\n        event.currentTarget\n      );\n      if (!details?.isPropagationAllowed) {\n        event.stopPropagation();\n      }\n    }\n  );\n\n  const handleTouchStart = composeHeidiEventHandlers(\n    onTouchStart,\n    (event: TouchEvent<HTMLDivElement>) => {\n      cancelLongPress();\n      if (context.disabled || event.touches.length !== 1) {\n        return;\n      }\n      const touch = event.touches[0];\n      const point = { x: touch.clientX, y: touch.clientY };\n      touchStartRef.current = point;\n      touchEventRef.current = event.nativeEvent;\n      const trigger = event.currentTarget;\n      longPressTimerRef.current = setTimeout(() => {\n        longPressTimerRef.current = null;\n        const nativeEvent = touchEventRef.current;\n        if (nativeEvent && touchStartRef.current) {\n          context.openAt(touchStartRef.current, nativeEvent, trigger);\n        }\n      }, Math.max(0, longPressDelay));\n    }\n  );\n\n  const handleTouchMove = composeHeidiEventHandlers(\n    onTouchMove,\n    (event: TouchEvent<HTMLDivElement>) => {\n      const start = touchStartRef.current;\n      const touch = event.touches[0];\n      if (\n        !start ||\n        !touch ||\n        Math.hypot(touch.clientX - start.x, touch.clientY - start.y) <= 10\n      ) {\n        return;\n      }\n      cancelLongPress();\n    }\n  );\n\n  return renderHeidiElement({\n    className: CONTEXT_MENU_CLASSES.trigger,\n    dataPart: \"context-menu-trigger\",\n    element: \"div\",\n    props: {\n      ...nativeProps,\n      children,\n      \"data-disabled\": context.disabled ? true : undefined,\n      // ponytail: `true`, not `\"\"` — matches Dialog/HoverCard, the only two\n      // primitives that ship a `[data-popup-open=\"true\"]` selector. See the\n      // note at the same hook in menu.tsx.\n      \"data-popup-open\": context.open ? true : undefined,\n      \"data-pressed\": context.open ? \"\" : undefined,\n      onContextMenu: handleContextMenu,\n      onKeyDown: handleKeyDown,\n      onTouchCancel: composeHeidiEventHandlers(onTouchCancel, cancelLongPress),\n      onTouchEnd: composeHeidiEventHandlers(onTouchEnd, cancelLongPress),\n      onTouchMove: handleTouchMove,\n      onTouchStart: handleTouchStart,\n      ref: mergeHeidiRefs(localRef, setTriggerRef, ref)\n    },\n    renderProps: { className, render, style },\n    state: { disabled: context.disabled, open: context.open },\n    structuralStyle: { WebkitTouchCallout: \"none\" } as CSSProperties\n  });\n}\n\ntype PresenceApi = {\n  scheduleUnmount: (element: HTMLElement) => void;\n  shouldRender: boolean;\n};\n\nfunction usePopoverPresence(\n  open: boolean,\n  keepMounted: boolean,\n  getOpen: () => boolean\n): PresenceApi {\n  const [present, setPresent] = useState(open);\n  const runRef = useRef(0);\n\n  useHeidiLayoutEffect(() => {\n    if (open) {\n      runRef.current += 1;\n      setPresent(true);\n    }\n  }, [open]);\n\n  const scheduleUnmount = useCallback(\n    (element: HTMLElement) => {\n      if (keepMounted) {\n        return;\n      }\n      const run = ++runRef.current;\n      requestAnimationFrame(() => {\n        const animations = element\n          .getAnimations?.()\n          .filter((animation) => animation.playState !== \"finished\") ?? [];\n        const finish = () => {\n          if (run === runRef.current && !getOpen()) {\n            setPresent(false);\n          }\n        };\n        if (animations.length === 0) {\n          finish();\n          return;\n        }\n        void Promise.allSettled(animations.map((animation) => animation.finished)).then(finish);\n      });\n    },\n    [getOpen, keepMounted]\n  );\n\n  useEffect(() => () => {\n    runRef.current += 1;\n  }, []);\n\n  return { scheduleUnmount, shouldRender: keepMounted || open || present };\n}\n\nfunction completeAfterPopoverAnimations(\n  element: HTMLElement,\n  expectedOpen: boolean,\n  complete: () => void\n): void {\n  requestAnimationFrame(() => {\n    let completed = false;\n    const finishOnce = () => {\n      if (completed) {\n        return;\n      }\n      completed = true;\n      if (element.matches(\":popover-open\") === expectedOpen) {\n        complete();\n      }\n    };\n    const parseTimeList = (value: string) =>\n      value.split(\",\").map((part) => {\n        const time = part.trim();\n        return time.endsWith(\"ms\")\n          ? Number.parseFloat(time)\n          : Number.parseFloat(time) * 1000;\n      });\n    const computed = getComputedStyle(element);\n    const durations = parseTimeList(computed.transitionDuration);\n    const delays = parseTimeList(computed.transitionDelay);\n    const transitionMs = durations.reduce(\n      (maximum, duration, index) =>\n        Math.max(maximum, duration + (delays[index % delays.length] ?? 0)),\n      0\n    );\n    const fallback = window.setTimeout(finishOnce, transitionMs + 50);\n    const animations = element\n      .getAnimations?.()\n      .filter((animation) => animation.playState !== \"finished\") ?? [];\n    if (animations.length === 0) {\n      window.clearTimeout(fallback);\n      finishOnce();\n      return;\n    }\n    void Promise.allSettled(animations.map((animation) => animation.finished)).then(() => {\n      window.clearTimeout(fallback);\n      finishOnce();\n    });\n  });\n}\n\ntype TypeaheadState = {\n  buffer: string;\n  timer: ReturnType<typeof setTimeout> | null;\n};\n\nfunction normalizedItemLabel(item: HTMLElement): string {\n  return (item.dataset.label ?? item.getAttribute(\"aria-label\") ?? item.textContent ?? \"\")\n    .trim()\n    .toLocaleLowerCase();\n}\n\nfunction runTypeahead(\n  menu: HTMLElement,\n  key: string,\n  state: TypeaheadState\n): void {\n  if (state.timer !== null) {\n    clearTimeout(state.timer);\n  }\n  state.buffer += key.toLocaleLowerCase();\n  state.timer = setTimeout(() => {\n    state.buffer = \"\";\n    state.timer = null;\n  }, 500);\n\n  const query = [...state.buffer].every((character) => character === state.buffer[0])\n    ? state.buffer[0]\n    : state.buffer;\n  const items = menuItemsIn(menu, { includeDisabled: true });\n  const activeIndex = items.indexOf(document.activeElement as HTMLElement);\n  const ordered = [...items.slice(activeIndex + 1), ...items.slice(0, activeIndex + 1)];\n  ordered.find((item) => normalizedItemLabel(item).startsWith(query))?.focus();\n}\n\nfunction moveMenuFocus(\n  menu: HTMLElement,\n  key: \"ArrowDown\" | \"ArrowUp\" | \"End\" | \"Home\",\n  loopFocus: boolean\n): void {\n  const items = menuItemsIn(menu, { includeDisabled: true });\n  if (items.length === 0) {\n    return;\n  }\n  const activeIndex = items.indexOf(document.activeElement as HTMLElement);\n  let nextIndex: number;\n  if (key === \"Home\") {\n    nextIndex = 0;\n  } else if (key === \"End\") {\n    nextIndex = items.length - 1;\n  } else if (key === \"ArrowUp\") {\n    nextIndex = activeIndex < 0 ? items.length - 1 : activeIndex - 1;\n    if (nextIndex < 0) {\n      nextIndex = loopFocus ? items.length - 1 : 0;\n    }\n  } else {\n    nextIndex = activeIndex < 0 ? 0 : activeIndex + 1;\n    if (nextIndex >= items.length) {\n      nextIndex = loopFocus ? 0 : items.length - 1;\n    }\n  }\n  closeDescendantSubmenus(menu);\n  items[nextIndex]?.focus({ preventScroll: true });\n}\n\nexport type ContextMenuContentState = { open: boolean };\n\ntype ContextMenuContentNativeProps = Omit<\n  NativeHostProps<\"div\">,\n  \"aria-label\" | \"id\" | \"onKeyDown\" | \"onPointerDown\" | \"onPointerMove\" | \"role\" | \"tabIndex\"\n>;\n\nexport type ContextMenuContentProps = HeidiIntrinsicHostProps<\n  ContextMenuContentState,\n  \"div\"\n> &\n  ContextMenuContentNativeProps & {\n    /** Cross-axis offset in CSS pixels. */\n    alignOffset?: number;\n    \"aria-label\"?: string;\n    children?: ReactNode;\n    keepMounted?: boolean;\n    /** Accessible name. `aria-label` takes precedence. */\n    label?: string;\n    onKeyDown?: ComponentPropsWithoutRef<\"div\">[\"onKeyDown\"];\n    onPointerDown?: ComponentPropsWithoutRef<\"div\">[\"onPointerDown\"];\n    onPointerMove?: ComponentPropsWithoutRef<\"div\">[\"onPointerMove\"];\n    onToggle?: (event: SyntheticEvent<HTMLDivElement>) => void;\n    ref?: Ref<HTMLDivElement>;\n    /** Main-axis gap in CSS pixels. Defaults to 0: the anchor is the pointer. */\n    sideOffset?: number;\n  };\n\nexport function ContextMenuContent({\n  alignOffset = 0,\n  \"aria-label\": ariaLabel,\n  children,\n  className,\n  keepMounted = false,\n  label = \"Context menu\",\n  onKeyDown,\n  onPointerDown,\n  onPointerMove,\n  onToggle,\n  ref,\n  render,\n  sideOffset = 0,\n  style,\n  ...nativeProps\n}: ContextMenuContentProps) {\n  const context = useContextMenuContext(\"Content\");\n  const localRef = useRef<HTMLDivElement | null>(null);\n  const typeaheadRef = useRef<TypeaheadState>({ buffer: \"\", timer: null });\n  const { scheduleUnmount, shouldRender } = usePopoverPresence(\n    context.open,\n    keepMounted,\n    context.getOpen\n  );\n\n  useEffect(() => () => {\n    if (typeaheadRef.current.timer !== null) {\n      clearTimeout(typeaheadRef.current.timer);\n    }\n  }, []);\n\n  useEffect(() => {\n    const element = localRef.current;\n    if (!element || typeof element.showPopover !== \"function\") {\n      return;\n    }\n    const nativeOpen = element.matches(\":popover-open\");\n    if (context.open && !nativeOpen) {\n      const timer = window.setTimeout(() => {\n        if (context.getOpen() && element.isConnected && !element.matches(\":popover-open\")) {\n          try {\n            element.showPopover();\n          } catch {\n            // An ancestor popover may be transitioning; the next render retries.\n          }\n        }\n      }, 0);\n      return () => window.clearTimeout(timer);\n    }\n    if (!context.open && nativeOpen) {\n      try {\n        element.hidePopover();\n      } catch {\n        // Already dismissed by the browser.\n      }\n    }\n    if (!context.open && !nativeOpen) {\n      scheduleUnmount(element);\n    }\n    return undefined;\n  }, [context, scheduleUnmount]);\n\n  if (!shouldRender) {\n    return null;\n  }\n\n  const handleToggle = composeHeidiEventHandlers(\n    onToggle,\n    (event: SyntheticEvent<HTMLDivElement>) => {\n      if (event.target !== event.currentTarget) {\n        return;\n      }\n      const element = event.currentTarget;\n      const nativeToggle = event.nativeEvent as Event & { newState?: string };\n      const nativeOpen = nativeToggle.newState === \"open\";\n      if (!nativeOpen && context.getOpen() && context.consumeReinvocation()) {\n        requestAnimationFrame(() => {\n          if (context.getOpen() && element.isConnected && !element.matches(\":popover-open\")) {\n            try {\n              element.showPopover();\n            } catch {\n              // The regular open-state effect is the final retry path.\n            }\n          }\n        });\n        return;\n      }\n      if (nativeOpen !== context.getOpen()) {\n        const details = context.requestOpen(\n          nativeOpen,\n          nativeOpen ? \"none\" : \"outside-press\",\n          nativeToggle,\n          element\n        );\n        requestAnimationFrame(() => {\n          if (!element.isConnected) {\n            return;\n          }\n          if (context.getOpen() && !element.matches(\":popover-open\")) {\n            try {\n              element.showPopover();\n            } catch {\n              // A canceled/controlled dismissal retries on the next effect.\n            }\n          } else if (\n            !context.getOpen() &&\n            element.matches(\":popover-open\") &&\n            (details?.isCanceled || nativeOpen)\n          ) {\n            try {\n              element.hidePopover();\n            } catch {\n              // Already reconciled.\n            }\n          }\n        });\n      }\n      completeAfterPopoverAnimations(element, nativeOpen, () => {\n        if (context.getOpen() === nativeOpen) {\n          context.completeOpenChange(nativeOpen);\n        }\n      });\n      if (nativeOpen) {\n        queueMicrotask(() => {\n          menuItemsIn(element, { includeDisabled: true })[0]?.focus({\n            preventScroll: true\n          });\n        });\n      } else {\n        scheduleUnmount(element);\n        requestAnimationFrame(context.restoreFocus);\n      }\n    }\n  );\n\n  const handleKeyDown = composeHeidiEventHandlers(\n    onKeyDown,\n    (event: KeyboardEvent<HTMLDivElement>) => {\n      const sourceMenu = (event.target as HTMLElement | null)?.closest('[role=\"menu\"]');\n      if (sourceMenu && sourceMenu !== event.currentTarget) {\n        return;\n      }\n      if (event.key === \"Escape\") {\n        event.preventDefault();\n        event.stopPropagation();\n        context.requestOpen(false, \"escape-key\", event.nativeEvent, event.currentTarget);\n        return;\n      }\n      if (event.key === \"Tab\") {\n        context.requestOpen(false, \"focus-out\", event.nativeEvent, event.currentTarget);\n        return;\n      }\n      if (\n        event.key === \"ArrowDown\" ||\n        event.key === \"ArrowUp\" ||\n        event.key === \"End\" ||\n        event.key === \"Home\"\n      ) {\n        event.preventDefault();\n        moveMenuFocus(event.currentTarget, event.key, context.loopFocus);\n        return;\n      }\n      if (\n        event.key.length === 1 &&\n        event.key !== \" \" &&\n        !event.altKey &&\n        !event.ctrlKey &&\n        !event.metaKey\n      ) {\n        runTypeahead(event.currentTarget, event.key, typeaheadRef.current);\n      }\n    }\n  );\n\n  const state = { open: context.open };\n  return renderHeidiElement({\n    className: CONTEXT_MENU_CLASSES.content,\n    dataPart: \"context-menu-content\",\n    element: \"div\",\n    props: {\n      ...nativeProps,\n      ...openStateAttributes(context.open),\n      \"aria-label\": ariaLabel ?? label,\n      children,\n      \"data-state\": dataState(context.open),\n      id: context.contentId,\n      onKeyDown: handleKeyDown,\n      onPointerDown: composeHeidiEventHandlers(onPointerDown, context.clearReleaseGuard),\n      onPointerMove: composeHeidiEventHandlers(onPointerMove, context.clearReleaseGuard),\n      onToggle: handleToggle,\n      popover: \"auto\",\n      ref: mergeHeidiRefs(localRef, ref),\n      role: \"menu\",\n      tabIndex: -1\n    },\n    renderProps: { className, render, style },\n    state,\n    structuralStyle: {\n      \"--_hui-context-menu-align-offset\": `${alignOffset}px`,\n      \"--_hui-context-menu-side-offset\": `${Math.max(0, sideOffset)}px`,\n      positionAnchor: context.anchorName\n    } as CSSProperties\n  });\n}\n\nexport const ContextMenuPopup = ContextMenuContent;\nexport type ContextMenuPopupProps = ContextMenuContentProps;\n\nexport type ContextMenuItemState = { disabled: boolean; highlighted: boolean };\n\ntype ContextMenuItemNativeProps = Omit<\n  NativeHostProps<\"div\">,\n  \"aria-disabled\" | \"onBlur\" | \"onClick\" | \"onFocus\" | \"onKeyDown\" | \"onPointerMove\" | \"role\" | \"tabIndex\"\n>;\n\nexport type ContextMenuItemProps = HeidiIntrinsicHostProps<\n  ContextMenuItemState,\n  \"div\"\n> &\n  ContextMenuItemNativeProps & {\n    children?: ReactNode;\n    closeOnClick?: boolean;\n    disabled?: boolean;\n    label?: string;\n    /** Set true only when `render` returns a native button. */\n    nativeButton?: boolean;\n    onBlur?: ComponentPropsWithoutRef<\"div\">[\"onBlur\"];\n    onClick?: ComponentPropsWithoutRef<\"div\">[\"onClick\"];\n    onFocus?: ComponentPropsWithoutRef<\"div\">[\"onFocus\"];\n    onKeyDown?: ComponentPropsWithoutRef<\"div\">[\"onKeyDown\"];\n    onPointerMove?: ComponentPropsWithoutRef<\"div\">[\"onPointerMove\"];\n    onSelect?: () => void;\n    ref?: Ref<HTMLDivElement>;\n  };\n\nexport function ContextMenuItem({\n  children,\n  className,\n  closeOnClick = true,\n  disabled = false,\n  label,\n  nativeButton = false,\n  onBlur,\n  onClick,\n  onFocus,\n  onKeyDown,\n  onPointerMove,\n  onSelect,\n  ref,\n  render,\n  style,\n  ...nativeProps\n}: ContextMenuItemProps) {\n  const context = useContextMenuContext(\"Item\");\n  const [highlighted, setHighlighted] = useState(false);\n  const state = useMemo(() => ({ disabled, highlighted }), [disabled, highlighted]);\n\n  const handleClick = (event: MouseEvent<HTMLDivElement>) => {\n    if (disabled || (context.isReleaseGuarded() && event.detail > 0)) {\n      event.preventDefault();\n      return;\n    }\n    onClick?.(event);\n    if (event.defaultPrevented) {\n      return;\n    }\n    onSelect?.();\n    if (closeOnClick) {\n      context.requestOpen(false, \"item-press\", event.nativeEvent, event.currentTarget);\n    }\n  };\n\n  const handleKeyDown = composeHeidiEventHandlers(\n    onKeyDown,\n    (event: KeyboardEvent<HTMLDivElement>) => {\n      if (nativeButton || disabled || event.target !== event.currentTarget) {\n        return;\n      }\n      if (event.key === \" \" || event.key === \"Enter\") {\n        event.preventDefault();\n        if (!event.repeat) {\n          event.currentTarget.click();\n        }\n      }\n    }\n  );\n\n  return renderHeidiElement({\n    className: CONTEXT_MENU_CLASSES.item,\n    dataPart: \"context-menu-item\",\n    element: \"div\",\n    props: {\n      ...nativeProps,\n      ...ariaDisabledAttrs(disabled),\n      children,\n      \"data-highlighted\": highlighted ? \"true\" : undefined,\n      \"data-label\": label,\n      onBlur: composeHeidiEventHandlers(onBlur, () => setHighlighted(false)),\n      onClick: handleClick,\n      onFocus: composeHeidiEventHandlers(onFocus, () => setHighlighted(true)),\n      onKeyDown: handleKeyDown,\n      onPointerMove: composeHeidiEventHandlers(\n        onPointerMove,\n        (event: PointerEvent<HTMLDivElement>) => {\n          context.clearReleaseGuard();\n          // Both effects are suppressed while a submenu holds the pointer\n          // grace: this item is only under the cursor because it sits on the\n          // diagonal to an open submenu. Closing it OR stealing focus to here\n          // ends that journey.\n          const pointerGraceActive = context.isSubmenuPointerGraceActive();\n          const menu = event.currentTarget.closest('[role=\"menu\"]');\n          if (menu instanceof HTMLElement && !pointerGraceActive) {\n            closeDescendantSubmenus(menu);\n          }\n          if (\n            !pointerGraceActive &&\n            context.highlightItemOnHover &&\n            event.pointerType !== \"touch\"\n          ) {\n            event.currentTarget.focus({ preventScroll: true });\n          }\n        }\n      ),\n      ref,\n      role: \"menuitem\",\n      tabIndex: -1\n    },\n    renderProps: { className, render, style },\n    state\n  });\n}\n\nexport type ContextMenuLinkItemState = ContextMenuItemState;\n\ntype ContextMenuLinkItemNativeProps = Omit<\n  NativeHostProps<\"a\">,\n  \"aria-disabled\" | \"onBlur\" | \"onClick\" | \"onFocus\" | \"onKeyDown\" | \"onPointerMove\" | \"role\" | \"tabIndex\"\n>;\n\nexport type ContextMenuLinkItemProps = HeidiIntrinsicHostProps<\n  ContextMenuLinkItemState,\n  \"a\"\n> &\n  ContextMenuLinkItemNativeProps & {\n    children?: ReactNode;\n    closeOnClick?: boolean;\n    disabled?: boolean;\n    label?: string;\n    onBlur?: ComponentPropsWithoutRef<\"a\">[\"onBlur\"];\n    onClick?: ComponentPropsWithoutRef<\"a\">[\"onClick\"];\n    onFocus?: ComponentPropsWithoutRef<\"a\">[\"onFocus\"];\n    onKeyDown?: ComponentPropsWithoutRef<\"a\">[\"onKeyDown\"];\n    onPointerMove?: ComponentPropsWithoutRef<\"a\">[\"onPointerMove\"];\n    onSelect?: () => void;\n    ref?: Ref<HTMLAnchorElement>;\n  };\n\n/** A semantic menu link. Navigation does not close the menu by default. */\nexport function ContextMenuLinkItem({\n  children,\n  className,\n  closeOnClick = false,\n  disabled = false,\n  label,\n  onBlur,\n  onClick,\n  onFocus,\n  onKeyDown,\n  onPointerMove,\n  onSelect,\n  ref,\n  render,\n  style,\n  ...nativeProps\n}: ContextMenuLinkItemProps) {\n  const context = useContextMenuContext(\"LinkItem\");\n  const [highlighted, setHighlighted] = useState(false);\n  const state = useMemo(() => ({ disabled, highlighted }), [disabled, highlighted]);\n\n  const handleClick = (event: MouseEvent<HTMLAnchorElement>) => {\n    if (disabled || (context.isReleaseGuarded() && event.detail > 0)) {\n      event.preventDefault();\n      return;\n    }\n    onClick?.(event);\n    if (event.defaultPrevented) {\n      return;\n    }\n    onSelect?.();\n    if (closeOnClick) {\n      context.requestOpen(false, \"item-press\", event.nativeEvent, event.currentTarget);\n    }\n  };\n\n  return renderHeidiElement({\n    className: CONTEXT_MENU_CLASSES.linkItem,\n    dataPart: \"context-menu-link-item\",\n    element: \"a\",\n    props: {\n      ...nativeProps,\n      ...ariaDisabledAttrs(disabled),\n      children,\n      \"data-highlighted\": highlighted ? \"true\" : undefined,\n      \"data-label\": label,\n      onBlur: composeHeidiEventHandlers(onBlur, () => setHighlighted(false)),\n      onClick: handleClick,\n      onFocus: composeHeidiEventHandlers(onFocus, () => setHighlighted(true)),\n      onKeyDown: composeHeidiEventHandlers(\n        onKeyDown,\n        (event: KeyboardEvent<HTMLAnchorElement>) => {\n          if (!disabled && event.key === \" \" && !event.repeat) {\n            event.preventDefault();\n            event.currentTarget.click();\n          }\n        }\n      ),\n      onPointerMove: composeHeidiEventHandlers(\n        onPointerMove,\n        (event: PointerEvent<HTMLAnchorElement>) => {\n          context.clearReleaseGuard();\n          // Same grace guard as Item: a link on the diagonal is still travel.\n          const pointerGraceActive = context.isSubmenuPointerGraceActive();\n          const menu = event.currentTarget.closest('[role=\"menu\"]');\n          if (menu instanceof HTMLElement && !pointerGraceActive) {\n            closeDescendantSubmenus(menu);\n          }\n          if (\n            !pointerGraceActive &&\n            context.highlightItemOnHover &&\n            event.pointerType !== \"touch\"\n          ) {\n            event.currentTarget.focus({ preventScroll: true });\n          }\n        }\n      ),\n      ref,\n      role: \"menuitem\",\n      tabIndex: -1\n    },\n    renderProps: { className, render, style },\n    state\n  });\n}\n\n/** The native pointer event behind a handler's `Event`, when there is one. */\nfunction asPointerEvent(event: Event): globalThis.PointerEvent | null {\n  return typeof globalThis.PointerEvent === \"function\" &&\n    event instanceof globalThis.PointerEvent\n    ? event\n    : null;\n}\n\ntype ContextMenuSubContextValue = {\n  anchorName: string;\n  cancelPointerTimers: () => void;\n  contentId: string;\n  focusOnOpen: () => boolean;\n  getOpen: () => boolean;\n  open: boolean;\n  requestOpen: (\n    open: boolean,\n    reason: ContextMenuRootChangeEventReason,\n    event: Event,\n    trigger?: Element,\n    focus?: boolean\n  ) => ContextMenuRootChangeEventDetails | null;\n  schedulePointerClose: (event: Event, trigger: Element) => void;\n  schedulePointerOpen: (event: Event, trigger: Element) => void;\n  triggerId: string;\n};\n\nconst ContextMenuSubContext = createContext<ContextMenuSubContextValue | null>(null);\n\nfunction useContextMenuSubContext(part: string): ContextMenuSubContextValue {\n  const context = useContext(ContextMenuSubContext);\n  if (!context) {\n    throw new Error(`ContextMenu.${part} must be rendered inside ContextMenu.Sub.`);\n  }\n  return context;\n}\n\nexport type ContextMenuSubProps = {\n  children?: ReactNode;\n  defaultOpen?: boolean;\n  onOpenChange?: (\n    open: boolean,\n    details: ContextMenuRootChangeEventDetails\n  ) => void;\n  open?: boolean;\n};\n\nexport function ContextMenuSub({\n  children,\n  defaultOpen = false,\n  onOpenChange,\n  open: openProp\n}: ContextMenuSubProps) {\n  const root = useContextMenuContext(\"Sub\");\n  const safeId = toSafeId(useId());\n  const [internalOpen, setInternalOpen] = useState(defaultOpen);\n  const controlled = openProp !== undefined;\n  const open = openProp ?? internalOpen;\n  const openRef = useRef(open);\n  openRef.current = open;\n  const focusOnOpenRef = useRef(false);\n  const openTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n  const closeTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n  const graceCleanupRef = useRef<(() => void) | null>(null);\n  const contentId = `hui-context-menu-sub-${safeId}`;\n  const triggerId = `hui-context-menu-sub-trigger-${safeId}`;\n\n  const getOpen = useCallback(() => openRef.current, []);\n  const cancelPointerTimers = useCallback(() => {\n    if (openTimerRef.current !== null) {\n      clearTimeout(openTimerRef.current);\n      openTimerRef.current = null;\n    }\n    if (closeTimerRef.current !== null) {\n      clearTimeout(closeTimerRef.current);\n      closeTimerRef.current = null;\n    }\n    graceCleanupRef.current?.();\n    graceCleanupRef.current = null;\n    root.setSubmenuPointerGrace(safeId, false);\n  }, [root, safeId]);\n\n  useEffect(() => () => cancelPointerTimers(), [cancelPointerTimers]);\n\n  const requestOpen = useCallback(\n    (\n      next: boolean,\n      reason: ContextMenuRootChangeEventReason,\n      event: Event,\n      trigger?: Element,\n      focus = false\n    ) => {\n      if (next === openRef.current) {\n        return null;\n      }\n      const details = createChangeEventDetails(reason, event, trigger);\n      onOpenChange?.(next, details);\n      if (details.isCanceled) {\n        return details;\n      }\n      focusOnOpenRef.current = next && focus;\n      if (!controlled) {\n        openRef.current = next;\n        setInternalOpen(next);\n      }\n      return details;\n    },\n    [controlled, onOpenChange]\n  );\n\n  const schedulePointerOpen = useCallback(\n    (event: Event, trigger: Element) => {\n      cancelPointerTimers();\n      const run = () => {\n        openTimerRef.current = null;\n        requestOpen(true, \"trigger-press\", event, trigger, false);\n      };\n      if (root.submenuOpenDelay <= 0) {\n        run();\n      } else {\n        openTimerRef.current = setTimeout(run, root.submenuOpenDelay);\n      }\n    },\n    [cancelPointerTimers, requestOpen, root.submenuOpenDelay]\n  );\n\n  // ponytail: the pointer leaving a sub-trigger is not intent to close — the\n  // submenu opens beside its trigger, so reaching it means travelling\n  // diagonally across the sibling item below, whose own pointermove used to\n  // kill the submenu mid-journey. Menu already solved this with a safe\n  // triangle from the departure point to the destination's near edge; the same\n  // geometry now guards ContextMenu, which Radix and Base UI both serve from\n  // the same submenu implementation as their dropdown.\n  //\n  // Rejected: simply lengthening `submenuCloseDelay`. A timer cannot tell\n  // \"heading for the submenu\" from \"left and stopped\", so it either closes\n  // during travel or leaves a stale submenu open under a stationary pointer.\n  // The grace ends the moment the pointer leaves the triangle, not on a clock.\n  const schedulePointerClose = useCallback(\n    (event: Event, trigger: Element) => {\n      cancelPointerTimers();\n      const run = () => {\n        closeTimerRef.current = null;\n        // Drop the document listener here too: once the close has fired there\n        // is nothing left to protect, and a pointer that never leaves the\n        // triangle would otherwise keep it attached until the next hover.\n        graceCleanupRef.current?.();\n        graceCleanupRef.current = null;\n        root.setSubmenuPointerGrace(safeId, false);\n        requestOpen(false, \"focus-out\", event, trigger);\n      };\n      const schedule = (minimumDelay = 0) => {\n        const delay = Math.max(root.submenuCloseDelay, minimumDelay);\n        if (!Number.isFinite(delay) || delay <= 0) {\n          run();\n        } else {\n          closeTimerRef.current = setTimeout(run, delay);\n        }\n      };\n\n      const pointer = asPointerEvent(event);\n      const content = document.getElementById(contentId);\n      const triggerElement = document.getElementById(triggerId);\n      if (\n        pointer?.pointerType === \"mouse\" &&\n        openRef.current &&\n        content &&\n        triggerElement &&\n        (trigger === triggerElement || trigger === content)\n      ) {\n        const destination = trigger === triggerElement ? content : triggerElement;\n        const start = { x: pointer.clientX, y: pointer.clientY };\n        const [a, b] = pointerBridgeEdge(destination.getBoundingClientRect(), start);\n        const ownerDocument = trigger.ownerDocument;\n        const handleMove = (moveEvent: globalThis.PointerEvent) => {\n          const target = moveEvent.target;\n          if (\n            target instanceof Node &&\n            (content.contains(target) || triggerElement.contains(target))\n          ) {\n            cancelPointerTimers();\n            return;\n          }\n          if (\n            pointInTriangle({ x: moveEvent.clientX, y: moveEvent.clientY }, start, a, b)\n          ) {\n            return;\n          }\n          graceCleanupRef.current?.();\n          graceCleanupRef.current = null;\n          root.setSubmenuPointerGrace(safeId, false);\n          if (closeTimerRef.current !== null) {\n            clearTimeout(closeTimerRef.current);\n            closeTimerRef.current = null;\n          }\n          schedule(120);\n        };\n        ownerDocument.addEventListener(\"pointermove\", handleMove);\n        graceCleanupRef.current = () =>\n          ownerDocument.removeEventListener(\"pointermove\", handleMove);\n        root.setSubmenuPointerGrace(safeId, true);\n        schedule(400);\n        return;\n      }\n      schedule();\n    },\n    [cancelPointerTimers, contentId, requestOpen, root, safeId, triggerId]\n  );\n\n  const focusOnOpen = useCallback(() => {\n    const focus = focusOnOpenRef.current;\n    focusOnOpenRef.current = false;\n    return focus;\n  }, []);\n\n  const value = useMemo<ContextMenuSubContextValue>(\n    () => ({\n      anchorName: `--hui-context-menu-sub-anchor-${safeId}`,\n      cancelPointerTimers,\n      contentId,\n      focusOnOpen,\n      getOpen,\n      open,\n      requestOpen,\n      schedulePointerClose,\n      schedulePointerOpen,\n      triggerId\n    }),\n    [\n      cancelPointerTimers,\n      contentId,\n      focusOnOpen,\n      getOpen,\n      open,\n      requestOpen,\n      safeId,\n      schedulePointerClose,\n      schedulePointerOpen,\n      triggerId\n    ]\n  );\n\n  return (\n    <ContextMenuSubContext value={value}>{children}</ContextMenuSubContext>\n  );\n}\n\nexport type ContextMenuSubTriggerState = {\n  disabled: boolean;\n  highlighted: boolean;\n  open: boolean;\n};\n\ntype ContextMenuSubTriggerNativeProps = Omit<\n  NativeHostProps<\"div\">,\n  \"aria-controls\" | \"aria-disabled\" | \"aria-expanded\" | \"aria-haspopup\" | \"id\" | \"onBlur\" | \"onClick\" | \"onFocus\" | \"onKeyDown\" | \"onPointerEnter\" | \"onPointerLeave\" | \"role\" | \"tabIndex\"\n>;\n\nexport type ContextMenuSubTriggerProps = HeidiIntrinsicHostProps<\n  ContextMenuSubTriggerState,\n  \"div\"\n> &\n  ContextMenuSubTriggerNativeProps & {\n    children?: ReactNode;\n    disabled?: boolean;\n    onBlur?: ComponentPropsWithoutRef<\"div\">[\"onBlur\"];\n    onClick?: ComponentPropsWithoutRef<\"div\">[\"onClick\"];\n    onFocus?: ComponentPropsWithoutRef<\"div\">[\"onFocus\"];\n    onKeyDown?: ComponentPropsWithoutRef<\"div\">[\"onKeyDown\"];\n    onPointerEnter?: ComponentPropsWithoutRef<\"div\">[\"onPointerEnter\"];\n    onPointerLeave?: ComponentPropsWithoutRef<\"div\">[\"onPointerLeave\"];\n    ref?: Ref<HTMLDivElement>;\n  };\n\nexport function ContextMenuSubTrigger({\n  children,\n  className,\n  disabled = false,\n  onBlur,\n  onClick,\n  onFocus,\n  onKeyDown,\n  onPointerEnter,\n  onPointerLeave,\n  ref,\n  render,\n  style,\n  ...nativeProps\n}: ContextMenuSubTriggerProps) {\n  const root = useContextMenuContext(\"SubTrigger\");\n  const sub = useContextMenuSubContext(\"SubTrigger\");\n  const [highlighted, setHighlighted] = useState(false);\n  const state = useMemo(\n    () => ({ disabled, highlighted, open: sub.open }),\n    [disabled, highlighted, sub.open]\n  );\n\n  const handleKeyDown = composeHeidiEventHandlers(\n    onKeyDown,\n    (event: KeyboardEvent<HTMLDivElement>) => {\n      if (disabled) {\n        return;\n      }\n      const openKey =\n        getComputedStyle(event.currentTarget).direction === \"rtl\"\n          ? \"ArrowLeft\"\n          : \"ArrowRight\";\n      if (event.key === openKey || event.key === \"Enter\" || event.key === \" \") {\n        event.preventDefault();\n        event.stopPropagation();\n        sub.cancelPointerTimers();\n        sub.requestOpen(true, \"trigger-press\", event.nativeEvent, event.currentTarget, true);\n      }\n    }\n  );\n\n  return renderHeidiElement({\n    className: CONTEXT_MENU_CLASSES.subTrigger,\n    dataPart: \"context-menu-sub-trigger\",\n    element: \"div\",\n    props: {\n      ...nativeProps,\n      ...ariaDisabledAttrs(disabled),\n      \"aria-controls\": sub.contentId,\n      \"aria-expanded\": sub.open,\n      \"aria-haspopup\": \"menu\",\n      children,\n      \"data-highlighted\": highlighted ? \"true\" : undefined,\n      \"data-state\": dataState(sub.open),\n      id: sub.triggerId,\n      onBlur: composeHeidiEventHandlers(onBlur, () => setHighlighted(false)),\n      onClick: composeHeidiEventHandlers(\n        onClick,\n        (event: MouseEvent<HTMLDivElement>) => {\n          if (disabled) {\n            event.preventDefault();\n            return;\n          }\n          sub.cancelPointerTimers();\n          sub.requestOpen(true, \"trigger-press\", event.nativeEvent, event.currentTarget, false);\n        }\n      ),\n      onFocus: composeHeidiEventHandlers(onFocus, () => setHighlighted(true)),\n      onKeyDown: handleKeyDown,\n      onPointerEnter: composeHeidiEventHandlers(\n        onPointerEnter,\n        (event: PointerEvent<HTMLDivElement>) => {\n          if (!disabled) {\n            if (root.highlightItemOnHover && event.pointerType !== \"touch\") {\n              event.currentTarget.focus({ preventScroll: true });\n            }\n            sub.schedulePointerOpen(event.nativeEvent, event.currentTarget);\n          }\n        }\n      ),\n      onPointerLeave: composeHeidiEventHandlers(\n        onPointerLeave,\n        (event: PointerEvent<HTMLDivElement>) => {\n          if (!disabled) {\n            sub.schedulePointerClose(event.nativeEvent, event.currentTarget);\n          }\n        }\n      ),\n      ref,\n      role: \"menuitem\",\n      tabIndex: -1\n    },\n    renderProps: { className, render, style },\n    state,\n    structuralStyle: { anchorName: sub.anchorName } as CSSProperties\n  });\n}\n\nexport type ContextMenuSubContentState = { open: boolean };\n\ntype ContextMenuSubContentNativeProps = Omit<\n  NativeHostProps<\"div\">,\n  \"aria-labelledby\" | \"id\" | \"onKeyDown\" | \"onPointerEnter\" | \"onPointerLeave\" | \"role\" | \"tabIndex\"\n>;\n\nexport type ContextMenuSubContentProps = HeidiIntrinsicHostProps<\n  ContextMenuSubContentState,\n  \"div\"\n> &\n  ContextMenuSubContentNativeProps & {\n    /** Cross-axis offset in CSS pixels. */\n    alignOffset?: number;\n    children?: ReactNode;\n    keepMounted?: boolean;\n    onKeyDown?: ComponentPropsWithoutRef<\"div\">[\"onKeyDown\"];\n    onPointerEnter?: ComponentPropsWithoutRef<\"div\">[\"onPointerEnter\"];\n    onPointerLeave?: ComponentPropsWithoutRef<\"div\">[\"onPointerLeave\"];\n    onToggle?: (event: SyntheticEvent<HTMLDivElement>) => void;\n    ref?: Ref<HTMLDivElement>;\n    /** Main-axis gap in CSS pixels. Defaults to 0, like ContextMenu.Content. */\n    sideOffset?: number;\n  };\n\nexport function ContextMenuSubContent({\n  alignOffset = 0,\n  children,\n  className,\n  keepMounted = false,\n  onKeyDown,\n  onPointerEnter,\n  onPointerLeave,\n  onToggle,\n  ref,\n  render,\n  sideOffset = 0,\n  style,\n  ...nativeProps\n}: ContextMenuSubContentProps) {\n  const root = useContextMenuContext(\"SubContent\");\n  const sub = useContextMenuSubContext(\"SubContent\");\n  const localRef = useRef<HTMLDivElement | null>(null);\n  const pendingNativeTransitionsRef = useRef<Array<{ open: boolean }>>([]);\n  const typeaheadRef = useRef<TypeaheadState>({ buffer: \"\", timer: null });\n  const { scheduleUnmount, shouldRender } = usePopoverPresence(\n    sub.open,\n    keepMounted,\n    sub.getOpen\n  );\n\n  const synchronizeNativeOpen = useCallback((element: HTMLDivElement, open: boolean) => {\n    if (element.matches(\":popover-open\") === open) {\n      return;\n    }\n    const transition = { open };\n    pendingNativeTransitionsRef.current.push(transition);\n    try {\n      if (open) {\n        element.showPopover();\n      } else {\n        element.hidePopover();\n      }\n    } catch {\n      const index = pendingNativeTransitionsRef.current.indexOf(transition);\n      if (index >= 0) {\n        pendingNativeTransitionsRef.current.splice(index, 1);\n      }\n    }\n  }, []);\n\n  useEffect(() => () => {\n    if (typeaheadRef.current.timer !== null) {\n      clearTimeout(typeaheadRef.current.timer);\n    }\n  }, []);\n\n  useEffect(() => {\n    const element = localRef.current;\n    if (!element || typeof element.showPopover !== \"function\") {\n      return;\n    }\n    const nativeOpen = element.matches(\":popover-open\");\n    if (sub.open && !nativeOpen) {\n      let timer: number | null = null;\n      const parentPopover = element.parentElement?.closest<HTMLElement>(\"[popover]\");\n      const show = () => {\n        if (sub.getOpen() && element.isConnected && !element.matches(\":popover-open\")) {\n          synchronizeNativeOpen(element, true);\n        }\n      };\n      const scheduleShow = () => {\n        timer = window.setTimeout(show, 0);\n      };\n      const handleParentToggle = (event: Event) => {\n        const toggle = event as Event & { newState?: string };\n        if (toggle.newState === \"open\") {\n          scheduleShow();\n        }\n      };\n\n      if (parentPopover && !parentPopover.matches(\":popover-open\")) {\n        parentPopover.addEventListener(\"toggle\", handleParentToggle);\n      } else {\n        scheduleShow();\n      }\n\n      return () => {\n        parentPopover?.removeEventListener(\"toggle\", handleParentToggle);\n        if (timer !== null) {\n          window.clearTimeout(timer);\n        }\n      };\n    }\n    if (!sub.open && nativeOpen) {\n      synchronizeNativeOpen(element, false);\n    }\n    if (!sub.open && !nativeOpen) {\n      scheduleUnmount(element);\n    }\n    return undefined;\n  }, [scheduleUnmount, sub, synchronizeNativeOpen]);\n\n  if (!shouldRender) {\n    return null;\n  }\n\n  const handleToggle = composeHeidiEventHandlers(\n    onToggle,\n    (event: SyntheticEvent<HTMLDivElement>) => {\n      if (event.target !== event.currentTarget) {\n        return;\n      }\n      const element = event.currentTarget;\n      const nativeToggle = event.nativeEvent as Event & { newState?: string };\n      const nativeOpen = nativeToggle.newState === \"open\";\n      const pendingTransitions = pendingNativeTransitionsRef.current;\n      const pendingIndex = pendingTransitions.findIndex(\n        (transition) => transition.open === nativeOpen\n      );\n      const internallySynchronized = pendingIndex >= 0;\n      if (internallySynchronized) {\n        pendingTransitions.splice(0, pendingIndex + 1);\n      }\n      if (!internallySynchronized && nativeOpen !== sub.getOpen()) {\n        sub.requestOpen(\n          nativeOpen,\n          nativeOpen ? \"trigger-press\" : \"outside-press\",\n          nativeToggle,\n          element,\n          false\n        );\n      }\n      if (nativeOpen !== sub.getOpen()) {\n        requestAnimationFrame(() => {\n          if (element.isConnected) {\n            synchronizeNativeOpen(element, sub.getOpen());\n          }\n        });\n      }\n      if (nativeOpen && sub.getOpen() && sub.focusOnOpen()) {\n        queueMicrotask(() => {\n          menuItemsIn(element, { includeDisabled: true })[0]?.focus({\n            preventScroll: true\n          });\n        });\n      }\n      if (!nativeOpen && !sub.getOpen()) {\n        sub.cancelPointerTimers();\n        scheduleUnmount(element);\n      }\n    }\n  );\n\n  const handleKeyDown = composeHeidiEventHandlers(\n    onKeyDown,\n    (event: KeyboardEvent<HTMLDivElement>) => {\n      const sourceMenu = (event.target as HTMLElement | null)?.closest('[role=\"menu\"]');\n      if (sourceMenu && sourceMenu !== event.currentTarget) {\n        return;\n      }\n      const closeKey =\n        getComputedStyle(event.currentTarget).direction === \"rtl\"\n          ? \"ArrowRight\"\n          : \"ArrowLeft\";\n      if (event.key === closeKey || event.key === \"Escape\") {\n        event.preventDefault();\n        event.stopPropagation();\n        sub.requestOpen(false, \"escape-key\", event.nativeEvent, event.currentTarget);\n        requestAnimationFrame(() => {\n          document.getElementById(sub.triggerId)?.focus({ preventScroll: true });\n        });\n        return;\n      }\n      if (event.key === \"Tab\") {\n        root.requestOpen(false, \"focus-out\", event.nativeEvent, event.currentTarget);\n        return;\n      }\n      if (\n        event.key === \"ArrowDown\" ||\n        event.key === \"ArrowUp\" ||\n        event.key === \"End\" ||\n        event.key === \"Home\"\n      ) {\n        event.preventDefault();\n        moveMenuFocus(event.currentTarget, event.key, root.loopFocus);\n        return;\n      }\n      if (\n        event.key.length === 1 &&\n        event.key !== \" \" &&\n        !event.altKey &&\n        !event.ctrlKey &&\n        !event.metaKey\n      ) {\n        runTypeahead(event.currentTarget, event.key, typeaheadRef.current);\n      }\n    }\n  );\n\n  return renderHeidiElement({\n    className: CONTEXT_MENU_CLASSES.subContent,\n    dataPart: \"context-menu-sub-content\",\n    element: \"div\",\n    props: {\n      ...nativeProps,\n      ...openStateAttributes(sub.open),\n      \"aria-labelledby\": sub.triggerId,\n      children,\n      \"data-state\": dataState(sub.open),\n      id: sub.contentId,\n      onKeyDown: handleKeyDown,\n      onPointerEnter: composeHeidiEventHandlers(onPointerEnter, sub.cancelPointerTimers),\n      onPointerLeave: composeHeidiEventHandlers(\n        onPointerLeave,\n        (event: PointerEvent<HTMLDivElement>) => {\n          sub.schedulePointerClose(event.nativeEvent, event.currentTarget);\n        }\n      ),\n      onToggle: handleToggle,\n      popover: \"auto\",\n      ref: mergeHeidiRefs(localRef, ref),\n      role: \"menu\",\n      tabIndex: -1\n    },\n    renderProps: { className, render, style },\n    state: { open: sub.open },\n    structuralStyle: {\n      \"--_hui-context-menu-align-offset\": `${alignOffset}px`,\n      \"--_hui-context-menu-side-offset\": `${Math.max(0, sideOffset)}px`,\n      positionAnchor: sub.anchorName\n    } as CSSProperties\n  });\n}\n\nexport type ContextMenuSeparatorState = Record<string, never>;\nexport type ContextMenuSeparatorProps = HeidiIntrinsicHostProps<\n  ContextMenuSeparatorState,\n  \"div\"\n> &\n  Omit<NativeHostProps<\"div\">, \"aria-orientation\" | \"role\"> & {\n    ref?: Ref<HTMLDivElement>;\n  };\n\nexport function ContextMenuSeparator({\n  className,\n  ref,\n  render,\n  style,\n  ...nativeProps\n}: ContextMenuSeparatorProps) {\n  return renderHeidiElement({\n    className: CONTEXT_MENU_CLASSES.separator,\n    dataPart: \"context-menu-separator\",\n    element: \"div\",\n    props: {\n      ...nativeProps,\n      \"aria-orientation\": \"horizontal\",\n      ref,\n      role: \"separator\"\n    },\n    renderProps: { className, render, style },\n    state: {}\n  });\n}\n\ntype ContextMenuGroupContextValue = {\n  labelId: string;\n  registerLabel: (present: boolean) => void;\n};\n\nconst ContextMenuGroupContext = createContext<ContextMenuGroupContextValue | null>(null);\n\nexport type ContextMenuGroupState = { labelled: boolean };\nexport type ContextMenuGroupProps = HeidiIntrinsicHostProps<\n  ContextMenuGroupState,\n  \"div\"\n> &\n  Omit<NativeHostProps<\"div\">, \"aria-labelledby\" | \"role\"> & {\n    children?: ReactNode;\n    ref?: Ref<HTMLDivElement>;\n  };\n\nexport function ContextMenuGroup({\n  children,\n  className,\n  ref,\n  render,\n  style,\n  ...nativeProps\n}: ContextMenuGroupProps) {\n  const labelId = `hui-context-menu-group-label-${toSafeId(useId())}`;\n  const [labelled, setLabelled] = useState(false);\n  const registerLabel = useCallback((present: boolean) => setLabelled(present), []);\n  const state = { labelled };\n  const value = useMemo(() => ({ labelId, registerLabel }), [labelId, registerLabel]);\n  return (\n    <ContextMenuGroupContext value={value}>\n      {renderHeidiElement({\n        className: CONTEXT_MENU_CLASSES.group,\n        dataPart: \"context-menu-group\",\n        element: \"div\",\n        props: {\n          ...nativeProps,\n          \"aria-labelledby\": labelled ? labelId : undefined,\n          children,\n          ref,\n          role: \"group\"\n        },\n        renderProps: { className, render, style },\n        state\n      })}\n    </ContextMenuGroupContext>\n  );\n}\n\nexport type ContextMenuGroupLabelState = Record<string, never>;\nexport type ContextMenuGroupLabelProps = HeidiIntrinsicHostProps<\n  ContextMenuGroupLabelState,\n  \"div\"\n> &\n  Omit<NativeHostProps<\"div\">, \"id\"> & {\n    children?: ReactNode;\n    ref?: Ref<HTMLDivElement>;\n  };\n\nexport function ContextMenuGroupLabel({\n  children,\n  className,\n  ref,\n  render,\n  style,\n  ...nativeProps\n}: ContextMenuGroupLabelProps) {\n  const group = useContext(ContextMenuGroupContext);\n  if (!group) {\n    throw new Error(\"ContextMenu.GroupLabel must be rendered inside ContextMenu.Group.\");\n  }\n  useHeidiLayoutEffect(() => {\n    group.registerLabel(true);\n    return () => group.registerLabel(false);\n  }, [group]);\n  return renderHeidiElement({\n    className: CONTEXT_MENU_CLASSES.groupLabel,\n    dataPart: \"context-menu-group-label\",\n    element: \"div\",\n    props: { ...nativeProps, children, id: group.labelId, ref },\n    renderProps: { className, render, style },\n    state: {}\n  });\n}\n\nexport const ContextMenu = {\n  Content: ContextMenuContent,\n  Group: ContextMenuGroup,\n  GroupLabel: ContextMenuGroupLabel,\n  Item: ContextMenuItem,\n  LinkItem: ContextMenuLinkItem,\n  Popup: ContextMenuPopup,\n  Root: ContextMenuRoot,\n  Separator: ContextMenuSeparator,\n  Sub: ContextMenuSub,\n  SubContent: ContextMenuSubContent,\n  SubTrigger: ContextMenuSubTrigger,\n  Trigger: ContextMenuTrigger\n} as const;\n",
      "path": "packages/heidi-ui/src/context-menu/context-menu.tsx",
      "target": "components/ui/heidi/context-menu/context-menu.tsx",
      "type": "registry:ui"
    },
    {
      "content": "/*\n * heidi-ui context-menu — STRUCTURAL CSS only. No --hui-*.\n * Physical pointer coordinates feed a zero-size fixed CSS anchor; logical\n * position-area keeps popup placement writing-mode aware after that point.\n */\n\n@layer heidi-ui-base {\n  .hui-context-menu-content,\n  .hui-context-menu-sub-content {\n    /* ponytail: sideOffset defaults to 0 here, not 4 like every other anchored\n       popup. The root menu anchors to a zero-size point at the pointer, where\n       a default gap would only push the menu off the spot the user aimed at;\n       the submenu then matches it so one primitive has one default. Rejected:\n       a 4px default for cross-primitive symmetry — that is symmetry of the\n       API's shape, which this DOES have, bought by shifting every existing\n       consumer and by opening a 4px pointer gap between a context submenu and\n       its parent that the hover-intent corridor would have to re-cross. */\n    --_hui-context-menu-align-offset: 0px;\n    --_hui-context-menu-side-offset: 0px;\n\n    box-sizing: border-box;\n    flex-direction: column;\n    inset: auto;\n    /* main-axis gap from the pointer anchor; sideOffset writes this var */\n    margin: var(--_hui-context-menu-side-offset);\n    max-block-size: calc(100dvb - 1rem);\n    max-inline-size: calc(100dvi - 1rem);\n    min-inline-size: 0;\n    overflow: auto;\n    overscroll-behavior: contain;\n    position: fixed;\n    scrollbar-gutter: stable;\n    transition-behavior: allow-discrete;\n    transition-property: display, overlay;\n  }\n\n  .hui-context-menu-content {\n    position-area: block-end span-inline-end;\n    position-try-fallbacks: flip-block, flip-inline, flip-block flip-inline;\n  }\n\n  .hui-context-menu-sub-content {\n    position-area: inline-end span-block-end;\n    position-try-fallbacks: flip-inline, flip-block, flip-inline flip-block;\n  }\n\n  /* Cross-axis nudge (alignOffset) runs along each popup's perpendicular axis:\n     the root menu drops on the block axis, so it nudges inline; a submenu\n     opens on the inline axis, so it nudges block. `translate` rather than\n     `transform` keeps this independent of the theme's scale animation. */\n  .hui-context-menu-content {\n    translate: var(--_hui-context-menu-align-offset) 0;\n  }\n\n  .hui-context-menu-sub-content {\n    translate: 0 var(--_hui-context-menu-align-offset);\n  }\n\n  .hui-context-menu-content:not(:popover-open),\n  .hui-context-menu-sub-content:not(:popover-open) {\n    display: none !important;\n  }\n\n  .hui-context-menu-content:popover-open,\n  .hui-context-menu-sub-content:popover-open {\n    display: flex;\n  }\n\n  .hui-context-menu-content[data-state=\"closed\"],\n  .hui-context-menu-sub-content[data-state=\"closed\"] {\n    pointer-events: none;\n  }\n\n  .hui-context-menu-content[data-state=\"open\"],\n  .hui-context-menu-sub-content[data-state=\"open\"] {\n    pointer-events: auto;\n  }\n\n  .hui-context-menu-group {\n    display: flex;\n    flex-direction: column;\n    min-inline-size: 0;\n  }\n\n  .hui-context-menu-item,\n  .hui-context-menu-link-item,\n  .hui-context-menu-sub-trigger {\n    box-sizing: border-box;\n    inline-size: 100%;\n    max-inline-size: 100%;\n    min-inline-size: 0;\n    overflow-wrap: anywhere;\n    white-space: normal;\n  }\n\n  .hui-context-menu-link-item {\n    display: block;\n  }\n\n  .hui-context-menu-separator {\n    block-size: 0;\n    inline-size: auto;\n    min-block-size: 0;\n  }\n}\n",
      "path": "packages/heidi-ui/src/context-menu/context-menu.base.css",
      "target": "components/ui/heidi/context-menu/context-menu.base.css",
      "type": "registry:ui"
    },
    {
      "content": "/* heidi-ui context-menu — VISUAL theme (opt-in). Consumes --hui-* only. */\n\n@layer heidi-ui {\n  .hui-context-menu-trigger {\n    background: var(--hui-color-bg-elevated);\n    border: var(--hui-border-width) var(--hui-border-style) var(--hui-color-border-default);\n    border-radius: var(--hui-radius-md);\n    color: var(--hui-color-fg-default);\n    cursor: context-menu;\n    font: inherit;\n    max-inline-size: 100%;\n    min-block-size: 6rem;\n    min-inline-size: 0;\n    outline-offset: var(--hui-focus-ring-offset);\n    overflow-wrap: anywhere;\n    padding: var(--hui-space-4);\n  }\n\n  .hui-context-menu-trigger:focus-visible {\n    outline: var(--hui-focus-ring-width) solid var(--hui-color-focus-ring);\n  }\n\n  .hui-context-menu-trigger[data-disabled=\"true\"] {\n    cursor: default;\n    opacity: 0.6;\n  }\n\n  .hui-context-menu-content,\n  .hui-context-menu-sub-content {\n    background: var(--hui-color-bg-raised);\n    border: 0;\n    border-radius: var(--hui-radius-2xl);\n    box-shadow: var(--hui-shadow-surface-lg);\n    color: var(--hui-color-fg-default);\n    gap: var(--hui-space-0-5);\n    min-inline-size: min(12rem, calc(100dvi - 1rem));\n    opacity: 0;\n    padding: var(--hui-space-1-5);\n    transform: scale(0.97);\n    transform-origin: top left;\n    transition-duration: var(--hui-duration-fast);\n    transition-property: display, opacity, overlay, transform;\n    transition-timing-function: var(--hui-ease-out);\n  }\n\n  .hui-context-menu-content:popover-open,\n  .hui-context-menu-sub-content:popover-open {\n    opacity: 1;\n    transform: none;\n  }\n\n  @starting-style {\n    .hui-context-menu-content:popover-open,\n    .hui-context-menu-sub-content:popover-open {\n      opacity: 0;\n      transform: scale(0.97);\n    }\n  }\n\n  .hui-context-menu-group {\n    gap: var(--hui-space-0-5);\n  }\n\n  .hui-context-menu-group-label {\n    color: var(--hui-color-fg-muted);\n    font: inherit;\n    font-weight: var(--hui-font-weight-medium);\n    overflow-wrap: anywhere;\n    padding: var(--hui-space-1) var(--hui-space-2);\n  }\n\n  .hui-context-menu-item,\n  .hui-context-menu-link-item,\n  .hui-context-menu-sub-trigger {\n    background: transparent;\n    border: none;\n    border-radius: var(--hui-radius-full);\n    color: var(--hui-color-fg-default);\n    cursor: pointer;\n    font: inherit;\n    padding: var(--hui-space-1-5) var(--hui-space-2);\n    text-align: start;\n    text-decoration: none;\n  }\n\n  .hui-context-menu-sub-trigger {\n    display: flex;\n    gap: var(--hui-space-2);\n    justify-content: space-between;\n  }\n\n  .hui-context-menu-item[data-highlighted=\"true\"],\n  .hui-context-menu-link-item[data-highlighted=\"true\"],\n  .hui-context-menu-sub-trigger[data-highlighted=\"true\"] {\n    background: var(--hui-color-interactive-ghost-bg-hover);\n    outline: none;\n  }\n\n  .hui-context-menu-item:focus-visible,\n  .hui-context-menu-link-item:focus-visible,\n  .hui-context-menu-sub-trigger:focus-visible {\n    outline: var(--hui-focus-ring-width) solid var(--hui-color-focus-ring);\n    outline-offset: calc(-1 * var(--hui-focus-ring-offset));\n  }\n\n  .hui-context-menu-item[data-disabled=\"true\"],\n  .hui-context-menu-link-item[data-disabled=\"true\"],\n  .hui-context-menu-sub-trigger[data-disabled=\"true\"] {\n    cursor: default;\n    opacity: 0.5;\n  }\n\n  .hui-context-menu-item[data-disabled=\"true\"][data-highlighted=\"true\"],\n  .hui-context-menu-link-item[data-disabled=\"true\"][data-highlighted=\"true\"],\n  .hui-context-menu-sub-trigger[data-disabled=\"true\"][data-highlighted=\"true\"] {\n    background: transparent;\n  }\n\n  .hui-context-menu-separator {\n    border: 0;\n    border-block-start: var(--hui-border-width) var(--hui-border-style)\n      var(--hui-color-border-default);\n    margin: var(--hui-space-1) var(--hui-space-2);\n  }\n\n  @media (prefers-reduced-motion: reduce) {\n    .hui-context-menu-content,\n    .hui-context-menu-sub-content {\n      transform: none;\n      transition-duration: 0s;\n    }\n\n    .hui-context-menu-content:popover-open,\n    .hui-context-menu-sub-content:popover-open {\n      opacity: 1;\n      transform: none;\n    }\n  }\n\n  @media (forced-colors: active) {\n    .hui-context-menu-content,\n    .hui-context-menu-sub-content {\n      border: var(--hui-border-width) solid CanvasText;\n    }\n\n    .hui-context-menu-trigger {\n      border-color: CanvasText;\n    }\n\n    .hui-context-menu-item:focus-visible,\n    .hui-context-menu-link-item:focus-visible,\n    .hui-context-menu-sub-trigger:focus-visible,\n    .hui-context-menu-trigger:focus-visible {\n      outline-color: Highlight;\n    }\n\n    .hui-context-menu-item[data-disabled=\"true\"],\n    .hui-context-menu-link-item[data-disabled=\"true\"],\n    .hui-context-menu-sub-trigger[data-disabled=\"true\"] {\n      color: GrayText;\n      opacity: 1;\n    }\n  }\n}\n",
      "path": "packages/heidi-ui/src/context-menu/context-menu.theme.css",
      "target": "components/ui/heidi/context-menu/context-menu.theme.css",
      "type": "registry:ui"
    },
    {
      "content": "/*\n * heidi-ui context-menu — aggregator (base + Heidi adapter + theme).\n */\n\n@import \"./context-menu.base.css\";\n@import \"../../../../styles/heidi-ui-adapter.css\";\n@import \"./context-menu.theme.css\";\n",
      "path": "packages/heidi-ui/src/context-menu/context-menu.css",
      "target": "components/ui/heidi/context-menu/context-menu.css",
      "type": "registry:ui"
    },
    {
      "content": "/** GENERATED from packages/heidi-ui/src/context-menu/context-menu.base.css + packages/heidi-ui/src/context-menu/context-menu.theme.css — do not edit by hand.\n * Rebuild: `bun run hui:build`. Freshness: `bun run contracts:check`. */\nexport const CONTEXT_MENU_CLASSES = {\n  content: \"hui-context-menu-content\",\n  group: \"hui-context-menu-group\",\n  groupLabel: \"hui-context-menu-group-label\",\n  item: \"hui-context-menu-item\",\n  linkItem: \"hui-context-menu-link-item\",\n  separator: \"hui-context-menu-separator\",\n  subContent: \"hui-context-menu-sub-content\",\n  subTrigger: \"hui-context-menu-sub-trigger\",\n  trigger: \"hui-context-menu-trigger\",\n} as const;\n",
      "path": "packages/heidi-ui/src/context-menu/context-menu.classes.generated.ts",
      "target": "components/ui/heidi/context-menu/context-menu.classes.generated.ts",
      "type": "registry:ui"
    },
    {
      "content": "/** GENERATED from context-menu.anatomy.json + context-menu.base.css + context-menu.theme.css — do not edit by hand.\n * Rebuild: `bun run hui:build`. Freshness: `bun run contracts:check`. */\n\nexport type ContextMenuDisabled = \"true\";\nexport type ContextMenuHighlighted = \"true\";\nexport type ContextMenuState = \"closed\" | \"open\";\n\nexport const CONTEXT_MENU_ANATOMY = {\n  \"component\": \"context-menu\",\n  \"description\": \"Base-UI-shaped APG context menu on the native Popover API. A semantics-neutral context target opens a lazy role=menu at physical pointer coordinates, keyboard center, or a 500ms long press. Controlled state is authoritative and cancellable; native light dismiss is reconciled without duplicate callbacks. Disabled items remain focusable, typeahead and direction-aware submenus are supported, and popup geometry is viewport-contained.\",\n  \"schemaVersion\": 2,\n  \"parts\": {\n    \"content\": {\n      \"aria\": {\n        \"owns\": [\n          \"aria-label\"\n        ],\n        \"role\": \"menu\"\n      },\n      \"class\": \"hui-context-menu-content\",\n      \"css\": {\n        \"structural\": [\n          \"box-sizing\",\n          \"display\",\n          \"flex-direction\",\n          \"inset\",\n          \"margin\",\n          \"max-block-size\",\n          \"max-inline-size\",\n          \"min-inline-size\",\n          \"overflow\",\n          \"overscroll-behavior\",\n          \"position\",\n          \"position-area\",\n          \"position-try-fallbacks\",\n          \"scrollbar-gutter\",\n          \"transition-behavior\",\n          \"transition-property\",\n          \"translate\"\n        ]\n      },\n      \"dataPart\": \"context-menu-content\",\n      \"description\": \"Lazy native popover with role=menu, a stable accessible name, APG focus movement, typeahead, exit retention, and a physical virtual anchor.\",\n      \"element\": \"div\",\n      \"nativeProps\": true,\n      \"props\": [\n        \"alignOffset\",\n        \"className\",\n        \"keepMounted\",\n        \"label\",\n        \"ref\",\n        \"render\",\n        \"sideOffset\",\n        \"style\"\n      ],\n      \"pseudoStates\": [\n        \":popover-open\"\n      ],\n      \"states\": {\n        \"data-state\": [\n          \"closed\",\n          \"open\"\n        ]\n      }\n    },\n    \"group\": {\n      \"aria\": {\n        \"owns\": [\n          \"aria-labelledby\"\n        ],\n        \"role\": \"group\"\n      },\n      \"class\": \"hui-context-menu-group\",\n      \"css\": {\n        \"structural\": [\n          \"display\",\n          \"flex-direction\",\n          \"min-inline-size\"\n        ]\n      },\n      \"dataPart\": \"context-menu-group\",\n      \"description\": \"Semantic menu item group which automatically references its mounted GroupLabel.\",\n      \"element\": \"div\",\n      \"nativeProps\": true,\n      \"props\": [\n        \"className\",\n        \"ref\",\n        \"render\",\n        \"style\"\n      ],\n      \"pseudoStates\": [],\n      \"states\": {}\n    },\n    \"group-label\": {\n      \"aria\": {\n        \"owns\": [],\n        \"role\": null\n      },\n      \"class\": \"hui-context-menu-group-label\",\n      \"css\": {\n        \"structural\": []\n      },\n      \"dataPart\": \"context-menu-group-label\",\n      \"description\": \"Visible label registered with the nearest Group without dangling aria-labelledby references.\",\n      \"element\": \"div\",\n      \"nativeProps\": true,\n      \"props\": [\n        \"className\",\n        \"ref\",\n        \"render\",\n        \"style\"\n      ],\n      \"pseudoStates\": [],\n      \"states\": {}\n    },\n    \"item\": {\n      \"aria\": {\n        \"owns\": [\n          \"aria-disabled\"\n        ],\n        \"role\": \"menuitem\"\n      },\n      \"class\": \"hui-context-menu-item\",\n      \"css\": {\n        \"structural\": [\n          \"box-sizing\",\n          \"inline-size\",\n          \"max-inline-size\",\n          \"min-inline-size\",\n          \"overflow-wrap\",\n          \"white-space\"\n        ]\n      },\n      \"dataPart\": \"context-menu-item\",\n      \"description\": \"Polymorphic role=menuitem with non-native keyboard activation. Disabled items remain focusable but inert; selection closes unless closeOnClick is false.\",\n      \"element\": \"div\",\n      \"nativeProps\": true,\n      \"props\": [\n        \"className\",\n        \"closeOnClick\",\n        \"disabled\",\n        \"label\",\n        \"nativeButton\",\n        \"onSelect\",\n        \"ref\",\n        \"render\",\n        \"style\"\n      ],\n      \"pseudoStates\": [\n        \":focus-visible\"\n      ],\n      \"states\": {\n        \"data-disabled\": [\n          \"true\"\n        ],\n        \"data-highlighted\": [\n          \"true\"\n        ]\n      }\n    },\n    \"link-item\": {\n      \"aria\": {\n        \"owns\": [\n          \"aria-disabled\"\n        ],\n        \"role\": \"menuitem\"\n      },\n      \"class\": \"hui-context-menu-link-item\",\n      \"css\": {\n        \"structural\": [\n          \"box-sizing\",\n          \"display\",\n          \"inline-size\",\n          \"max-inline-size\",\n          \"min-inline-size\",\n          \"overflow-wrap\",\n          \"white-space\"\n        ]\n      },\n      \"dataPart\": \"context-menu-link-item\",\n      \"description\": \"Semantic anchor menu item with APG Space activation; navigation stays open by default and disabled links are inert but focusable.\",\n      \"element\": \"a\",\n      \"nativeProps\": true,\n      \"props\": [\n        \"className\",\n        \"closeOnClick\",\n        \"disabled\",\n        \"label\",\n        \"onSelect\",\n        \"ref\",\n        \"render\",\n        \"style\"\n      ],\n      \"pseudoStates\": [\n        \":focus-visible\"\n      ],\n      \"states\": {\n        \"data-disabled\": [\n          \"true\"\n        ],\n        \"data-highlighted\": [\n          \"true\"\n        ]\n      }\n    },\n    \"separator\": {\n      \"aria\": {\n        \"owns\": [\n          \"aria-orientation\"\n        ],\n        \"role\": \"separator\"\n      },\n      \"class\": \"hui-context-menu-separator\",\n      \"css\": {\n        \"structural\": [\n          \"block-size\",\n          \"inline-size\",\n          \"min-block-size\"\n        ]\n      },\n      \"dataPart\": \"context-menu-separator\",\n      \"description\": \"Horizontal semantic separator excluded from the menu focus ring.\",\n      \"element\": \"div\",\n      \"nativeProps\": true,\n      \"props\": [\n        \"className\",\n        \"ref\",\n        \"render\",\n        \"style\"\n      ],\n      \"pseudoStates\": [],\n      \"states\": {}\n    },\n    \"sub-content\": {\n      \"aria\": {\n        \"owns\": [\n          \"aria-labelledby\"\n        ],\n        \"role\": \"menu\"\n      },\n      \"class\": \"hui-context-menu-sub-content\",\n      \"css\": {\n        \"structural\": [\n          \"box-sizing\",\n          \"display\",\n          \"flex-direction\",\n          \"inset\",\n          \"margin\",\n          \"max-block-size\",\n          \"max-inline-size\",\n          \"min-inline-size\",\n          \"overflow\",\n          \"overscroll-behavior\",\n          \"position\",\n          \"position-area\",\n          \"position-try-fallbacks\",\n          \"scrollbar-gutter\",\n          \"transition-behavior\",\n          \"transition-property\",\n          \"translate\"\n        ]\n      },\n      \"dataPart\": \"context-menu-sub-content\",\n      \"description\": \"Lazy nested menu anchored to SubTrigger with controlled-state reconciliation, direction-aware close keys, typeahead, and pointer bridge delays.\",\n      \"element\": \"div\",\n      \"nativeProps\": true,\n      \"props\": [\n        \"alignOffset\",\n        \"className\",\n        \"keepMounted\",\n        \"ref\",\n        \"render\",\n        \"sideOffset\",\n        \"style\"\n      ],\n      \"pseudoStates\": [\n        \":popover-open\"\n      ],\n      \"states\": {\n        \"data-state\": [\n          \"closed\",\n          \"open\"\n        ]\n      }\n    },\n    \"sub-trigger\": {\n      \"aria\": {\n        \"owns\": [\n          \"aria-controls\",\n          \"aria-disabled\",\n          \"aria-expanded\",\n          \"aria-haspopup\"\n        ],\n        \"role\": \"menuitem\"\n      },\n      \"class\": \"hui-context-menu-sub-trigger\",\n      \"css\": {\n        \"structural\": [\n          \"box-sizing\",\n          \"inline-size\",\n          \"max-inline-size\",\n          \"min-inline-size\",\n          \"overflow-wrap\",\n          \"white-space\"\n        ]\n      },\n      \"dataPart\": \"context-menu-sub-trigger\",\n      \"description\": \"Focusable submenu item with authoritative controlled state, delayed pointer intent, and direction-aware open-key handling.\",\n      \"element\": \"div\",\n      \"nativeProps\": true,\n      \"props\": [\n        \"className\",\n        \"disabled\",\n        \"ref\",\n        \"render\",\n        \"style\"\n      ],\n      \"pseudoStates\": [\n        \":focus-visible\"\n      ],\n      \"states\": {\n        \"data-disabled\": [\n          \"true\"\n        ],\n        \"data-highlighted\": [\n          \"true\"\n        ]\n      }\n    },\n    \"trigger\": {\n      \"aria\": {\n        \"owns\": [],\n        \"role\": null\n      },\n      \"class\": \"hui-context-menu-trigger\",\n      \"css\": {\n        \"structural\": []\n      },\n      \"dataPart\": \"context-menu-trigger\",\n      \"description\": \"Semantics-neutral context target. Right click, ContextMenu/Shift+F10 on a consumer-focusable host, or a stationary 500ms touch opens at the physical invocation point.\",\n      \"element\": \"div\",\n      \"nativeProps\": true,\n      \"props\": [\n        \"className\",\n        \"longPressDelay\",\n        \"ref\",\n        \"render\",\n        \"style\"\n      ],\n      \"pseudoStates\": [\n        \":focus-visible\"\n      ],\n      \"states\": {\n        \"data-disabled\": [\n          \"true\"\n        ]\n      }\n    }\n  },\n  \"rootProps\": [\n    \"defaultOpen\",\n    \"disabled\",\n    \"highlightItemOnHover\",\n    \"loopFocus\",\n    \"onOpenChange\",\n    \"onOpenChangeComplete\",\n    \"open\",\n    \"submenuCloseDelay\",\n    \"submenuOpenDelay\"\n  ],\n  \"tokens\": [\n    \"--hui-border-style\",\n    \"--hui-border-width\",\n    \"--hui-color-bg-elevated\",\n    \"--hui-color-bg-raised\",\n    \"--hui-color-border-default\",\n    \"--hui-color-fg-default\",\n    \"--hui-color-fg-muted\",\n    \"--hui-color-focus-ring\",\n    \"--hui-color-interactive-ghost-bg-hover\",\n    \"--hui-duration-fast\",\n    \"--hui-ease-out\",\n    \"--hui-focus-ring-offset\",\n    \"--hui-focus-ring-width\",\n    \"--hui-font-weight-medium\",\n    \"--hui-radius-2xl\",\n    \"--hui-radius-full\",\n    \"--hui-radius-md\",\n    \"--hui-shadow-surface-lg\",\n    \"--hui-space-0-5\",\n    \"--hui-space-1\",\n    \"--hui-space-1-5\",\n    \"--hui-space-2\",\n    \"--hui-space-4\"\n  ]\n} as const;\n",
      "path": "packages/heidi-ui/src/context-menu/context-menu.anatomy.generated.ts",
      "target": "components/ui/heidi/context-menu/context-menu.anatomy.generated.ts",
      "type": "registry:ui"
    },
    {
      "content": "{\n  \"component\": \"context-menu\",\n  \"description\": \"Base-UI-shaped APG context menu on the native Popover API. A semantics-neutral context target opens a lazy role=menu at physical pointer coordinates, keyboard center, or a 500ms long press. Controlled state is authoritative and cancellable; native light dismiss is reconciled without duplicate callbacks. Disabled items remain focusable, typeahead and direction-aware submenus are supported, and popup geometry is viewport-contained.\",\n  \"schemaVersion\": 2,\n  \"parts\": {\n    \"content\": {\n      \"aria\": {\n        \"owns\": [\n          \"aria-label\"\n        ],\n        \"role\": \"menu\"\n      },\n      \"class\": \"hui-context-menu-content\",\n      \"css\": {\n        \"structural\": [\n          \"box-sizing\",\n          \"display\",\n          \"flex-direction\",\n          \"inset\",\n          \"margin\",\n          \"max-block-size\",\n          \"max-inline-size\",\n          \"min-inline-size\",\n          \"overflow\",\n          \"overscroll-behavior\",\n          \"position\",\n          \"position-area\",\n          \"position-try-fallbacks\",\n          \"scrollbar-gutter\",\n          \"transition-behavior\",\n          \"transition-property\",\n          \"translate\"\n        ]\n      },\n      \"dataPart\": \"context-menu-content\",\n      \"description\": \"Lazy native popover with role=menu, a stable accessible name, APG focus movement, typeahead, exit retention, and a physical virtual anchor.\",\n      \"element\": \"div\",\n      \"nativeProps\": true,\n      \"props\": [\n        \"alignOffset\",\n        \"className\",\n        \"keepMounted\",\n        \"label\",\n        \"ref\",\n        \"render\",\n        \"sideOffset\",\n        \"style\"\n      ],\n      \"pseudoStates\": [\n        \":popover-open\"\n      ],\n      \"states\": {\n        \"data-state\": [\n          \"closed\",\n          \"open\"\n        ]\n      }\n    },\n    \"group\": {\n      \"aria\": {\n        \"owns\": [\n          \"aria-labelledby\"\n        ],\n        \"role\": \"group\"\n      },\n      \"class\": \"hui-context-menu-group\",\n      \"css\": {\n        \"structural\": [\n          \"display\",\n          \"flex-direction\",\n          \"min-inline-size\"\n        ]\n      },\n      \"dataPart\": \"context-menu-group\",\n      \"description\": \"Semantic menu item group which automatically references its mounted GroupLabel.\",\n      \"element\": \"div\",\n      \"nativeProps\": true,\n      \"props\": [\n        \"className\",\n        \"ref\",\n        \"render\",\n        \"style\"\n      ],\n      \"pseudoStates\": [],\n      \"states\": {}\n    },\n    \"group-label\": {\n      \"aria\": {\n        \"owns\": [],\n        \"role\": null\n      },\n      \"class\": \"hui-context-menu-group-label\",\n      \"css\": {\n        \"structural\": []\n      },\n      \"dataPart\": \"context-menu-group-label\",\n      \"description\": \"Visible label registered with the nearest Group without dangling aria-labelledby references.\",\n      \"element\": \"div\",\n      \"nativeProps\": true,\n      \"props\": [\n        \"className\",\n        \"ref\",\n        \"render\",\n        \"style\"\n      ],\n      \"pseudoStates\": [],\n      \"states\": {}\n    },\n    \"item\": {\n      \"aria\": {\n        \"owns\": [\n          \"aria-disabled\"\n        ],\n        \"role\": \"menuitem\"\n      },\n      \"class\": \"hui-context-menu-item\",\n      \"css\": {\n        \"structural\": [\n          \"box-sizing\",\n          \"inline-size\",\n          \"max-inline-size\",\n          \"min-inline-size\",\n          \"overflow-wrap\",\n          \"white-space\"\n        ]\n      },\n      \"dataPart\": \"context-menu-item\",\n      \"description\": \"Polymorphic role=menuitem with non-native keyboard activation. Disabled items remain focusable but inert; selection closes unless closeOnClick is false.\",\n      \"element\": \"div\",\n      \"nativeProps\": true,\n      \"props\": [\n        \"className\",\n        \"closeOnClick\",\n        \"disabled\",\n        \"label\",\n        \"nativeButton\",\n        \"onSelect\",\n        \"ref\",\n        \"render\",\n        \"style\"\n      ],\n      \"pseudoStates\": [\n        \":focus-visible\"\n      ],\n      \"states\": {\n        \"data-disabled\": [\n          \"true\"\n        ],\n        \"data-highlighted\": [\n          \"true\"\n        ]\n      }\n    },\n    \"link-item\": {\n      \"aria\": {\n        \"owns\": [\n          \"aria-disabled\"\n        ],\n        \"role\": \"menuitem\"\n      },\n      \"class\": \"hui-context-menu-link-item\",\n      \"css\": {\n        \"structural\": [\n          \"box-sizing\",\n          \"display\",\n          \"inline-size\",\n          \"max-inline-size\",\n          \"min-inline-size\",\n          \"overflow-wrap\",\n          \"white-space\"\n        ]\n      },\n      \"dataPart\": \"context-menu-link-item\",\n      \"description\": \"Semantic anchor menu item with APG Space activation; navigation stays open by default and disabled links are inert but focusable.\",\n      \"element\": \"a\",\n      \"nativeProps\": true,\n      \"props\": [\n        \"className\",\n        \"closeOnClick\",\n        \"disabled\",\n        \"label\",\n        \"onSelect\",\n        \"ref\",\n        \"render\",\n        \"style\"\n      ],\n      \"pseudoStates\": [\n        \":focus-visible\"\n      ],\n      \"states\": {\n        \"data-disabled\": [\n          \"true\"\n        ],\n        \"data-highlighted\": [\n          \"true\"\n        ]\n      }\n    },\n    \"separator\": {\n      \"aria\": {\n        \"owns\": [\n          \"aria-orientation\"\n        ],\n        \"role\": \"separator\"\n      },\n      \"class\": \"hui-context-menu-separator\",\n      \"css\": {\n        \"structural\": [\n          \"block-size\",\n          \"inline-size\",\n          \"min-block-size\"\n        ]\n      },\n      \"dataPart\": \"context-menu-separator\",\n      \"description\": \"Horizontal semantic separator excluded from the menu focus ring.\",\n      \"element\": \"div\",\n      \"nativeProps\": true,\n      \"props\": [\n        \"className\",\n        \"ref\",\n        \"render\",\n        \"style\"\n      ],\n      \"pseudoStates\": [],\n      \"states\": {}\n    },\n    \"sub-content\": {\n      \"aria\": {\n        \"owns\": [\n          \"aria-labelledby\"\n        ],\n        \"role\": \"menu\"\n      },\n      \"class\": \"hui-context-menu-sub-content\",\n      \"css\": {\n        \"structural\": [\n          \"box-sizing\",\n          \"display\",\n          \"flex-direction\",\n          \"inset\",\n          \"margin\",\n          \"max-block-size\",\n          \"max-inline-size\",\n          \"min-inline-size\",\n          \"overflow\",\n          \"overscroll-behavior\",\n          \"position\",\n          \"position-area\",\n          \"position-try-fallbacks\",\n          \"scrollbar-gutter\",\n          \"transition-behavior\",\n          \"transition-property\",\n          \"translate\"\n        ]\n      },\n      \"dataPart\": \"context-menu-sub-content\",\n      \"description\": \"Lazy nested menu anchored to SubTrigger with controlled-state reconciliation, direction-aware close keys, typeahead, and pointer bridge delays.\",\n      \"element\": \"div\",\n      \"nativeProps\": true,\n      \"props\": [\n        \"alignOffset\",\n        \"className\",\n        \"keepMounted\",\n        \"ref\",\n        \"render\",\n        \"sideOffset\",\n        \"style\"\n      ],\n      \"pseudoStates\": [\n        \":popover-open\"\n      ],\n      \"states\": {\n        \"data-state\": [\n          \"closed\",\n          \"open\"\n        ]\n      }\n    },\n    \"sub-trigger\": {\n      \"aria\": {\n        \"owns\": [\n          \"aria-controls\",\n          \"aria-disabled\",\n          \"aria-expanded\",\n          \"aria-haspopup\"\n        ],\n        \"role\": \"menuitem\"\n      },\n      \"class\": \"hui-context-menu-sub-trigger\",\n      \"css\": {\n        \"structural\": [\n          \"box-sizing\",\n          \"inline-size\",\n          \"max-inline-size\",\n          \"min-inline-size\",\n          \"overflow-wrap\",\n          \"white-space\"\n        ]\n      },\n      \"dataPart\": \"context-menu-sub-trigger\",\n      \"description\": \"Focusable submenu item with authoritative controlled state, delayed pointer intent, and direction-aware open-key handling.\",\n      \"element\": \"div\",\n      \"nativeProps\": true,\n      \"props\": [\n        \"className\",\n        \"disabled\",\n        \"ref\",\n        \"render\",\n        \"style\"\n      ],\n      \"pseudoStates\": [\n        \":focus-visible\"\n      ],\n      \"states\": {\n        \"data-disabled\": [\n          \"true\"\n        ],\n        \"data-highlighted\": [\n          \"true\"\n        ]\n      }\n    },\n    \"trigger\": {\n      \"aria\": {\n        \"owns\": [],\n        \"role\": null\n      },\n      \"class\": \"hui-context-menu-trigger\",\n      \"css\": {\n        \"structural\": []\n      },\n      \"dataPart\": \"context-menu-trigger\",\n      \"description\": \"Semantics-neutral context target. Right click, ContextMenu/Shift+F10 on a consumer-focusable host, or a stationary 500ms touch opens at the physical invocation point.\",\n      \"element\": \"div\",\n      \"nativeProps\": true,\n      \"props\": [\n        \"className\",\n        \"longPressDelay\",\n        \"ref\",\n        \"render\",\n        \"style\"\n      ],\n      \"pseudoStates\": [\n        \":focus-visible\"\n      ],\n      \"states\": {\n        \"data-disabled\": [\n          \"true\"\n        ]\n      }\n    }\n  },\n  \"rootProps\": [\n    \"defaultOpen\",\n    \"disabled\",\n    \"highlightItemOnHover\",\n    \"loopFocus\",\n    \"onOpenChange\",\n    \"onOpenChangeComplete\",\n    \"open\",\n    \"submenuCloseDelay\",\n    \"submenuOpenDelay\"\n  ]\n}\n",
      "path": "packages/heidi-ui/src/context-menu/context-menu.anatomy.json",
      "target": "components/ui/heidi/context-menu/context-menu.anatomy.json",
      "type": "registry:ui"
    },
    {
      "content": "/**\n * Cross-cutting disabled attrs for heidi-ui interactive hosts.\n *\n * House convention (Phase 23):\n * - Button widgets → native `disabled` + `data-disabled=\"true\"` (CSS + a11y).\n * - Non-button options (Select.Item) → `aria-disabled` + `data-disabled=\"true\"`\n *   (native disabled is invalid on role=option divs).\n * - Group roots may cascade `disabled` to children and mirror `data-disabled`.\n * - Keyboard nav / activation always skip disabled hosts.\n */\n\nexport type NativeDisabledAttrs = {\n  \"data-disabled\"?: true;\n  disabled?: true;\n};\n\nexport type AriaDisabledAttrs = {\n  \"aria-disabled\"?: true;\n  \"data-disabled\"?: true;\n};\n\nexport type DataDisabledAttrs = {\n  \"data-disabled\"?: true;\n};\n\n/** Native button/input disabled + styling hook. */\nexport function nativeDisabledAttrs(disabled: boolean | undefined): NativeDisabledAttrs {\n  if (!disabled) {\n    return {};\n  }\n  return {\n    \"data-disabled\": true,\n    disabled: true\n  };\n}\n\n/** ARIA-disabled for non-native hosts (e.g. role=option). */\nexport function ariaDisabledAttrs(disabled: boolean | undefined): AriaDisabledAttrs {\n  if (!disabled) {\n    return {};\n  }\n  return {\n    \"aria-disabled\": true,\n    \"data-disabled\": true\n  };\n}\n\n/** Styling hook only (group roots that cascade disabled). */\nexport function dataDisabledAttrs(disabled: boolean | undefined): DataDisabledAttrs {\n  if (!disabled) {\n    return {};\n  }\n  return {\n    \"data-disabled\": true\n  };\n}\n\nexport function isDisabledElement(el: Element | null | undefined): boolean {\n  if (!el || !(el instanceof HTMLElement)) {\n    return false;\n  }\n  if (el.matches(\":disabled\")) {\n    return true;\n  }\n  return el.getAttribute(\"aria-disabled\") === \"true\" || el.getAttribute(\"data-disabled\") === \"true\";\n}\n",
      "path": "packages/heidi-ui/src/_internal/disabled.ts",
      "target": "components/ui/heidi/_internal/disabled.ts",
      "type": "registry:ui"
    },
    {
      "content": "/**\n * Shared menu keyboard + pointer helpers — Menu + ContextMenu (+ nested SubContent).\n * Items are scoped to the nearest role=menu so nested submenu menuitems are\n * not part of the parent menu's ArrowUp/ArrowDown ring.\n */\n\nimport { isDisabledElement } from \"./disabled\";\n\nexport type MenuItemsOptions = {\n  /**\n   * APG menus keep disabled items in the roving-focus order. Pass false only\n   * for non-menu listbox-style consumers that intentionally omit them.\n   */\n  includeDisabled?: boolean;\n};\n\nexport function menuItemsIn(container: HTMLElement): HTMLElement[];\nexport function menuItemsIn(\n  container: HTMLElement,\n  options: MenuItemsOptions & { includeDisabled: true }\n): HTMLElement[];\nexport function menuItemsIn(\n  container: HTMLElement,\n  options: MenuItemsOptions\n): HTMLElement[];\nexport function menuItemsIn(\n  container: HTMLElement,\n  options: MenuItemsOptions = { includeDisabled: true }\n): HTMLElement[] {\n  return [\n    ...container.querySelectorAll<HTMLElement>(\n      '[role=\"menuitem\"], [role=\"menuitemcheckbox\"], [role=\"menuitemradio\"]'\n    )\n  ].filter((element) => {\n    // A genuinely disabled native control cannot receive focus. It must never\n    // enter a menu focus ring, even though aria-disabled menu items remain\n    // discoverable per APG and Base UI's focusable-when-disabled contract.\n    if (element.matches(\":disabled\")) {\n      return false;\n    }\n    if (options.includeDisabled === false && isDisabledElement(element)) {\n      return false;\n    }\n    if (\n      element.closest('[role=\"menu\"]') !== container ||\n      element.closest(\"[hidden], [inert], [aria-hidden='true']\") !== null ||\n      element.getClientRects().length === 0\n    ) {\n      return false;\n    }\n    const styles = element.ownerDocument.defaultView?.getComputedStyle(element);\n    return styles?.display !== \"none\" && styles?.visibility !== \"hidden\";\n  });\n}\n\nexport type OpenSubmenuOptions = {\n  /** When true (default), focus the first enabled item after open (keyboard). */\n  focus?: boolean;\n};\n\n/** Open a submenu from its trigger (menuitem + aria-haspopup=menu). */\nexport function openSubmenuFromTrigger(\n  trigger: HTMLElement,\n  options: OpenSubmenuOptions = {}\n): HTMLElement | null {\n  const focus = options.focus !== false;\n  const targetId = trigger.getAttribute(\"popovertarget\");\n  if (!targetId) {\n    return null;\n  }\n  const sub = trigger.ownerDocument.getElementById(targetId);\n  if (!sub || typeof sub.showPopover !== \"function\") {\n    return null;\n  }\n  closeSiblingSubmenus(sub);\n  try {\n    sub.showPopover();\n  } catch {\n    return null;\n  }\n  if (focus) {\n    menuItemsIn(sub, { includeDisabled: false })[0]?.focus();\n  }\n  return sub;\n}\n\n/** Hide every open nested role=menu under parentMenu (not parentMenu itself). */\nexport function closeDescendantSubmenus(parentMenu: HTMLElement): void {\n  for (const el of parentMenu.querySelectorAll<HTMLElement>('[role=\"menu\"]')) {\n    if (el === parentMenu) {\n      continue;\n    }\n    if (el.matches(\":popover-open\") && typeof el.hidePopover === \"function\") {\n      try {\n        el.hidePopover();\n      } catch {\n        // ignore\n      }\n    }\n  }\n}\n\nfunction closeSiblingSubmenus(except: HTMLElement): void {\n  const parentMenu = except.parentElement?.closest('[role=\"menu\"]');\n  if (!parentMenu) {\n    return;\n  }\n  for (const el of parentMenu.querySelectorAll<HTMLElement>('[role=\"menu\"]')) {\n    if (el === except || el === parentMenu) {\n      continue;\n    }\n    if (el.matches(\":popover-open\") && typeof el.hidePopover === \"function\") {\n      try {\n        el.hidePopover();\n      } catch {\n        // ignore\n      }\n    }\n  }\n}\n",
      "path": "packages/heidi-ui/src/_internal/menu-items.ts",
      "target": "components/ui/heidi/_internal/menu-items.ts",
      "type": "registry:ui"
    },
    {
      "content": "/**\n * Shared host rendering for heidi-ui parts — Base-UI-shaped composition\n * (docs/HEIDI-UI-HEADLESS.md § 4 / HEIDI-UI.md § 4.5):\n *   - className / style as value or (state) => value\n *   - render as ReactElement or (props, state) => ReactElement\n *   - ref forwarding\n *   - data-hui-part always set (stable unstyled hook)\n *\n * Structural inline styles (e.g. anchorName) are merged last so a consumer\n * style override cannot drop platform wiring.\n */\n\nimport {\n  cloneElement,\n  createElement,\n  isValidElement,\n  type CSSProperties,\n  type ComponentPropsWithRef,\n  type HTMLAttributes,\n  type ReactElement,\n  type ReactNode,\n  type Ref,\n  type RefCallback\n} from \"react\";\n\nexport type HeidiClassName<State> = string | ((state: State) => string | undefined) | undefined;\n\nexport type HeidiStyle<State> =\n  | CSSProperties\n  | ((state: State) => CSSProperties | undefined)\n  | undefined;\n\nexport type HeidiRenderFn<State, Props> = (\n  props: Props,\n  state: State\n) => ReactElement;\n\nexport type HeidiRender<State, Props> =\n  | ReactElement\n  | HeidiRenderFn<State, Props>\n  | undefined;\n\ntype DefaultRenderProps = HTMLAttributes<HTMLElement> & { ref?: Ref<HTMLElement> };\n\nexport type HeidiHostProps<\n  State,\n  RenderProps extends object = DefaultRenderProps\n> = {\n  className?: HeidiClassName<State>;\n  render?: HeidiRender<State, RenderProps>;\n  style?: HeidiStyle<State>;\n};\n\nexport type HeidiIntrinsicHostProps<\n  State,\n  Tag extends keyof HTMLElementTagNameMap\n> = HeidiHostProps<State, ComponentPropsWithRef<Tag>>;\n\nfunction resolveClassName<State>(\n  className: HeidiClassName<State>,\n  state: State\n): string | undefined {\n  return typeof className === \"function\" ? className(state) : className;\n}\n\nfunction resolveStyle<State>(style: HeidiStyle<State>, state: State): CSSProperties | undefined {\n  return typeof style === \"function\" ? style(state) : style;\n}\n\nfunction mergeClassNames(...parts: Array<string | undefined>): string | undefined {\n  const merged = parts.filter(Boolean).join(\" \");\n  return merged.length > 0 ? merged : undefined;\n}\n\n/**\n * Compose a consumer event with library behavior. Consumers run first and may\n * cancel the component behavior with `event.preventDefault()`. Heidi keeps\n * this native-event convention intentionally: it avoids a second branded event\n * API while still making async/controlled actions vetoable.\n */\nexport function composeHeidiEventHandlers<Event extends { defaultPrevented: boolean }>(\n  consumer: ((event: Event) => void) | undefined,\n  library: (event: Event) => void\n): (event: Event) => void {\n  return (event) => {\n    consumer?.(event);\n    if (!event.defaultPrevented) {\n      library(event);\n    }\n  };\n}\n\n/** React 19-safe ref fan-out, including callback-ref cleanup functions. */\nexport function mergeHeidiRefs<Element>(\n  ...inputRefs: Array<Ref<Element> | undefined>\n): Ref<Element> | undefined {\n  const refs = Array.from(\n    new Set(\n      inputRefs.filter(\n        (ref): ref is Exclude<Ref<Element>, null> => ref != null\n      )\n    )\n  );\n  if (refs.length === 0) {\n    return undefined;\n  }\n  if (refs.length === 1) {\n    return refs[0];\n  }\n\n  let cache = mergedRefCache;\n  for (const ref of refs) {\n    const key = ref as object;\n    let child = cache.children.get(key);\n    if (!child) {\n      child = { children: new WeakMap() };\n      cache.children.set(key, child);\n    }\n    cache = child;\n  }\n  if (cache.callback) {\n    return cache.callback as RefCallback<Element>;\n  }\n\n  const callback: RefCallback<Element> = (node) => {\n    const cleanups: Array<() => void> = [];\n    for (const ref of refs) {\n      if (typeof ref === \"function\") {\n        const cleanup = ref(node);\n        if (node !== null) {\n          cleanups.push(typeof cleanup === \"function\" ? cleanup : () => ref(null));\n        }\n      } else if (ref) {\n        ref.current = node;\n        if (node !== null) {\n          cleanups.push(() => {\n            ref.current = null;\n          });\n        }\n      }\n    }\n    return cleanups.length > 0\n      ? () => {\n          for (const cleanup of cleanups) {\n            cleanup();\n          }\n        }\n      : undefined;\n  };\n  cache.callback = callback as RefCallback<unknown>;\n  return callback;\n}\n\ntype MergedRefCache = {\n  callback?: RefCallback<unknown>;\n  children: WeakMap<object, MergedRefCache>;\n};\n\nconst mergedRefCache: MergedRefCache = { children: new WeakMap() };\n\ntype UnknownHandler = (...args: never[]) => unknown;\n\nfunction isEventHandler(key: string, value: unknown): value is UnknownHandler {\n  return /^on[A-Z]/.test(key) && typeof value === \"function\";\n}\n\nfunction defaultPrevented(args: unknown[]): boolean {\n  const event = args[0];\n  return (\n    typeof event === \"object\" &&\n    event !== null &&\n    \"defaultPrevented\" in event &&\n    event.defaultPrevented === true\n  );\n}\n\nfunction composeUnknownHandlers(\n  consumer: UnknownHandler,\n  library: UnknownHandler\n): UnknownHandler {\n  if (consumer === library) {\n    return library;\n  }\n  return ((...args: unknown[]) => {\n    (consumer as (...handlerArgs: unknown[]) => unknown)(...args);\n    if (!defaultPrevented(args)) {\n      (library as (...handlerArgs: unknown[]) => unknown)(...args);\n    }\n  }) as UnknownHandler;\n}\n\n/** Library-owned host props — permissive so button `type`, `data-*`, `popover`, etc. type-check. */\nexport type HeidiElementProps<Tag extends keyof HTMLElementTagNameMap> = Record<\n  string,\n  unknown\n> & {\n  children?: ReactNode;\n  className?: string;\n  ref?: Ref<HTMLElementTagNameMap[Tag]>;\n  style?: CSSProperties;\n};\n\ntype RenderElementParams<\n  State,\n  Tag extends keyof HTMLElementTagNameMap,\n  RenderProps extends object = DefaultRenderProps\n> = {\n  /** Default class from the generated hui-* map (theme/base target). */\n  className?: string;\n  /** Stable machine hook — always emitted. */\n  dataPart: string;\n  /** Intrinsic tag when `render` is omitted. */\n  element: Tag;\n  /** Props the library owns (aria, ids, handlers, popover, …). */\n  props: HeidiElementProps<Tag>;\n  /** Optional consumer composition props. */\n  renderProps?: HeidiHostProps<State, RenderProps>;\n  /** Typed state passed to functional className/style/render. */\n  state: State;\n  /** Structural inline styles that must survive consumer style merges. */\n  structuralStyle?: CSSProperties;\n  /**\n   * Render-element handlers to remove instead of composing. This is reserved\n   * for states such as a focusable disabled composite item where the public\n   * contract requires press handlers to be completely inert.\n   */\n  suppressRenderedHandlers?: readonly string[];\n};\n\n/**\n * Render a heidi-ui host element with optional Base-shaped composition.\n */\nexport function renderHeidiElement<\n  State,\n  Tag extends keyof HTMLElementTagNameMap,\n  RenderProps extends object = DefaultRenderProps\n>(\n  params: RenderElementParams<State, Tag, RenderProps>\n): ReactElement {\n  const {\n    className,\n    dataPart,\n    element,\n    props,\n    renderProps,\n    state,\n    structuralStyle,\n    suppressRenderedHandlers\n  } = params;\n  const consumerClass = resolveClassName(renderProps?.className, state);\n  const consumerStyle = resolveStyle(renderProps?.style, state);\n  const mergedStyle: CSSProperties | undefined =\n    props.style || consumerStyle || structuralStyle\n      ? { ...props.style, ...consumerStyle, ...structuralStyle }\n      : undefined;\n\n  const outProps: HTMLAttributes<HTMLElement> & {\n    \"data-hui-part\": string;\n    ref?: Ref<HTMLElement>;\n  } = {\n    ...(props as HTMLAttributes<HTMLElement> & { ref?: Ref<HTMLElement> }),\n    className: mergeClassNames(consumerClass, className),\n    \"data-hui-part\": dataPart,\n    style: mergedStyle\n  };\n\n  const render = renderProps?.render;\n  if (typeof render === \"function\") {\n    // Post-merge the returned element as well. This deliberately makes the\n    // stable data hook, owned semantics, refs, and internal handlers survive\n    // even when a render function forgets to spread one of the supplied props.\n    return mergeRenderedElement(\n      render(outProps as unknown as RenderProps, state),\n      outProps,\n      dataPart,\n      structuralStyle,\n      suppressRenderedHandlers\n    );\n  }\n  if (isValidElement(render)) {\n    return mergeRenderedElement(\n      render,\n      outProps,\n      dataPart,\n      structuralStyle,\n      suppressRenderedHandlers\n    );\n  }\n\n  return createElement(element, outProps as never, props.children);\n}\n\ntype RenderedProps = Record<string, unknown> & {\n  children?: ReactNode;\n  className?: string;\n  ref?: Ref<HTMLElement>;\n  style?: CSSProperties;\n};\n\nfunction mergeRenderedElement(\n  element: ReactElement,\n  libraryProps: HTMLAttributes<HTMLElement> & {\n    \"data-hui-part\": string;\n    ref?: Ref<HTMLElement>;\n  },\n  dataPart: string,\n  structuralStyle: CSSProperties | undefined,\n  suppressRenderedHandlers: readonly string[] | undefined\n): ReactElement {\n  const rendered = element as ReactElement<RenderedProps>;\n  const renderedProps = rendered.props;\n  const renderedClass =\n    renderedProps.className === libraryProps.className\n      ? undefined\n      : renderedProps.className;\n  const merged: RenderedProps = {\n    ...renderedProps,\n    ...libraryProps,\n    className: mergeClassNames(renderedClass, libraryProps.className),\n    \"data-hui-part\": dataPart,\n    ref: mergeHeidiRefs(libraryProps.ref, renderedProps.ref),\n    style: {\n      ...libraryProps.style,\n      ...renderedProps.style,\n      ...structuralStyle\n    }\n  };\n\n  if (Object.prototype.hasOwnProperty.call(renderedProps, \"children\")) {\n    merged.children = renderedProps.children;\n  }\n\n  const suppressedHandlers = suppressRenderedHandlers\n    ? new Set(suppressRenderedHandlers)\n    : null;\n  for (const key of new Set([...Object.keys(renderedProps), ...Object.keys(libraryProps)])) {\n    const consumer = renderedProps[key];\n    const library = (libraryProps as unknown as Record<string, unknown>)[key];\n    if (suppressedHandlers?.has(key)) {\n      merged[key] = library;\n      continue;\n    }\n    if (isEventHandler(key, consumer) && isEventHandler(key, library)) {\n      merged[key] = composeUnknownHandlers(consumer, library);\n    }\n  }\n\n  return cloneElement(rendered, merged as never);\n}\n",
      "path": "packages/heidi-ui/src/_internal/render-element.ts",
      "target": "components/ui/heidi/_internal/render-element.ts",
      "type": "registry:ui"
    },
    {
      "content": "/**\n * Shared id sanitizer for heidi-ui hosts.\n *\n * React's useId() emits characters that are invalid in HTML id / attribute\n * selectors and in CSS identifiers (`«r0»` in React 19, `:r0:` earlier).\n * heidi-ui interpolates these ids into element ids, aria relationships, and\n * dashed-ident anchor names (`--hui-*-anchor-…`), so everything outside\n * [a-zA-Z0-9_-] is stripped.\n */\n\nexport function safeId(id: string): string {\n  return id.replace(/[^a-zA-Z0-9_-]/g, \"\");\n}\n",
      "path": "packages/heidi-ui/src/_internal/safe-id.ts",
      "target": "components/ui/heidi/_internal/safe-id.ts",
      "type": "registry:ui"
    },
    {
      "content": "export type SafeTrianglePoint = {\n  x: number;\n  y: number;\n};\n\ntype SafeTriangleRect = Pick<DOMRect, \"bottom\" | \"left\" | \"right\" | \"top\">;\n\nexport function pointInTriangle(\n  point: SafeTrianglePoint,\n  first: SafeTrianglePoint,\n  second: SafeTrianglePoint,\n  third: SafeTrianglePoint\n): boolean {\n  const sign = (\n    value: SafeTrianglePoint,\n    edgeStart: SafeTrianglePoint,\n    edgeEnd: SafeTrianglePoint\n  ) =>\n    (value.x - edgeEnd.x) * (edgeStart.y - edgeEnd.y) -\n    (edgeStart.x - edgeEnd.x) * (value.y - edgeEnd.y);\n  const firstSign = sign(point, first, second);\n  const secondSign = sign(point, second, third);\n  const thirdSign = sign(point, third, first);\n  const hasNegative =\n    firstSign < 0 || secondSign < 0 || thirdSign < 0;\n  const hasPositive =\n    firstSign > 0 || secondSign > 0 || thirdSign > 0;\n  return !(hasNegative && hasPositive);\n}\n\n/**\n * Return the padded destination edge facing the pointer's departure point.\n *\n * ponytail: choosing the edge at the smallest scalar distance is a different\n * question and fails for an ordinary top-aligned submenu. A pointer leaving\n * below its trigger can be numerically closer to the submenu's top than its\n * left, even though it approaches from the left. That puts the whole bridge\n * above the diagonal and drops pointer grace on the first move. Containment\n * chooses the approach axis first; horizontal wins because submenus open\n * beside their triggers, with vertical as the collision-flip fallback.\n */\nexport function pointerBridgeEdge(\n  rect: SafeTriangleRect,\n  start: SafeTrianglePoint\n): [SafeTrianglePoint, SafeTrianglePoint] {\n  const padding = 8;\n  if (start.x <= rect.left) {\n    return [\n      { x: rect.left, y: rect.top - padding },\n      { x: rect.left, y: rect.bottom + padding }\n    ];\n  }\n  if (start.x >= rect.right) {\n    return [\n      { x: rect.right, y: rect.top - padding },\n      { x: rect.right, y: rect.bottom + padding }\n    ];\n  }\n  if (start.y <= rect.top) {\n    return [\n      { x: rect.left - padding, y: rect.top },\n      { x: rect.right + padding, y: rect.top }\n    ];\n  }\n  return [\n    { x: rect.left - padding, y: rect.bottom },\n    { x: rect.right + padding, y: rect.bottom }\n  ];\n}\n",
      "path": "packages/heidi-ui/src/_internal/safe-triangle.ts",
      "target": "components/ui/heidi/_internal/safe-triangle.ts",
      "type": "registry:ui"
    },
    {
      "content": "import { useEffect, useLayoutEffect } from \"react\";\n\n/**\n * `useLayoutEffect` that does not warn on the server.\n *\n * ponytail: fifteen primitives imported `useLayoutEffect` straight from React.\n * Every one of them carries `\"use client\"`, which is easy to misread as\n * \"client-only\" — in the App Router it means \"hydrated on the client\", and the\n * component is still rendered to HTML on the server first. React logs\n * \"useLayoutEffect does nothing on the server\" for each one, so a consumer\n * doing SSR saw a wall of warnings the app that ships this library never saw,\n * because it renders these routes on the client path.\n *\n * The branch is evaluated ONCE at module scope, not per render, and it is\n * therefore stable across a component's lifetime — swapping which hook is\n * called between renders would violate the rules of hooks. `typeof document`\n * rather than `typeof window`: both work, but `document` is the thing the\n * effect actually needs, and it keeps the check honest in exotic runtimes that\n * define a partial `window`.\n *\n * Rejected: `useInsertionEffect`, which runs earlier but is specified for\n * style injection and is not a general layout hook; and per-file guards, which\n * is what the 15 copies would have become.\n */\nexport const useHeidiLayoutEffect =\n  typeof document === \"undefined\" ? useEffect : useLayoutEffect;\n",
      "path": "packages/heidi-ui/src/_internal/use-layout-effect.ts",
      "target": "components/ui/heidi/_internal/use-layout-effect.ts",
      "type": "registry:ui"
    }
  ],
  "name": "context-menu",
  "registryDependencies": [
    "@heidi-ui/heidi-tokens"
  ],
  "title": "Context-menu",
  "type": "registry:ui"
}
