{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "core-console-tooltip",
  "type": "registry:ui",
  "title": "Console Tooltip",
  "description": "A short visual label for a console control on Base UI's Tooltip, opening after a short hover delay and at once on keyboard focus, closing on Escape, blur or pointer leave. The child element itself is the trigger through Base UI's render prop, so it wraps any button without an extra element. The popup portals into the fullscreen element while a face is in browser fullscreen, carries the face's theme variables and an optional shortcut, stacks at --uikit-z-overlay, and fades only when motion is allowed.",
  "dependencies": [
    "@base-ui/react",
    "maruaudio@>=0.0.3 <0.1.0"
  ],
  "registryDependencies": [],
  "files": [
    {
      "path": "components/ui/console-tooltip.tsx",
      "content": "/**\n * Source: https://maru.audio/ui-kit/console-tooltip\n * Copyright © [Maru](https://maru.audio)\n *\n * Maru UI Kit — https://maru.audio/ui-kit\n *\n * Installed from the Maru UI registry. This file is yours now: edit it\n * freely, it will not be overwritten unless you re-run the add command.\n *\n * Keep this item’s companion files and CSS imports together. Dependencies\n * declared by the registry are installed with the source.\n */\n/**\n * ConsoleTooltip — a short label for a console control, shown on hover and on\n * keyboard focus: \"Export MIDI\", \"Loop  L\".\n *\n * Built on Base UI's Tooltip, which opens it after a short hover delay and at\n * once on keyboard focus, closes it on Escape, pointer leave or blur, and\n * leaves touch alone (a tap activates the control). The trigger is the child\n * element itself, through Base UI's `render`, so it wraps any button without\n * an extra element:\n *\n *   <ConsoleTooltip content=\"Export MIDI\" shortcut=\"⌘E\">\n *     <button aria-label=\"Export MIDI\">…</button>\n *   </ConsoleTooltip>\n *\n * A tooltip is visual only. The child must carry its own accessible name\n * (text or `aria-label`), and anything a reader needs to know belongs in\n * visible text or an InfoTip, not here. The child must accept a `ref` and\n * spread the props it is given (a DOM element, or a component that forwards\n * both).\n *\n * The popup is portaled into `overlayContainerFor(trigger)` when it opens: the\n * fullscreen element while the face is in browser fullscreen, the document\n * body otherwise, with the face's theme variables copied onto it. It appears\n * without animation under reduced motion and fades in over 100ms otherwise.\n */\n\nimport {\n  createElement,\n  useRef,\n  useState,\n  type CSSProperties,\n  type JSX,\n  type ReactElement,\n  type ReactNode,\n} from \"react\"\nimport { Tooltip } from \"@base-ui/react/tooltip\"\nimport {\n  OVERLAY_POSITIONER_DEFAULTS,\n  overlayPopupStyle,\n  overlayContainerFor,\n} from \"maruaudio/ui/shared/overlay-container\"\nimport {\n  OVERLAY_Z_INDEX,\n  contextMenuPortalTheme,\n} from \"maruaudio/ui/shared/context-menu-target\"\n\nexport interface ConsoleTooltipProps {\n  readonly positionerProps?: Omit<Tooltip.Positioner.Props, \"children\">\n  /** The tooltip text. Keep it to a few words. */\n  readonly content: ReactNode\n  /** A keyboard shortcut drawn after the text, such as \"⌘↵\". */\n  readonly shortcut?: string\n  /** Which side of the trigger it opens on. Defaults to \"top\". */\n  readonly side?: \"top\" | \"bottom\" | \"left\" | \"right\"\n  /** Hover delay before opening, in milliseconds. Defaults to 350. */\n  readonly delay?: number\n  /** Do not open at all. */\n  readonly disabled?: boolean\n  /** The trigger: one element that accepts a ref and spreads its props. */\n  readonly children: ReactElement\n}\n\n/** Hover delay before a console tooltip opens, in milliseconds. */\nexport const CONSOLE_TOOLTIP_DELAY = 350\n\n/** The `href` React dedupes the tooltip stylesheet by. */\nexport const CONSOLE_TOOLTIP_STYLES_HREF = \"uikit-console-tooltip\"\n\n/**\n * The fade, only for a reader who has not asked for reduced motion. Base UI\n * sets `data-starting-style` and `data-ending-style` for one frame at either\n * end; without the rule the popup simply appears and disappears.\n */\nexport const CONSOLE_TOOLTIP_STYLES = String.raw`\n@media (prefers-reduced-motion: no-preference){\n[data-console-tooltip]{transition:opacity 100ms ease-out}\n[data-console-tooltip][data-starting-style],[data-console-tooltip][data-ending-style]{opacity:0}\n}\n`.replace(/\\n/g, \"\")\n\nconst popupStyle: CSSProperties = {\n  ...overlayPopupStyle({ maxWidth: \"18rem\" }),\n  maxHeight: \"none\",\n  overflow: \"visible\",\n  overflowWrap: \"anywhere\",\n  display: \"flex\",\n  alignItems: \"baseline\",\n  gap: 8,\n  padding: \"4px 8px\",\n  borderRadius: 6,\n  border: \"1px solid var(--uikit-menu-border, rgba(148, 163, 184, 0.35))\",\n  background: \"var(--uikit-menu-surface, #202127)\",\n  // 14.6:1 on the fallback surface.\n  color: \"var(--uikit-menu-text, #f4f4f5)\",\n  boxShadow: \"0 6px 18px rgb(0 0 0 / .32)\",\n  font: \"12px/1.35 var(--uikit-font, ui-sans-serif, system-ui, sans-serif)\",\n  pointerEvents: \"none\",\n}\n\nconst shortcutStyle: CSSProperties = {\n  // 5.4:1 on the fallback surface.\n  color:\n    \"var(--uikit-menu-text-muted, var(--uikit-text-muted, rgba(244, 244, 245, 0.55)))\",\n  font: \"inherit\",\n  fontVariantNumeric: \"tabular-nums\",\n  whiteSpace: \"nowrap\",\n}\n\ninterface TooltipLayer {\n  readonly container: HTMLElement\n  readonly theme: CSSProperties\n}\n\n/** A tooltip on a console control, on hover and keyboard focus. */\nexport function ConsoleTooltip({\n  content,\n  shortcut,\n  side = \"top\",\n  delay = CONSOLE_TOOLTIP_DELAY,\n  disabled = false,\n  children,\n  positionerProps,\n}: ConsoleTooltipProps): JSX.Element {\n  const triggerRef = useRef<HTMLElement | null>(null)\n  const [layer, setLayer] = useState<TooltipLayer | null>(null)\n\n  return (\n    <Tooltip.Root\n      disabled={disabled}\n      onOpenChange={(open, details) => {\n        if (!open) return\n        // Chosen as it opens: fullscreen may have changed since last time.\n        const trigger =\n          (details.trigger as HTMLElement | undefined) ?? triggerRef.current\n        const container = overlayContainerFor(trigger ?? null)\n        if (trigger && container) {\n          setLayer({ container, theme: contextMenuPortalTheme(trigger) })\n        }\n      }}\n    >\n      <Tooltip.Trigger\n        ref={(element: HTMLElement | null) => {\n          triggerRef.current = element\n        }}\n        delay={delay}\n        closeOnClick\n        render={children}\n      />\n      {layer ? (\n        <Tooltip.Portal container={layer.container}>\n          <Tooltip.Positioner\n            {...OVERLAY_POSITIONER_DEFAULTS}\n            side={side}\n            sideOffset={6}\n            collisionPadding={8}\n            style={{ zIndex: OVERLAY_Z_INDEX }}\n            {...positionerProps}\n          >\n            <Tooltip.Popup\n              data-console-tooltip=\"\"\n              style={{ ...popupStyle, ...layer.theme }}\n            >\n              {createElement(\n                \"style\",\n                { href: CONSOLE_TOOLTIP_STYLES_HREF, precedence: \"default\" },\n                CONSOLE_TOOLTIP_STYLES\n              )}\n              <span>{content}</span>\n              {shortcut ? <kbd style={shortcutStyle}>{shortcut}</kbd> : null}\n            </Tooltip.Popup>\n          </Tooltip.Positioner>\n        </Tooltip.Portal>\n      ) : null}\n    </Tooltip.Root>\n  )\n}\n",
      "type": "registry:component",
      "target": "@ui/console-tooltip.tsx"
    }
  ]
}
