@baseline-ui/mcp 0.0.0-nightly-20251125000500 → 0.0.0-nightly-20251203000617

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,6 +1,12 @@
1
1
  # @baseline-ui/mcp
2
2
 
3
- ## 0.0.0-nightly-20251125000500
3
+ ## 0.0.0-nightly-20251203000617
4
+
5
+ ## 0.47.0
6
+
7
+ ### Patch Changes
8
+
9
+ - a75ad6f: Added resource with guidelines for Baseline UI
4
10
 
5
11
  ## 0.46.2
6
12
 
@@ -27,7 +27,7 @@
27
27
  "Group": "import React from \"react\";\nimport { useHover } from \"react-aria\";\n\nimport type { GroupProps } from \"./Group.types\";\n\nexport const Group = React.forwardRef<HTMLDivElement, GroupProps>(\n (\n {\n className,\n style,\n children,\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlock,\n role = \"group\",\n isDisabled,\n ...ariaLabelling\n },\n ref,\n ) => {\n const { hoverProps, isHovered } = useHover({ isDisabled });\n\n return (\n <div\n {...hoverProps}\n role={role}\n data-hovered={isHovered}\n data-disabled={isDisabled}\n data-block-id={dataBlockId}\n data-block-class={dataBlock}\n className={className}\n style={style}\n {...ariaLabelling}\n ref={ref}\n >\n {children}\n </div>\n );\n },\n);\n\nGroup.displayName = \"Group\";\n",
28
28
  "I18nProvider": "import { I18nProvider as Provider } from \"react-aria\";\nimport React, { useEffect, useRef, useState } from \"react\";\n\nimport type { LocalizedStrings } from \"react-aria\";\nimport type { I18nProviderProps } from \"./I18Provider.types\";\n\nexport const IntlMessagesProvider = React.createContext<{\n messages: LocalizedStrings;\n shouldLogMissingMessages: boolean;\n hasMissingErrorLoggedFor: React.MutableRefObject<Set<string>>;\n setLang: (lang: string) => void;\n}>({\n messages: {},\n shouldLogMissingMessages: true,\n hasMissingErrorLoggedFor: { current: new Set() },\n setLang: () => {},\n});\n\nexport const I18nProvider: React.FC<I18nProviderProps> = ({\n children,\n messages = {},\n shouldLogMissingMessages = true,\n ...props\n}) => {\n const hasMissingErrorLoggedFor = useRef<Set<string>>(new Set());\n\n const [lang, setLang] = useState(props.locale || \"\");\n\n useEffect(() => {\n hasMissingErrorLoggedFor.current.clear();\n }, [lang, props.locale]);\n\n const messagesValue = React.useMemo(\n () => ({\n messages,\n shouldLogMissingMessages,\n hasMissingErrorLoggedFor,\n setLang,\n }),\n [messages, shouldLogMissingMessages, setLang],\n );\n\n return (\n <Provider {...props}>\n <IntlMessagesProvider.Provider value={messagesValue}>\n {children}\n </IntlMessagesProvider.Provider>\n </Provider>\n );\n};\n\nI18nProvider.displayName = \"I18nProvider\";\n",
29
29
  "Icon": "import * as Dompurify from \"dompurify\";\nimport { getOwnerDocument, mergeRefs } from \"@react-aria/utils\";\nimport React, { useId } from \"react\";\n\nimport { invariant } from \"../../utils\";\n\nimport type { IconComponentProps } from \"./Icon.types\";\n\nconst setSizeAttributes = (svgElement: SVGElement, size: number | string) => {\n svgElement.setAttribute(\n \"height\",\n typeof size === \"number\" ? `${size}px` : size,\n );\n svgElement.setAttribute(\n \"width\",\n typeof size === \"number\" ? `${size}px` : size,\n );\n};\n\nconst updateSvgTitle = (svgElement: SVGElement, title: string, id: string) => {\n const titleElement = svgElement.querySelector(\"title\");\n const ownerDocument = getOwnerDocument(svgElement);\n\n if (titleElement) {\n titleElement.textContent = title;\n titleElement.setAttribute(\"id\", id);\n } else {\n const newTitleElement = ownerDocument.createElement(\"title\");\n newTitleElement.textContent = title;\n newTitleElement.setAttribute(\"id\", id);\n svgElement.prepend(newTitleElement);\n }\n};\n\nconst setClassName = (svgElement: SVGElement, className?: string) => {\n if (className) {\n svgElement.classList.add(className);\n }\n};\n\nconst getSanitizedSvgElement = (_svg: string, ownerDocument: Document) => {\n const span = ownerDocument.createElement(\"span\");\n span.innerHTML = Dompurify.default.sanitize(_svg);\n const svgElement = span.querySelector(\"svg\");\n invariant(svgElement, \"Icon: svg prop must be a valid svg string\");\n return svgElement;\n};\n\nconst Icon = React.forwardRef<HTMLSpanElement, IconComponentProps>(\n ({ size, title, className, style, color, ...rest }, ref) => {\n const id = useId();\n const svgRef = React.useRef<HTMLSpanElement>(null);\n\n const _svg = \"svg\" in rest ? rest.svg : undefined;\n\n const svgString = React.useMemo(() => {\n if (_svg) {\n const ownerDocument = getOwnerDocument(svgRef.current);\n\n const svgElement = getSanitizedSvgElement(_svg, ownerDocument);\n if (size) {\n setSizeAttributes(svgElement, size);\n }\n\n if (title) {\n updateSvgTitle(svgElement, title, id);\n }\n\n setClassName(svgElement, className);\n\n return svgElement.outerHTML;\n }\n }, [_svg, size, title, className, id]);\n\n return (\n <span\n className={className}\n ref={mergeRefs(ref, svgRef)}\n style={{ display: \"contents\", ...style, color }}\n dangerouslySetInnerHTML={svgString ? { __html: svgString } : undefined}\n />\n );\n },\n);\n\nIcon.displayName = \"Icon\";\n\nexport { Icon };\n",
30
- "ImageDropZone": "import { useControlledState } from \"@react-stately/utils\";\nimport React, { useMemo } from \"react\";\nimport {\n VisuallyHidden,\n mergeProps,\n useClipboard,\n useDrop,\n useField,\n useFocusRing,\n useHover,\n} from \"react-aria\";\n\nimport messages from \"./intl\";\nimport { classNames, filterTruthyValues, invariant } from \"../../utils\";\nimport { buttonCn } from \"../ActionButton/ActionButton.css\";\nimport { Box } from \"../Box\";\nimport { freehandCanvasFooterCn } from \"../FreehandCanvas/FreehandCanvas.css\";\nimport { ProgressSpinner } from \"../ProgressSpinner\";\nimport { onDropOrPaste } from \"../FileUpload/hooks/useFileUpload\";\nimport { imageDropZoneCn, opacityNoneCn, previewCn } from \"./ImageDropZone.css\";\nimport { ActionButton } from \"../ActionButton\";\nimport { useI18n, useImage } from \"../../hooks\";\n\nimport type { ActionButtonProps } from \"../ActionButton\";\nimport type { ChangeEventHandler } from \"react\";\nimport type { ImageDropZoneProps } from \"./ImageDropZone.types\";\n\nexport const ImageDropZone = React.forwardRef<\n HTMLDivElement,\n ImageDropZoneProps\n>(\n (\n {\n className,\n style,\n isInline = true,\n accept = \"image/*\",\n defaultImageSrc,\n imageSrc,\n imageAlt,\n pickerButtonLabel,\n isDisabled,\n onDrop,\n onPaste,\n includeCheckeredBackground = true,\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n pickerButtonStyle,\n pickerButtonClassName,\n footerClassName,\n footerStyle,\n clearLabel,\n placeholder,\n labelStyle,\n labelClassName,\n ...rest\n },\n ref,\n ) => {\n const elementRef = React.useRef<HTMLLabelElement>(null);\n const inputRef = React.useRef<HTMLInputElement>(null);\n const { formatMessage } = useI18n(messages);\n\n const [url, setUrl] = useControlledState(\n imageSrc,\n defaultImageSrc || null,\n (value) => {\n invariant(typeof value !== \"string\");\n\n rest.onValueChange?.(value ? [value] : []);\n },\n );\n\n const _url = useMemo(() => getImageSrc(url || undefined), [url]);\n\n const { fieldProps, labelProps } = useField({\n ...rest,\n ref: elementRef,\n });\n\n const { dropProps, isDropTarget } = useDrop({\n ref: elementRef,\n onDrop: async (e) => {\n if (isDisabled) return;\n const files = await onDropOrPaste({ items: e.items });\n if (files.length) {\n setUrl(files[0]);\n }\n onDrop?.(e);\n },\n ...rest,\n });\n\n const { clipboardProps } = useClipboard({\n onPaste: async (items) => {\n if (isDisabled) return;\n const files = await onDropOrPaste({ items });\n if (files.length) {\n setUrl(files[0]);\n }\n onPaste?.(items);\n },\n ...rest,\n });\n\n const reset = () => {\n setUrl(null);\n if (inputRef.current) {\n inputRef.current.value = \"\";\n }\n };\n\n const inputProps = {\n type: \"file\",\n onChange: ((e) => {\n const files = [...(e.target.files as FileList)];\n setUrl(files[0]);\n\n rest.onChange?.(e);\n }) as ChangeEventHandler<HTMLInputElement>,\n accept,\n disabled: isDisabled,\n } as React.ComponentProps<\"input\">;\n\n const { focusProps, isFocused, isFocusVisible } = useFocusRing({\n within: true,\n });\n const { hoverProps, isHovered } = useHover({ isDisabled });\n\n const { imgProps, isLoading, isLoaded } = useImage({\n src: _url,\n alt: imageAlt || \"\",\n });\n\n const chooseFileButtonProps = {\n label: pickerButtonLabel || formatMessage(\"selectImage\"),\n onPress: () => elementRef.current?.click(),\n variant: \"secondary\",\n isDisabled,\n excludeFromTabOrder: true,\n className: classNames(\n buttonCn({ isFocusVisible }),\n {\n [opacityNoneCn]: isDropTarget && !isDisabled,\n },\n pickerButtonClassName,\n ),\n style: pickerButtonStyle,\n } as ActionButtonProps;\n\n const dataAttrs = filterTruthyValues({\n \"data-loaded\": isLoaded,\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n \"data-hovered\": isHovered,\n \"data-focused\": isFocused,\n \"data-drop-target\": isDropTarget,\n \"data-disabled\": isDisabled,\n \"data-focus-visible\": isFocusVisible,\n });\n\n return (\n <Box\n {...dataAttrs}\n className={classNames(\"BaselineUI-ImageDropZone\", className)}\n display=\"flex\"\n flexDirection=\"column\"\n alignItems=\"center\"\n style={style}\n ref={ref}\n >\n <label\n {...mergeProps(dropProps, hoverProps, focusProps, labelProps)}\n className={classNames(\n imageDropZoneCn({\n isInline,\n isHovered,\n isDisabled,\n hasLoadedImage: !!_url && isLoaded,\n showFocusRing: isDropTarget || (!!_url && isFocusVisible),\n }),\n labelClassName,\n )}\n style={labelStyle}\n ref={elementRef}\n >\n <VisuallyHidden>\n <input\n {...mergeProps(fieldProps, clipboardProps, inputProps)}\n ref={inputRef}\n />\n </VisuallyHidden>\n\n {!_url && <ActionButton {...chooseFileButtonProps} />}\n\n {!!_url &&\n (isLoading ? (\n <ProgressSpinner />\n ) : (\n <img\n className={previewCn({\n isInline,\n isDisabled,\n hasLoadedImage:\n !!_url && isLoaded && includeCheckeredBackground,\n })}\n {...imgProps}\n />\n ))}\n </label>\n\n {placeholder || clearLabel ? (\n <div\n style={footerStyle}\n className={classNames(\n freehandCanvasFooterCn({ isInline, isDisabled }),\n footerClassName,\n )}\n >\n {(!!_url || !placeholder) && clearLabel ? (\n <ActionButton\n size=\"sm\"\n variant=\"ghost\"\n label={clearLabel}\n isDisabled={isDisabled}\n onPress={reset}\n />\n ) : (\n <ActionButton\n size=\"sm\"\n variant=\"ghost\"\n label={placeholder as string}\n isDisabled={true}\n />\n )}\n </div>\n ) : null}\n </Box>\n );\n },\n);\n\nImageDropZone.displayName = \"ImageDropZone\";\n\nfunction getImageSrc(imageSrc: string | File | undefined) {\n if (imageSrc instanceof File) {\n return URL.createObjectURL(imageSrc);\n }\n\n return imageSrc;\n}\n",
30
+ "ImageDropZone": "import { useControlledState } from \"@react-stately/utils\";\nimport React, { useMemo } from \"react\";\nimport {\n VisuallyHidden,\n mergeProps,\n useClipboard,\n useDrop,\n useField,\n useFocusRing,\n useHover,\n} from \"react-aria\";\n\nimport messages from \"./intl\";\nimport { classNames, filterTruthyValues, invariant } from \"../../utils\";\nimport { buttonCn } from \"../ActionButton/ActionButton.css\";\nimport { Box } from \"../Box\";\nimport { freehandCanvasFooterCn } from \"../FreehandCanvas/FreehandCanvas.css\";\nimport { ProgressSpinner } from \"../ProgressSpinner\";\nimport { onDropOrPaste } from \"../FileUpload/hooks/useFileUpload\";\nimport { imageDropZoneCn, opacityNoneCn, previewCn } from \"./ImageDropZone.css\";\nimport { ActionButton } from \"../ActionButton\";\nimport { useI18n, useImage } from \"../../hooks\";\n\nimport type { ActionButtonProps } from \"../ActionButton\";\nimport type { ChangeEventHandler } from \"react\";\nimport type { ImageDropZoneProps } from \"./ImageDropZone.types\";\n\nexport const ImageDropZone = React.forwardRef<\n HTMLDivElement,\n ImageDropZoneProps\n>(\n (\n {\n className,\n style,\n isInline = true,\n accept = \"image/*\",\n defaultImageSrc,\n imageSrc,\n imageAlt,\n pickerButtonLabel,\n isDisabled,\n onDrop,\n onPaste,\n includeCheckeredBackground = true,\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n pickerButtonStyle,\n pickerButtonClassName,\n footerClassName,\n footerStyle,\n clearLabel,\n placeholder,\n labelStyle,\n labelClassName,\n ...rest\n },\n ref,\n ) => {\n const elementRef = React.useRef<HTMLLabelElement>(null);\n const inputRef = React.useRef<HTMLInputElement>(null);\n const { formatMessage } = useI18n(messages);\n\n const [url, setUrl] = useControlledState(\n imageSrc,\n defaultImageSrc || null,\n (value) => {\n invariant(typeof value !== \"string\");\n\n rest.onValueChange?.(value ? [value] : []);\n },\n );\n\n const _url = useMemo(() => getImageSrc(url || undefined), [url]);\n\n const { fieldProps, labelProps } = useField({\n ...rest,\n ref: elementRef,\n });\n\n const { dropProps, isDropTarget } = useDrop({\n ref: elementRef,\n onDrop: async (e) => {\n if (isDisabled) return;\n const files = await onDropOrPaste({ items: e.items });\n if (files.length) {\n setUrl(files[0]);\n }\n onDrop?.(e);\n },\n ...rest,\n });\n\n const { clipboardProps } = useClipboard({\n onPaste: async (items) => {\n if (isDisabled) return;\n const files = await onDropOrPaste({ items });\n if (files.length) {\n setUrl(files[0]);\n }\n onPaste?.(items);\n },\n ...rest,\n });\n\n const reset = () => {\n setUrl(null);\n if (inputRef.current) {\n inputRef.current.value = \"\";\n }\n };\n\n const inputProps = {\n type: \"file\",\n onChange: ((e) => {\n const files = [...(e.target.files as FileList)];\n setUrl(files[0]);\n\n rest.onChange?.(e);\n }) as ChangeEventHandler<HTMLInputElement>,\n accept,\n disabled: isDisabled,\n } as React.ComponentProps<\"input\">;\n\n const { focusProps, isFocused, isFocusVisible } = useFocusRing({\n within: true,\n });\n const { hoverProps, isHovered } = useHover({ isDisabled });\n\n const { imgProps, isLoading, isLoaded } = useImage({\n src: _url,\n alt: imageAlt || \"\",\n });\n\n const chooseFileButtonProps = {\n label: pickerButtonLabel || formatMessage(\"selectImage\"),\n onPress: () => elementRef.current?.click(),\n variant: \"secondary\",\n isDisabled,\n excludeFromTabOrder: true,\n className: classNames(\n buttonCn({ isFocusVisible }),\n {\n [opacityNoneCn]: isDropTarget && !isDisabled,\n },\n pickerButtonClassName,\n ),\n style: pickerButtonStyle,\n } as ActionButtonProps;\n\n const dataAttrs = filterTruthyValues({\n \"data-loaded\": isLoaded,\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n \"data-hovered\": isHovered,\n \"data-focused\": isFocused,\n \"data-drop-target\": isDropTarget,\n \"data-disabled\": isDisabled,\n \"data-focus-visible\": isFocusVisible,\n });\n\n return (\n <Box\n {...dataAttrs}\n className={classNames(\"BaselineUI-ImageDropZone\", className)}\n display=\"flex\"\n flexDirection=\"column\"\n alignItems=\"center\"\n style={style}\n ref={ref}\n >\n <label\n {...mergeProps(dropProps, hoverProps, focusProps, labelProps)}\n className={classNames(\n imageDropZoneCn({\n isInline,\n isHovered,\n isDisabled,\n hasLoadedImage: !!_url && isLoaded,\n showFocusRing: isDropTarget || (!!_url && isFocusVisible),\n }),\n labelClassName,\n )}\n style={labelStyle}\n ref={elementRef}\n >\n <VisuallyHidden>\n <input\n {...mergeProps(fieldProps, clipboardProps, inputProps)}\n ref={inputRef}\n />\n </VisuallyHidden>\n\n {!_url && <ActionButton {...chooseFileButtonProps} />}\n\n {!!_url &&\n (isLoading ? (\n <ProgressSpinner />\n ) : (\n <img\n className={previewCn({\n isInline,\n isDisabled,\n hasLoadedImage:\n !!_url && isLoaded && includeCheckeredBackground,\n })}\n {...imgProps}\n />\n ))}\n </label>\n\n {placeholder || clearLabel ? (\n <div\n style={footerStyle}\n className={classNames(\n freehandCanvasFooterCn({ isInline, isDisabled }),\n footerClassName,\n )}\n >\n {(!!_url || !placeholder) && clearLabel ? (\n <ActionButton\n size=\"sm\"\n variant=\"ghost\"\n label={clearLabel}\n isDisabled={isDisabled}\n onPress={reset}\n />\n ) : (\n <ActionButton\n size=\"sm\"\n variant=\"ghost\"\n label={placeholder as string}\n isDisabled={true}\n />\n )}\n </div>\n ) : null}\n </Box>\n );\n },\n);\n\nImageDropZone.displayName = \"ImageDropZone\";\n\nfunction getImageSrc(imageSrc: string | File | undefined) {\n if (!imageSrc || typeof imageSrc === \"string\") {\n return imageSrc;\n }\n\n return URL.createObjectURL(imageSrc);\n}\n",
31
31
  "ImageGallery": "import { useControlledState } from \"@react-stately/utils\";\nimport React, { useCallback, useContext, useEffect, useState } from \"react\";\nimport { mergeRefs } from \"@react-aria/utils\";\nimport {\n CollectionRendererContext,\n DropIndicator,\n} from \"react-aria-components\";\nimport { sprinkles } from \"@baseline-ui/tokens\";\n\nimport { classNames } from \"../../utils\";\nimport { defineMessages, useI18n } from \"../../hooks\";\nimport { AlertDialog } from \"../AlertDialog\";\nimport { UNSAFE_ListBox as ListBox } from \"../UNSAFE_ListBox\";\nimport { Modal, ModalContent } from \"../Modal\";\nimport { DragPreviewContent } from \"../shared/components/DragPreviewContent\";\nimport {\n dropIndicatorCn,\n imageGalleryCn,\n imageGalleryOptionCn,\n} from \"./ImageGallery.css\";\nimport { ImageGalleryItem } from \"./ImageGalleryItem\";\nimport {\n getByKey,\n imageItemsToListOption,\n listOptionToImageItems,\n moveAfter,\n moveBefore,\n} from \"./utils\";\nimport { getDynamicClassNameForCollectionItem } from \"../../utils/style\";\nimport { useDragAndDrop } from \"../ListBox/hooks/useDragAndDrop\";\n\nimport type { Key } from \"react-aria\";\nimport type { UNSAFE_ListBoxProps as ListBoxProps } from \"../UNSAFE_ListBox\";\nimport type { DroppableCollectionReorderEvent } from \"@react-types/shared\";\nimport type { ImageGalleryProps } from \"./ImageGallery.types\";\n\nconst Image = {\n src: \"src\",\n alt: \"alt\",\n} as const;\n\nexport const ImageSize = {\n sm: 102,\n md: 170,\n} as const;\n\nexport const ImageGallery = React.forwardRef<HTMLDivElement, ImageGalleryProps>(\n (\n {\n className,\n style,\n fit = \"cover\",\n onReorder,\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n onDelete,\n imageWidth = ImageSize.sm,\n aspectRatio = 3 / 4,\n items,\n defaultItems = [],\n onKeyDown,\n imageContainerClassName,\n imageClassName,\n labelClassName,\n onListChange,\n renderImage,\n layoutTransition,\n imageDimensions = {\n width: imageWidth,\n aspectRatio,\n },\n imageContainerStyle,\n deleteConfirmationTitle = \"Are you sure you want to delete this item?\",\n ...rest\n },\n ref,\n ) => {\n const { isVirtualized } = useContext(CollectionRendererContext);\n const divRef = React.useRef<HTMLDivElement>(null);\n const [itemsToDelete, setItemsToDelete] = useState<Key[]>([]);\n const { formatMessage } = useI18n();\n\n useEffect(() => {\n if (aspectRatio || imageWidth) {\n console.warn(\n \"ImageGallery: aspectRatio and imageWidth are deprecated. Use imageDimensions instead.\",\n );\n }\n }, [imageWidth, aspectRatio]);\n\n const [selectedKeys, setSelectedKeys] = useControlledState(\n rest.selectedKeys,\n rest.defaultSelectedKeys ?? [],\n rest.onSelectionChange,\n );\n\n const [list, setList] = useControlledState(\n items ? imageItemsToListOption(items) : undefined,\n imageItemsToListOption(defaultItems),\n onListChange\n ? (_items) => {\n onListChange?.(listOptionToImageItems(_items));\n }\n : undefined,\n );\n\n const _onReorder = useCallback(\n (e: DroppableCollectionReorderEvent) => {\n const referenceKey = e.target.key;\n const keysToMove = e.keys;\n\n if (e.target.dropPosition === \"before\") {\n const newList = moveBefore(list, referenceKey, keysToMove);\n setList(newList);\n } else if (e.target.dropPosition === \"after\") {\n const newList = moveAfter(list, referenceKey, keysToMove);\n setList(newList);\n }\n onReorder?.(e);\n },\n [list, onReorder, setList],\n );\n\n const { dragAndDropHooks } = useDragAndDrop({\n onReorder: _onReorder,\n isDisabled: !onReorder,\n renderDragPreview: (items) => (\n <DragPreviewContent\n items={items}\n containerClassName=\".BaselineUI-ImageGallery\"\n previewClassName={imageGalleryOptionCn()}\n itemDataAttribute=\"data-image-key\"\n showCount={true}\n />\n ),\n getItems: (keys) =>\n [...keys].map((key) => {\n const item = getByKey(list, key);\n\n return {\n [Image.src]: item?.src || \"\",\n [Image.alt]: item?.alt || \"\",\n key: key as string,\n };\n }),\n renderDropIndicator(target) {\n return (\n <DropIndicator\n target={target}\n className={classNames(\n dropIndicatorCn({ isVirtualized: !!isVirtualized }),\n \"BaselineUI-DropIndicator\",\n )}\n />\n );\n },\n });\n\n const _onDelete = useCallback(() => {\n if (!onDelete) return;\n\n if (itemsToDelete.length) {\n setList(list.filter((item) => !itemsToDelete.includes(item.id)));\n\n onDelete(itemsToDelete);\n setItemsToDelete([]);\n }\n }, [itemsToDelete, list, onDelete, setList]);\n\n const renderOption = useCallback<\n Exclude<ListBoxProps[\"renderOption\"], undefined>\n >(\n (item, options) => {\n const galleryItem = {\n id: item.id,\n label: item.label,\n src: item.data?.src,\n alt: item.data?.alt,\n };\n\n return (\n <ImageGalleryItem\n key={item.id}\n item={item}\n options={options}\n fit={fit}\n onDelete={\n onDelete\n ? (id: string) => {\n setItemsToDelete([id]);\n }\n : undefined\n }\n renderImage={renderImage}\n className={getDynamicClassNameForCollectionItem(\n imageContainerClassName,\n galleryItem,\n options,\n )}\n imageClassName={imageClassName}\n imageLabelClassName={labelClassName}\n imageDimensions={imageDimensions}\n style={imageContainerStyle}\n layoutTransition={layoutTransition}\n />\n );\n },\n [\n fit,\n onDelete,\n renderImage,\n imageContainerClassName,\n imageClassName,\n labelClassName,\n imageDimensions,\n imageContainerStyle,\n layoutTransition,\n ],\n );\n\n useEffect(() => {\n const container = divRef.current;\n if (!container) return;\n\n const handleKeyDown = (e: KeyboardEvent) => {\n onKeyDown?.(e);\n\n if (e.key === \"Backspace\" || e.key === \"Delete\") {\n if (!onDelete) return;\n\n const _selectedKeys = [...selectedKeys];\n // If there are selected items, delete them\n // Otherwise, delete the item that was focused\n if (_selectedKeys.length) {\n setItemsToDelete([...selectedKeys]);\n } else {\n const key = (e.target as HTMLUListElement).dataset.key as string;\n setItemsToDelete([key]);\n }\n }\n };\n\n container.addEventListener(\"keydown\", handleKeyDown);\n\n return () => {\n container.removeEventListener(\"keydown\", handleKeyDown);\n };\n }, [onDelete, onKeyDown, selectedKeys]);\n\n return (\n <>\n <ListBox\n {...rest}\n ref={mergeRefs(ref, divRef)}\n style={style}\n className={classNames(\n imageGalleryCn,\n \"BaselineUI-ImageGallery\",\n className,\n )}\n data-block-id={dataBlockId}\n data-block-class={dataBlockClass}\n selectedKeys={selectedKeys}\n items={list}\n dragAndDropHooks={dragAndDropHooks}\n layout=\"grid\"\n orientation=\"vertical\"\n renderOption={renderOption}\n onSelectionChange={setSelectedKeys}\n optionClassName={\n onReorder && !isVirtualized\n ? sprinkles({\n marginLeft: \"sm\",\n })\n : undefined\n }\n />\n <Modal\n isOpen={!![...itemsToDelete].length}\n onOpenChange={(open) => {\n if (!open) {\n setItemsToDelete([]);\n }\n }}\n >\n <ModalContent isDismissable={true}>\n <AlertDialog\n showCloseButton={false}\n title={\n typeof deleteConfirmationTitle === \"function\"\n ? deleteConfirmationTitle(\n selectedKeys === \"all\" ? \"all\" : new Set(selectedKeys),\n )\n : deleteConfirmationTitle\n }\n primaryActionLabel={formatMessage(messages.delete)}\n cancelLabel={formatMessage(messages.cancel)}\n onCancel={() => {\n setItemsToDelete([]);\n }}\n onPrimaryAction={_onDelete}\n autoFocusButton=\"cancel\"\n />\n </ModalContent>\n </Modal>\n </>\n );\n },\n);\n\nImageGallery.displayName = \"ImageGallery\";\n\nexport const messages = defineMessages({\n delete: {\n id: \"delete\",\n defaultMessage: \"Delete\",\n },\n cancel: {\n id: \"cancel\",\n defaultMessage: \"Cancel\",\n },\n loading: {\n id: \"loading\",\n defaultMessage: \"Loading\",\n },\n});\n",
32
32
  "InlineAlert": "import {\n CheckmarkCircleFilledIcon,\n ErrorAltCircleFilledIcon,\n InfoCircleFilledIcon,\n WarningFilledIcon,\n XIcon,\n} from \"@baseline-ui/icons/16\";\nimport {\n CheckCircleFilledIcon as CheckFilledIcon20,\n ErrorAltCircleFilledIcon as ErrorAltFilledIcon20,\n InfoCircleFilledIcon as InfoFilledIcon20,\n WarningFilledIcon as WarningFilledIcon20,\n} from \"@baseline-ui/icons/20\";\nimport React from \"react\";\n\nimport messages from \"./intl\";\nimport { classNames } from \"../../utils\";\nimport { ActionButton, ActionIconButton } from \"../\";\nimport {\n contentCn,\n inlineAlertCn,\n inlineAlertDescriptionCn,\n inlineAlertEndCn,\n inlineAlertIconCn,\n inlineAlertTitleCn,\n inlineStartCn,\n} from \"./InlineAlert.css\";\nimport { useI18n } from \"../../hooks\";\n\nimport type { InlineAlertProps } from \"./InlineAlert.types\";\nimport type Messages from \"./intl/en.json\";\n\nconst icons = {\n error: { sm: ErrorAltCircleFilledIcon, md: ErrorAltFilledIcon20 },\n info: { sm: InfoCircleFilledIcon, md: InfoFilledIcon20 },\n success: { sm: CheckmarkCircleFilledIcon, md: CheckFilledIcon20 },\n warning: { sm: WarningFilledIcon, md: WarningFilledIcon20 },\n};\n\nexport const InlineAlert = React.forwardRef<HTMLDivElement, InlineAlertProps>(\n (\n {\n className,\n description,\n actionLabel,\n onAction,\n style,\n variant = \"info\",\n title,\n arrangement = \"single\",\n onClose,\n size = \"sm\",\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n elementProps,\n },\n ref,\n ) => {\n const intl = useI18n<typeof Messages>(messages);\n\n const actionButton = actionLabel && (\n <ActionButton\n variant={arrangement === \"compact\" ? \"tertiary\" : \"ghost\"}\n label={actionLabel}\n onPress={onAction}\n />\n );\n\n const Icon = icons[variant][size];\n\n return (\n <div\n className={classNames(\n inlineAlertCn({\n variant,\n arrangement,\n hasButton: !!actionLabel || !!onClose,\n hasCloseButton: !!onClose,\n }),\n \"BaselineUI-InlineAlert\",\n className,\n )}\n data-block-id={dataBlockId}\n data-block-class={dataBlockClass}\n style={style}\n role=\"alert\"\n ref={ref}\n {...elementProps?.root}\n >\n <div className={inlineStartCn({ arrangement })}>\n <Icon className={inlineAlertIconCn({ variant })} size={16} />\n\n <div\n className={contentCn({ arrangement })}\n {...elementProps?.content}\n >\n <h3\n className={inlineAlertTitleCn({ size })}\n {...elementProps?.title}\n >\n {title}\n </h3>\n <section\n className={inlineAlertDescriptionCn({ size })}\n {...elementProps?.description}\n >\n {description}\n </section>\n </div>\n\n {arrangement === \"compact\" && actionButton}\n </div>\n\n <div className={inlineAlertEndCn({ arrangement })}>\n {arrangement !== \"compact\" && actionButton}\n {onClose ? (\n <ActionIconButton\n variant=\"secondary\"\n icon={XIcon}\n size=\"sm\"\n onPress={onClose}\n aria-label={intl.formatMessage(\"close\")}\n {...elementProps?.close}\n />\n ) : null}\n </div>\n </div>\n );\n },\n);\n\nInlineAlert.displayName = \"InlineAlert\";\n",
33
33
  "InlineToolbar": "import { themeVars } from \"@baseline-ui/tokens\";\nimport {\n getActiveElement,\n getOwnerDocument,\n getOwnerWindow,\n mergeRefs,\n nodeContains,\n useViewportSize,\n} from \"@react-aria/utils\";\nimport { useControlledState } from \"@react-stately/utils\";\nimport { useGranularEffect, useGranularLayoutEffect } from \"granular-hooks\";\nimport React, {\n Fragment,\n useCallback,\n useEffect,\n useLayoutEffect,\n useMemo,\n} from \"react\";\nimport {\n FocusScope,\n mergeProps,\n useInteractOutside,\n useKeyboard,\n useOverlayPosition,\n} from \"react-aria\";\nimport { useOverlayTriggerState } from \"react-stately\";\nimport { useToolbar } from \"@react-aria/toolbar\";\n\nimport { classNames, findFocusableElements, invariant } from \"../../utils\";\nimport { arrowInlineCn, inlineToolbarContentCn } from \"./InlineToolbar.css\";\nimport { InlineToolbarButton } from \"./InlineToolbarButton\";\nimport { Portal } from \"../Portal\";\nimport { Separator } from \"../Separator\";\nimport { useTextSelection } from \"../../hooks\";\n\nimport type { InlineToolbarProps, Item } from \"./InlineToolbar.types\";\n\nexport const InlineToolbar = React.forwardRef<\n HTMLDivElement,\n InlineToolbarProps\n>(\n (\n {\n className,\n onAction,\n items,\n scrollRef,\n placement = \"top\",\n children,\n \"data-block-id\": dataBlockId,\n \"data-block-class\": dataBlockClass,\n disabledKeys,\n isDisabled,\n anchorRect,\n ...rest\n },\n ref,\n ) => {\n const divRef = React.useRef<HTMLDivElement>(null);\n const overlayRef = React.useRef<HTMLDivElement>(null);\n const triggerRef = React.useRef<HTMLDivElement>(null);\n const selectionRef = React.useRef<Selection | null>(null);\n const [selectionRect, setSelectionRect] = useControlledState(\n anchorRect,\n null,\n );\n\n const state = useOverlayTriggerState(rest);\n\n const {\n overlayProps,\n arrowProps,\n placement: placementAxis,\n updatePosition,\n } = useOverlayPosition({\n arrowSize: 13,\n targetRef: triggerRef,\n arrowBoundaryOffset: 8,\n offset: 12,\n overlayRef,\n isOpen: state.isOpen,\n placement,\n scrollRef,\n ...rest,\n });\n\n const size = useViewportSize();\n\n useGranularEffect(\n () => {\n if (selectionRef.current) {\n setSelectionRect(\n selectionRef.current.getRangeAt(0).getBoundingClientRect(),\n );\n }\n },\n [size],\n [setSelectionRect],\n );\n\n const toolbarRef = React.useRef<HTMLDivElement>(null);\n\n const { toolbarProps } = useToolbar(\n {\n ...rest,\n isSingleTabStop: false,\n },\n toolbarRef,\n );\n\n useInteractOutside({\n ref: toolbarRef,\n onInteractOutside() {\n state.close();\n setSelectionRect(null);\n },\n });\n\n useEffect(() => {\n if (isDisabled && state.isOpen) {\n state.close();\n }\n }, [isDisabled, state]);\n\n useTextSelection({\n ref: divRef,\n isDisabled,\n onSelectionChange: (selection) => {\n if (selection.isCollapsed) {\n selectionRef.current = null;\n setSelectionRect(null);\n } else {\n selectionRef.current = selection;\n\n setSelectionRect(selection.getRangeAt(0).getBoundingClientRect());\n }\n },\n });\n\n const handleScroll = useCallback(() => {\n const selection = selectionRef.current;\n if (selection) {\n setSelectionRect(selection.getRangeAt(0).getBoundingClientRect());\n }\n }, [setSelectionRect]);\n\n useGranularEffect(\n () => {\n const ownerDocument = getOwnerDocument(divRef.current);\n\n const _scrollRef = scrollRef?.current ?? ownerDocument.body;\n\n _scrollRef?.addEventListener(\"scroll\", handleScroll);\n\n return () => {\n _scrollRef?.removeEventListener(\"scroll\", handleScroll);\n };\n },\n [scrollRef],\n [updatePosition],\n );\n\n useLayoutEffect(() => {\n if (selectionRect && !state.isOpen) {\n state.open();\n } else if (!selectionRect) {\n state.close();\n }\n }, [selectionRect, state]);\n\n useGranularLayoutEffect(updatePosition, [selectionRect], [updatePosition]);\n\n const _placement =\n placementAxis === \"center\" ? \"top\" : placementAxis || \"top\";\n\n const { keyboardProps } = useKeyboard({\n onKeyDown(e) {\n if (e.key === \"Escape\") {\n state.close();\n setSelectionRect(null);\n } else {\n e.continuePropagation();\n }\n },\n });\n\n const handleToolbarPress = useCallback(\n (item: Item) => {\n invariant(selectionRef.current, \"No selection\");\n onAction?.(item.id, selectionRef.current);\n setSelectionRect(null);\n selectionRef.current?.removeAllRanges();\n },\n [onAction, setSelectionRect],\n );\n\n const _items =\n typeof items === \"function\" ? items(selectionRef.current) : items;\n\n const overlayPositionStyle = useMemo<React.CSSProperties>(() => {\n const ownerWindow = getOwnerWindow(triggerRef.current);\n\n return {\n position: \"absolute\",\n top: (selectionRect?.top || 0) + ownerWindow.scrollY,\n left: (selectionRect?.left || 0) + ownerWindow.scrollX,\n width: selectionRect?.width,\n height: selectionRect?.height,\n visibility: \"hidden\",\n };\n }, [selectionRect]);\n\n const _disabledKeys =\n typeof disabledKeys === \"function\"\n ? disabledKeys(selectionRef.current)\n : disabledKeys;\n\n useEffect(\n function moveFocusToToolbarOnTab() {\n if (!state.isOpen) return;\n\n const ownerDocument = getOwnerDocument(divRef.current);\n\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === \"Tab\" && state.isOpen && toolbarRef.current) {\n const focusableEls = findFocusableElements(toolbarRef.current);\n\n const first = focusableEls[0] as HTMLElement;\n\n if (!first) return;\n\n const activeElement = getActiveElement(ownerDocument);\n\n const isCurrentlyFocusedElementOutsideToolbar = !nodeContains(\n toolbarRef.current,\n activeElement,\n );\n\n if (isCurrentlyFocusedElementOutsideToolbar) {\n e.preventDefault();\n first.focus();\n }\n }\n };\n\n ownerDocument.addEventListener(\"keydown\", handleKeyDown);\n\n return () => {\n ownerDocument.removeEventListener(\"keydown\", handleKeyDown);\n };\n },\n [state.isOpen],\n );\n\n return (\n <>\n {React.cloneElement(children, { ref: divRef })}\n\n <Portal>\n <div ref={mergeRefs(triggerRef, ref)} style={overlayPositionStyle} />\n </Portal>\n\n {state.isOpen && selectionRect ? (\n <div\n {...overlayProps}\n ref={overlayRef}\n data-block-id={dataBlockId}\n data-block-class={dataBlockClass}\n className=\"BaselineUI-InlineToolbar\"\n >\n <FocusScope contain={true} restoreFocus={true}>\n <div\n className={classNames(inlineToolbarContentCn, className)}\n {...mergeProps(toolbarProps, keyboardProps)}\n aria-keyshortcuts=\"Tab Shift+Tab ArrowRight ArrowLeft\"\n ref={toolbarRef}\n >\n {_items.map((item, i) => {\n const Icon = item.icon;\n\n return (\n <Fragment key={item.id}>\n <InlineToolbarButton\n onPress={() => {\n handleToolbarPress(item);\n }}\n aria-label={item[\"aria-label\"]}\n isDisabled={new Set(_disabledKeys).has(item.id)}\n >\n {item.label || (Icon && <Icon size={24} />)}\n </InlineToolbarButton>\n {i !== items.length - 1 && (\n <Separator\n orientation=\"vertical\"\n variant=\"secondary\"\n style={{\n backgroundColor: themeVars.color.border.strong,\n }}\n />\n )}\n </Fragment>\n );\n })}\n </div>\n </FocusScope>\n\n <div\n {...arrowProps}\n className={arrowInlineCn({ placement: _placement })}\n />\n </div>\n ) : null}\n </>\n );\n },\n);\n\nInlineToolbar.displayName = \"InlineToolbar\";\n",
@@ -0,0 +1,88 @@
1
+ # Baseline UI MCP Server Guidelines
2
+
3
+ This MCP server provides AI assistants with access to Baseline UI's component documentation, icons, and design resources.
4
+
5
+ - Baseline UI is a design system for building accessible and consistent user interfaces.
6
+ - It is built with React and TypeScript and styled with Vanilla Extract.
7
+ - It is also known as BUI.
8
+ - It is a client side design system that depends on global variables like document, window, etc.
9
+
10
+ ## Available Tools
11
+
12
+ ### Component Documentation
13
+
14
+ **`list_baseline_components`**
15
+
16
+ - Lists all available components in the Baseline UI design system
17
+ - Optional parameter: `includeDescription` (boolean) - includes component descriptions
18
+ - Use this when you need to discover what components are available
19
+
20
+ **`get_baseline_component_info`**
21
+
22
+ - Returns high-level component information: name, description, and available documentation sections
23
+ - Requires: `componentName` (string)
24
+ - Use this before diving into detailed documentation to understand what sections are available
25
+
26
+ **`get_baseline_component_docs`**
27
+
28
+ - Returns complete or section-specific component documentation in markdown format
29
+ - Requires: `componentName` (string)
30
+ - Optional: `sectionName` (string) - get a specific section (e.g., "Usage", "Examples")
31
+ - Use this to read component documentation, examples, and usage patterns
32
+
33
+ **`get_baseline_component_types`**
34
+
35
+ - Returns TypeScript type definitions and prop interfaces for a component
36
+ - Requires: `componentName` (string)
37
+ - Use this when you need to understand component props, their types, and constraints
38
+
39
+ **`get_baseline_component_source`**
40
+
41
+ - Returns the source code implementation of a component
42
+ - Requires: `componentName` (string)
43
+ - Use this to understand how a component is implemented
44
+
45
+ ### Icon System
46
+
47
+ **`search_baseline_icons`**
48
+
49
+ - Searches the Baseline UI icon set by name
50
+ - Requires: `terms` (string or array of strings)
51
+ - Returns icon names matching the search terms
52
+ - Import syntax: `import { IconName } from '@baseline-ui/icons/[size]'`
53
+
54
+ ### Getting Started
55
+
56
+ **`get_baseline_getting_started`**
57
+
58
+ - Returns complete setup and installation guide for Baseline UI
59
+ - Use this when helping users set up Baseline UI in a new project
60
+ - Always use this for setup-related questions or issues
61
+
62
+ ## Recommended Workflow
63
+
64
+ 1. **For component discovery**: Use `list_baseline_components` to see available components
65
+ 2. **For understanding a component**: Use `get_baseline_component_info` to see available sections
66
+ 3. **For detailed documentation**: Use `get_baseline_component_docs` to read full documentation or specific sections
67
+ 4. **For implementation details**: Use `get_baseline_component_types` and `get_baseline_component_source`
68
+ 5. **For icon selection**: Use `search_baseline_icons` with relevant terms
69
+ 6. **For setup**: Use `get_baseline_getting_started` for new projects or setup issues
70
+
71
+ ## Styling Guidelines
72
+
73
+ When recommending styling or customization approaches:
74
+
75
+ - **Always prioritize using sprinkles from `@baseline-ui/tokens`** for styling
76
+ - Sprinkles provides a type-safe, zero-runtime CSS utility layer built with Vanilla Extract
77
+ - Use sprinkles for spacing, colors, typography, shadows, and other design tokens
78
+ - Example: `<div className={sprinkles({ padding: 'md', color: 'text.primary' })}>Content</div>`
79
+ - Only use custom CSS when sprinkles doesn't provide the necessary utilities
80
+ - This ensures consistency with the design system and maintains design token governance
81
+
82
+ ## Tips for Better Results
83
+
84
+ - When presenting component information to users, format tool results as readable lists or tables, not raw JSON
85
+ - For component documentation, render markdown content with proper formatting and syntax highlighting
86
+ - When suggesting components, mention available sections that users can explore
87
+ - For props and types, highlight required vs optional properties and their constraints
88
+ - When searching icons, try multiple terms if the first search doesn't yield results (e.g., "search", "find", "magnifying")
package/dist/index.js CHANGED
@@ -164,6 +164,26 @@ async function startServer() {
164
164
  name: "baseline-ui-docs-server",
165
165
  version: packageJson.version,
166
166
  });
167
+ // Register guidelines resource
168
+ server.registerResource("guidelines", "resource://baseline-ui/guidelines.md", {
169
+ description: "Guidelines for using the Baseline UI MCP server",
170
+ mimeType: "text/markdown",
171
+ }, () => {
172
+ const guidelinesPath = path.resolve(__dirname, "guidelines.md");
173
+ if (!fs.existsSync(guidelinesPath)) {
174
+ throw new Error("Guidelines file not found");
175
+ }
176
+ const content = fs.readFileSync(guidelinesPath, "utf8");
177
+ return {
178
+ contents: [
179
+ {
180
+ uri: "resource://baseline-ui/guidelines.md",
181
+ mimeType: "text/markdown",
182
+ text: content,
183
+ },
184
+ ],
185
+ };
186
+ });
167
187
  // List components tool
168
188
  server.registerTool("list_baseline_components", {
169
189
  title: "List Baseline UI components",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@baseline-ui/mcp",
3
- "version": "0.0.0-nightly-20251125000500",
3
+ "version": "0.0.0-nightly-20251203000617",
4
4
  "description": "MCP server for Baseline UI design system documentation",
5
5
  "type": "module",
6
6
  "bin": "dist/index.js",