Skip to main content
Glama

Karakeep MCP server

by karakeep-app
dynamic-popover.tsx5.86 kB
import * as React from "react"; import * as PopoverPrimitive from "@radix-ui/react-popover"; import { cn } from "../../utils/css"; interface DynamicPopoverContentProps extends React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content> { /** * Whether to enable dynamic height adjustment * If true, use max-h when content can fit the viewport, otherwise use fixed height * If false, always use h-[var(--radix-popover-content-available-height)] */ dynamicHeight?: boolean; /** * Debounce delay for height adjustment (milliseconds) * Used to optimize performance and avoid frequent recalculations */ debounceMs?: number; } /** * Custom Hook for debouncing */ function useDebounce<T>(value: T, delay: number): T { const [debouncedValue, setDebouncedValue] = React.useState<T>(value); React.useEffect(() => { const handler = setTimeout(() => { setDebouncedValue(value); }, delay); return () => { clearTimeout(handler); }; }, [value, delay]); return debouncedValue; } /** * Utility function to get available height */ function getAvailableHeight(element: HTMLElement): number { try { const cssValue = getComputedStyle(element).getPropertyValue( "--radix-popover-content-available-height", ); const parsedValue = parseInt(cssValue, 10); // If CSS variable value cannot be obtained, fallback to 80% of viewport height return !isNaN(parsedValue) && parsedValue > 0 ? parsedValue : Math.floor(window.innerHeight * 0.8); } catch (error) { console.warn("Failed to get available height from CSS variable:", error); return Math.floor(window.innerHeight * 0.8); } } /** * Utility function to calculate content height */ function getContentHeight(element: HTMLElement): number { try { return element.scrollHeight; } catch (error) { console.warn("Failed to get content height:", error); return 0; } } const DynamicPopoverContent = React.forwardRef< React.ElementRef<typeof PopoverPrimitive.Content>, DynamicPopoverContentProps >( ( { className, align = "center", sideOffset = 4, dynamicHeight = true, debounceMs = 100, children, ...props }, ref, ) => { const contentRef = React.useRef<HTMLDivElement>(null); // Use state to manage height class name const [heightClass, setHeightClass] = React.useState<string>( "max-h-[var(--radix-popover-content-available-height)]", ); // Create a dependency to trigger recalculation const [childrenKey, setChildrenKey] = React.useState(0); // Use debounce to optimize performance const debouncedChildrenKey = useDebounce(childrenKey, debounceMs); // Listen for children changes React.useEffect(() => { setChildrenKey((prev) => prev + 1); }, [children]); // Utility function to merge refs const setRefs = React.useCallback( (node: HTMLDivElement | null) => { // Set internal ref contentRef.current = node; // Set external ref if (typeof ref === "function") { ref(node); } else if (ref) { ref.current = node; } }, [ref], ); // Core logic for calculating height const calculateHeight = React.useCallback(() => { if (!dynamicHeight || !contentRef.current) { return; } const element = contentRef.current; const availableHeight = getAvailableHeight(element); const contentHeight = getContentHeight(element); // Add some buffer to avoid edge cases const BUFFER = 10; if (contentHeight + BUFFER > availableHeight) { setHeightClass("h-[var(--radix-popover-content-available-height)]"); } else { setHeightClass("max-h-[var(--radix-popover-content-available-height)]"); } }, [dynamicHeight]); // Use useLayoutEffect to avoid layout flickering React.useLayoutEffect(() => { calculateHeight(); }, [calculateHeight, debouncedChildrenKey]); // Handle window resize React.useEffect(() => { if (!dynamicHeight) return; const handleResize = () => { calculateHeight(); }; window.addEventListener("resize", handleResize); return () => { window.removeEventListener("resize", handleResize); }; }, [calculateHeight, dynamicHeight]); // Define all styles as a single constant for better performance and simplicity const POPOVER_STYLES = "z-50 w-72 overflow-y-auto rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2"; // Determine final height class name const finalHeightClass = React.useMemo(() => { return dynamicHeight ? heightClass : "h-[var(--radix-popover-content-available-height)]"; }, [dynamicHeight, heightClass]); // Memoize the complete class name for performance const popoverClassName = React.useMemo( () => cn(POPOVER_STYLES, finalHeightClass, className), [finalHeightClass, className], ); return ( <PopoverPrimitive.Portal> <PopoverPrimitive.Content ref={setRefs} align={align} sideOffset={sideOffset} className={popoverClassName} {...props} > {children} </PopoverPrimitive.Content> </PopoverPrimitive.Portal> ); }, ); DynamicPopoverContent.displayName = PopoverPrimitive.Content.displayName; export { DynamicPopoverContent };

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/karakeep-app/karakeep'

If you have feedback or need assistance with the MCP directory API, please join our Discord server