From 095a085fd433614a3f9508d55de6c0896ab677d4 Mon Sep 17 00:00:00 2001 From: yyh Date: Mon, 2 Mar 2026 13:36:12 +0800 Subject: [PATCH] feat(web): complete Phase 0 guardrails and add Base UI overlay primitives - Install @base-ui/react 1.2.0 - Define semantic z-index layer tokens (dropdown/popover/modal/toast/tooltip) - Add TooltipProvider to root layout with global timing config - Mark portal-to-follow-elem as deprecated with migration guide - Enforce no-restricted-imports as error with suppression baseline - Add ESLint rule to block new portal-to-follow-elem usage in business code - Scaffold Phase 1 semantic primitives: Tooltip, DropdownMenu, Popover, Dialog Part of #32767 --- .../base/portal-to-follow-elem/index.tsx | 16 + web/app/components/base/ui/dialog/index.tsx | 44 ++ .../base/ui/dropdown-menu/index.tsx | 100 ++++ web/app/components/base/ui/popover/index.tsx | 62 +++ web/app/components/base/ui/select/index.tsx | 112 +++++ web/app/components/base/ui/tooltip/index.tsx | 69 +++ web/app/layout.tsx | 5 +- web/eslint-suppressions.json | 447 +++++++++++++++++- web/eslint.config.mjs | 30 ++ web/package.json | 1 + web/pnpm-lock.yaml | 53 +++ web/tailwind-common-config.ts | 7 + 12 files changed, 930 insertions(+), 16 deletions(-) create mode 100644 web/app/components/base/ui/dialog/index.tsx create mode 100644 web/app/components/base/ui/dropdown-menu/index.tsx create mode 100644 web/app/components/base/ui/popover/index.tsx create mode 100644 web/app/components/base/ui/select/index.tsx create mode 100644 web/app/components/base/ui/tooltip/index.tsx diff --git a/web/app/components/base/portal-to-follow-elem/index.tsx b/web/app/components/base/portal-to-follow-elem/index.tsx index c57fba9dd0..7d4f6baa9b 100644 --- a/web/app/components/base/portal-to-follow-elem/index.tsx +++ b/web/app/components/base/portal-to-follow-elem/index.tsx @@ -1,4 +1,16 @@ 'use client' +/** + * @deprecated Use semantic overlay primitives from `@/app/components/base/ui/` instead. + * This component will be removed after migration is complete. + * See: https://github.com/langgenius/dify/issues/32767 + * + * Migration guide: + * - Tooltip → `@/app/components/base/ui/tooltip` + * - Menu/Dropdown → `@/app/components/base/ui/dropdown-menu` + * - Popover → `@/app/components/base/ui/popover` + * - Dialog/Modal → `@/app/components/base/ui/dialog` + * - Select → `@/app/components/base/ui/select` + */ import type { OffsetOptions, Placement } from '@floating-ui/react' import { autoUpdate, @@ -33,6 +45,7 @@ export type PortalToFollowElemOptions = { triggerPopupSameWidth?: boolean } +/** @deprecated Use semantic overlay primitives instead. See #32767. */ export function usePortalToFollowElem({ placement = 'bottom', open: controlledOpen, @@ -110,6 +123,7 @@ export function usePortalToFollowElemContext() { return context } +/** @deprecated Use semantic overlay primitives instead. See #32767. */ export function PortalToFollowElem({ children, ...options @@ -124,6 +138,7 @@ export function PortalToFollowElem({ ) } +/** @deprecated Use semantic overlay primitives instead. See #32767. */ export const PortalToFollowElemTrigger = ( { ref: propRef, @@ -164,6 +179,7 @@ export const PortalToFollowElemTrigger = ( } PortalToFollowElemTrigger.displayName = 'PortalToFollowElemTrigger' +/** @deprecated Use semantic overlay primitives instead. See #32767. */ export const PortalToFollowElemContent = ( { ref: propRef, diff --git a/web/app/components/base/ui/dialog/index.tsx b/web/app/components/base/ui/dialog/index.tsx new file mode 100644 index 0000000000..07d1e1f649 --- /dev/null +++ b/web/app/components/base/ui/dialog/index.tsx @@ -0,0 +1,44 @@ +'use client' + +import { Dialog as BaseDialog } from '@base-ui/react/dialog' +import * as React from 'react' +import { cn } from '@/utils/classnames' + +export const Dialog = BaseDialog.Root +export const DialogTrigger = BaseDialog.Trigger +export const DialogTitle = BaseDialog.Title +export const DialogDescription = BaseDialog.Description +export const DialogClose = BaseDialog.Close + +type DialogContentProps = { + children: React.ReactNode + className?: string + overlayClassName?: string +} + +export function DialogContent({ + children, + className, + overlayClassName, +}: DialogContentProps) { + return ( + + + + {children} + + + ) +} diff --git a/web/app/components/base/ui/dropdown-menu/index.tsx b/web/app/components/base/ui/dropdown-menu/index.tsx new file mode 100644 index 0000000000..57519f84fd --- /dev/null +++ b/web/app/components/base/ui/dropdown-menu/index.tsx @@ -0,0 +1,100 @@ +'use client' + +import type { Placement } from '@floating-ui/react' +import { Menu } from '@base-ui/react/menu' +import * as React from 'react' +import { cn } from '@/utils/classnames' + +function parsePlacement(placement: Placement) { + const [side, align] = placement.split('-') as [ + 'top' | 'bottom' | 'left' | 'right', + 'start' | 'center' | 'end' | undefined, + ] + return { side, align: align ?? 'center' as const } +} + +export const DropdownMenu = Menu.Root +export const DropdownMenuTrigger = Menu.Trigger +export const DropdownMenuGroup = Menu.Group +export const DropdownMenuGroupLabel = Menu.GroupLabel +export const DropdownMenuRadioGroup = Menu.RadioGroup +export const DropdownMenuRadioItem = Menu.RadioItem +export const DropdownMenuRadioItemIndicator = Menu.RadioItemIndicator +export const DropdownMenuCheckboxItem = Menu.CheckboxItem +export const DropdownMenuCheckboxItemIndicator = Menu.CheckboxItemIndicator + +type DropdownMenuContentProps = { + children: React.ReactNode + placement?: Placement + sideOffset?: number + alignOffset?: number + className?: string + popupClassName?: string +} + +export function DropdownMenuContent({ + children, + placement = 'bottom-end', + sideOffset = 4, + alignOffset = 0, + className, + popupClassName, +}: DropdownMenuContentProps) { + const { side, align } = parsePlacement(placement) + + return ( + + + + {children} + + + + ) +} + +type DropdownMenuItemProps = React.ComponentPropsWithoutRef & { + destructive?: boolean +} + +export function DropdownMenuItem({ + className, + destructive, + ...props +}: DropdownMenuItemProps) { + return ( + + ) +} + +export function DropdownMenuSeparator({ + className, + ...props +}: React.ComponentPropsWithoutRef) { + return ( + + ) +} diff --git a/web/app/components/base/ui/popover/index.tsx b/web/app/components/base/ui/popover/index.tsx new file mode 100644 index 0000000000..1569a63c7a --- /dev/null +++ b/web/app/components/base/ui/popover/index.tsx @@ -0,0 +1,62 @@ +'use client' + +import type { Placement } from '@floating-ui/react' +import { Popover as BasePopover } from '@base-ui/react/popover' +import * as React from 'react' +import { cn } from '@/utils/classnames' + +function parsePlacement(placement: Placement) { + const [side, align] = placement.split('-') as [ + 'top' | 'bottom' | 'left' | 'right', + 'start' | 'center' | 'end' | undefined, + ] + return { side, align: align ?? 'center' as const } +} + +export const Popover = BasePopover.Root +export const PopoverTrigger = BasePopover.Trigger +export const PopoverClose = BasePopover.Close +export const PopoverTitle = BasePopover.Title +export const PopoverDescription = BasePopover.Description + +type PopoverContentProps = { + children: React.ReactNode + placement?: Placement + sideOffset?: number + alignOffset?: number + className?: string + popupClassName?: string +} + +export function PopoverContent({ + children, + placement = 'bottom', + sideOffset = 8, + alignOffset = 0, + className, + popupClassName, +}: PopoverContentProps) { + const { side, align } = parsePlacement(placement) + + return ( + + + + {children} + + + + ) +} diff --git a/web/app/components/base/ui/select/index.tsx b/web/app/components/base/ui/select/index.tsx new file mode 100644 index 0000000000..f240e5729b --- /dev/null +++ b/web/app/components/base/ui/select/index.tsx @@ -0,0 +1,112 @@ +'use client' + +import type { Placement } from '@floating-ui/react' +import { Select as BaseSelect } from '@base-ui/react/select' +import * as React from 'react' +import { cn } from '@/utils/classnames' + +function parsePlacement(placement: Placement) { + const [side, align] = placement.split('-') as [ + 'top' | 'bottom' | 'left' | 'right', + 'start' | 'center' | 'end' | undefined, + ] + return { side, align: align ?? 'center' as const } +} + +export const Select = BaseSelect.Root +export const SelectValue = BaseSelect.Value +export const SelectGroup = BaseSelect.Group +export const SelectGroupLabel = BaseSelect.GroupLabel +export const SelectSeparator = BaseSelect.Separator + +export function SelectTrigger({ + className, + children, + ...props +}: React.ComponentPropsWithoutRef) { + return ( + + {children} + + + + + ) +} + +type SelectContentProps = { + children: React.ReactNode + placement?: Placement + sideOffset?: number + alignOffset?: number + className?: string + popupClassName?: string + listClassName?: string +} + +export function SelectContent({ + children, + placement = 'bottom-start', + sideOffset = 4, + alignOffset = 0, + className, + popupClassName, + listClassName, +}: SelectContentProps) { + const { side, align } = parsePlacement(placement) + + return ( + + + + + {children} + + + + + ) +} + +export function SelectItem({ + className, + children, + ...props +}: React.ComponentPropsWithoutRef) { + return ( + + + {children} + + + + + + ) +} diff --git a/web/app/components/base/ui/tooltip/index.tsx b/web/app/components/base/ui/tooltip/index.tsx new file mode 100644 index 0000000000..ff277892a5 --- /dev/null +++ b/web/app/components/base/ui/tooltip/index.tsx @@ -0,0 +1,69 @@ +'use client' + +import type { Placement } from '@floating-ui/react' +import { Tooltip as BaseTooltip } from '@base-ui/react/tooltip' +import * as React from 'react' +import { cn } from '@/utils/classnames' + +function parsePlacement(placement: Placement) { + const [side, align] = placement.split('-') as [ + 'top' | 'bottom' | 'left' | 'right', + 'start' | 'center' | 'end' | undefined, + ] + return { side, align: align ?? 'center' as const } +} + +export type TooltipProps = { + position?: Placement + disabled?: boolean + popupContent?: React.ReactNode + children?: React.ReactNode + popupClassName?: string + noDecoration?: boolean + offset?: number +} + +const Tooltip = React.memo(({ + position = 'top', + disabled = false, + popupContent, + children, + popupClassName, + noDecoration, + offset = 8, +}: TooltipProps) => { + const { side, align } = parsePlacement(position) + + if (!popupContent || disabled) + return <>{children} + + return ( + + {React.isValidElement(children) + ? + : }>{children}} + + + + {popupContent} + + + + + ) +}) + +Tooltip.displayName = 'Tooltip' + +export const TooltipProvider = BaseTooltip.Provider +export default Tooltip diff --git a/web/app/layout.tsx b/web/app/layout.tsx index 64f0e5ac3b..367aa120b8 100644 --- a/web/app/layout.tsx +++ b/web/app/layout.tsx @@ -9,6 +9,7 @@ import { getDatasetMap } from '@/env' import { getLocaleOnServer } from '@/i18n-config/server' import { cn } from '@/utils/classnames' import { ToastProvider } from './components/base/toast' +import { TooltipProvider } from './components/base/ui/tooltip' import BrowserInitializer from './components/browser-initializer' import { ReactScanLoader } from './components/devtools/react-scan/loader' import { I18nServerProvider } from './components/provider/i18n-server' @@ -78,7 +79,9 @@ const LocaleLayout = async ({ - {children} + + {children} + diff --git a/web/eslint-suppressions.json b/web/eslint-suppressions.json index 3282630fef..1c94fedf07 100644 --- a/web/eslint-suppressions.json +++ b/web/eslint-suppressions.json @@ -102,6 +102,11 @@ "count": 3 } }, + "app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/config-button.tsx": { + "no-restricted-imports": { + "count": 2 + } + }, "app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/config-popup.tsx": { "tailwindcss/enforce-consistent-class-order": { "count": 6 @@ -113,6 +118,9 @@ } }, "app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/tracing/provider-config-modal.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 1 } @@ -286,6 +294,9 @@ } }, "app/components/app-sidebar/app-operations.tsx": { + "no-restricted-imports": { + "count": 1 + }, "react-hooks-extra/no-direct-set-state-in-use-effect": { "count": 4 }, @@ -294,6 +305,9 @@ } }, "app/components/app-sidebar/app-sidebar-dropdown.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 2 } @@ -304,6 +318,9 @@ } }, "app/components/app-sidebar/dataset-info/dropdown.tsx": { + "no-restricted-imports": { + "count": 1 + }, "ts/no-explicit-any": { "count": 1 } @@ -319,6 +336,9 @@ } }, "app/components/app-sidebar/dataset-sidebar-dropdown.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 3 } @@ -480,6 +500,9 @@ } }, "app/components/app/app-access-control/add-member-or-group-pop.tsx": { + "no-restricted-imports": { + "count": 1 + }, "tailwindcss/enforce-consistent-class-order": { "count": 8 } @@ -500,10 +523,18 @@ } }, "app/components/app/app-publisher/index.tsx": { + "no-restricted-imports": { + "count": 2 + }, "ts/no-explicit-any": { "count": 5 } }, + "app/components/app/app-publisher/publish-with-multiple-model.tsx": { + "no-restricted-imports": { + "count": 2 + } + }, "app/components/app/app-publisher/suggested-action.tsx": { "tailwindcss/enforce-consistent-class-order": { "count": 1 @@ -565,6 +596,11 @@ "count": 6 } }, + "app/components/app/configuration/config-var/config-modal/type-select.tsx": { + "no-restricted-imports": { + "count": 2 + } + }, "app/components/app/configuration/config-var/config-select/index.tsx": { "tailwindcss/enforce-consistent-class-order": { "count": 1 @@ -587,6 +623,9 @@ } }, "app/components/app/configuration/config-var/select-var-type.tsx": { + "no-restricted-imports": { + "count": 2 + }, "ts/no-explicit-any": { "count": 1 } @@ -617,6 +656,11 @@ "count": 1 } }, + "app/components/app/configuration/config-vision/param-config.tsx": { + "no-restricted-imports": { + "count": 2 + } + }, "app/components/app/configuration/config/agent-setting-button.spec.tsx": { "ts/no-explicit-any": { "count": 2 @@ -671,6 +715,9 @@ } }, "app/components/app/configuration/config/assistant-type-picker/index.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/no-unnecessary-whitespace": { "count": 3 }, @@ -716,6 +763,9 @@ } }, "app/components/app/configuration/config/automatic/version-selector.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 3 }, @@ -778,6 +828,9 @@ } }, "app/components/app/configuration/dataset-config/context-var/var-picker.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/no-unnecessary-whitespace": { "count": 4 } @@ -1039,6 +1092,9 @@ } }, "app/components/app/log/model-info.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 3 }, @@ -1159,6 +1215,9 @@ } }, "app/components/app/type-selector/index.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 3 } @@ -1856,11 +1915,6 @@ "count": 4 } }, - "app/components/base/file-uploader/utils.spec.ts": { - "ts/no-explicit-any": { - "count": 2 - } - }, "app/components/base/file-uploader/utils.ts": { "ts/no-explicit-any": { "count": 3 @@ -2033,11 +2087,6 @@ "count": 1 } }, - "app/components/base/input/index.spec.tsx": { - "ts/no-explicit-any": { - "count": 1 - } - }, "app/components/base/input/index.stories.tsx": { "no-console": { "count": 2 @@ -2618,11 +2667,6 @@ "count": 4 } }, - "app/components/base/with-input-validation/index.spec.tsx": { - "ts/no-explicit-any": { - "count": 2 - } - }, "app/components/base/with-input-validation/index.stories.tsx": { "no-console": { "count": 1 @@ -2755,11 +2799,17 @@ } }, "app/components/datasets/common/document-picker/index.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 1 } }, "app/components/datasets/common/document-picker/preview-document-picker.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 2 } @@ -3075,6 +3125,11 @@ "count": 1 } }, + "app/components/datasets/documents/create-from-pipeline/data-source/base/credential-selector/index.tsx": { + "no-restricted-imports": { + "count": 2 + } + }, "app/components/datasets/documents/create-from-pipeline/data-source/base/credential-selector/item.tsx": { "tailwindcss/enforce-consistent-class-order": { "count": 1 @@ -3121,6 +3176,9 @@ } }, "app/components/datasets/documents/create-from-pipeline/data-source/online-drive/file-list/header/breadcrumbs/dropdown/index.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 1 } @@ -3450,6 +3508,9 @@ } }, "app/components/datasets/external-api/external-api-modal/index.tsx": { + "no-restricted-imports": { + "count": 2 + }, "react-hooks-extra/no-direct-set-state-in-use-effect": { "count": 1 }, @@ -3509,6 +3570,9 @@ } }, "app/components/datasets/extra-info/api-access/index.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 1 } @@ -3519,6 +3583,9 @@ } }, "app/components/datasets/extra-info/service-api/index.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 1 } @@ -3655,6 +3722,9 @@ } }, "app/components/datasets/metadata/metadata-dataset/create-metadata-modal.tsx": { + "no-restricted-imports": { + "count": 1 + }, "ts/no-explicit-any": { "count": 1 } @@ -3672,6 +3742,11 @@ "count": 1 } }, + "app/components/datasets/metadata/metadata-dataset/select-metadata-modal.tsx": { + "no-restricted-imports": { + "count": 1 + } + }, "app/components/datasets/metadata/metadata-dataset/select-metadata.tsx": { "tailwindcss/enforce-consistent-class-order": { "count": 3 @@ -3718,6 +3793,11 @@ "count": 7 } }, + "app/components/datasets/settings/index-method/index.tsx": { + "no-restricted-imports": { + "count": 2 + } + }, "app/components/datasets/settings/index-method/keyword-number.tsx": { "tailwindcss/enforce-consistent-class-order": { "count": 1 @@ -3729,6 +3809,9 @@ } }, "app/components/datasets/settings/permission-selector/index.tsx": { + "no-restricted-imports": { + "count": 2 + }, "react/no-missing-key": { "count": 1 }, @@ -3802,6 +3885,9 @@ } }, "app/components/explore/item-operation/index.tsx": { + "no-restricted-imports": { + "count": 2 + }, "react-hooks-extra/no-direct-set-state-in-use-effect": { "count": 1 } @@ -3903,6 +3989,11 @@ "count": 2 } }, + "app/components/header/account-setting/api-based-extension-page/selector.tsx": { + "no-restricted-imports": { + "count": 2 + } + }, "app/components/header/account-setting/data-source-page-new/card.tsx": { "tailwindcss/enforce-consistent-class-order": { "count": 4 @@ -3912,6 +4003,9 @@ } }, "app/components/header/account-setting/data-source-page-new/configure.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 1 } @@ -3953,16 +4047,25 @@ } }, "app/components/header/account-setting/data-source-page/data-source-website/config-firecrawl-modal.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 1 } }, "app/components/header/account-setting/data-source-page/data-source-website/config-jina-reader-modal.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 1 } }, "app/components/header/account-setting/data-source-page/data-source-website/config-watercrawl-modal.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 1 } @@ -4000,7 +4103,15 @@ "count": 3 } }, + "app/components/header/account-setting/members-page/invite-modal/role-selector.tsx": { + "no-restricted-imports": { + "count": 2 + } + }, "app/components/header/account-setting/members-page/operation/index.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 5 } @@ -4016,6 +4127,9 @@ } }, "app/components/header/account-setting/members-page/transfer-ownership-modal/member-selector.tsx": { + "no-restricted-imports": { + "count": 2 + }, "ts/no-explicit-any": { "count": 2 } @@ -4052,6 +4166,9 @@ } }, "app/components/header/account-setting/model-provider-page/model-auth/add-custom-model.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 2 } @@ -4067,6 +4184,9 @@ } }, "app/components/header/account-setting/model-provider-page/model-auth/authorized/index.tsx": { + "no-restricted-imports": { + "count": 4 + }, "tailwindcss/enforce-consistent-class-order": { "count": 2 }, @@ -4080,6 +4200,9 @@ } }, "app/components/header/account-setting/model-provider-page/model-auth/credential-selector.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 4 } @@ -4118,6 +4241,9 @@ } }, "app/components/header/account-setting/model-provider-page/model-modal/index.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 6 }, @@ -4144,6 +4270,9 @@ } }, "app/components/header/account-setting/model-provider-page/model-parameter-modal/index.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 3 } @@ -4179,6 +4308,11 @@ "count": 1 } }, + "app/components/header/account-setting/model-provider-page/model-selector/index.tsx": { + "no-restricted-imports": { + "count": 2 + } + }, "app/components/header/account-setting/model-provider-page/model-selector/popup-item.tsx": { "tailwindcss/enforce-consistent-class-order": { "count": 3 @@ -4243,6 +4377,11 @@ "count": 1 } }, + "app/components/header/account-setting/model-provider-page/system-model-selector/index.tsx": { + "no-restricted-imports": { + "count": 2 + } + }, "app/components/header/account-setting/plugin-page/utils.ts": { "ts/no-explicit-any": { "count": 4 @@ -4450,6 +4589,9 @@ } }, "app/components/plugins/marketplace/search-box/tags-filter.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 1 } @@ -4465,6 +4607,9 @@ } }, "app/components/plugins/marketplace/sort-dropdown/index.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 3 } @@ -4498,6 +4643,9 @@ } }, "app/components/plugins/plugin-auth/authorized/index.tsx": { + "no-restricted-imports": { + "count": 4 + }, "tailwindcss/enforce-consistent-class-order": { "count": 2 }, @@ -4565,6 +4713,9 @@ } }, "app/components/plugins/plugin-detail-panel/app-selector/app-picker.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 2 } @@ -4575,6 +4726,9 @@ } }, "app/components/plugins/plugin-detail-panel/app-selector/index.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 1 } @@ -4625,6 +4779,9 @@ } }, "app/components/plugins/plugin-detail-panel/model-selector/index.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 1 }, @@ -4654,6 +4811,9 @@ } }, "app/components/plugins/plugin-detail-panel/operation-dropdown.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 4 } @@ -4713,6 +4873,9 @@ } }, "app/components/plugins/plugin-detail-panel/subscription-list/selector-entry.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 1 } @@ -4753,6 +4916,9 @@ } }, "app/components/plugins/plugin-detail-panel/tool-selector/index.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 1 } @@ -4805,16 +4971,25 @@ } }, "app/components/plugins/plugin-page/filter-management/category-filter.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 3 } }, "app/components/plugins/plugin-page/filter-management/tag-filter.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 3 } }, "app/components/plugins/plugin-page/install-plugin-dropdown.tsx": { + "no-restricted-imports": { + "count": 2 + }, "react-hooks-extra/no-direct-set-state-in-use-effect": { "count": 2 }, @@ -4827,6 +5002,11 @@ "count": 2 } }, + "app/components/plugins/plugin-page/plugin-tasks/index.tsx": { + "no-restricted-imports": { + "count": 2 + } + }, "app/components/plugins/provider-card.tsx": { "tailwindcss/enforce-consistent-class-order": { "count": 2 @@ -4858,6 +5038,9 @@ } }, "app/components/plugins/reference-setting-modal/auto-update-setting/strategy-picker.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 2 } @@ -4870,6 +5053,11 @@ "count": 1 } }, + "app/components/plugins/reference-setting-modal/auto-update-setting/tool-picker.tsx": { + "no-restricted-imports": { + "count": 2 + } + }, "app/components/plugins/reference-setting-modal/index.tsx": { "tailwindcss/enforce-consistent-class-order": { "count": 1 @@ -4896,6 +5084,9 @@ } }, "app/components/plugins/update-plugin/plugin-version-picker.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 3 } @@ -5054,6 +5245,11 @@ "count": 1 } }, + "app/components/rag-pipeline/components/rag-pipeline-header/publisher/index.tsx": { + "no-restricted-imports": { + "count": 2 + } + }, "app/components/rag-pipeline/components/rag-pipeline-header/publisher/popup.tsx": { "tailwindcss/enforce-consistent-class-order": { "count": 8 @@ -5139,6 +5335,9 @@ } }, "app/components/share/text-generation/menu-dropdown.tsx": { + "no-restricted-imports": { + "count": 2 + }, "react-hooks-extra/no-direct-set-state-in-use-effect": { "count": 1 }, @@ -5238,11 +5437,17 @@ } }, "app/components/tools/labels/filter.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/no-unnecessary-whitespace": { "count": 1 } }, "app/components/tools/labels/selector.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/no-unnecessary-whitespace": { "count": 1 } @@ -5269,6 +5474,9 @@ } }, "app/components/tools/mcp/detail/operation-dropdown.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 2 } @@ -5382,6 +5590,9 @@ } }, "app/components/tools/workflow-tool/method-selector.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/no-unnecessary-whitespace": { "count": 1 } @@ -5510,7 +5721,15 @@ "count": 1 } }, + "app/components/workflow/block-selector/main.tsx": { + "no-restricted-imports": { + "count": 2 + } + }, "app/components/workflow/block-selector/market-place-plugin/action.tsx": { + "no-restricted-imports": { + "count": 2 + }, "react-hooks-extra/no-direct-set-state-in-use-effect": { "count": 1 }, @@ -5554,6 +5773,11 @@ "count": 1 } }, + "app/components/workflow/block-selector/tool-picker.tsx": { + "no-restricted-imports": { + "count": 2 + } + }, "app/components/workflow/block-selector/tool/action-item.tsx": { "tailwindcss/enforce-consistent-class-order": { "count": 2 @@ -5624,6 +5848,11 @@ "count": 6 } }, + "app/components/workflow/header/checklist.tsx": { + "no-restricted-imports": { + "count": 2 + } + }, "app/components/workflow/header/editing-title.tsx": { "tailwindcss/enforce-consistent-class-order": { "count": 1 @@ -5656,6 +5885,9 @@ } }, "app/components/workflow/header/test-run-menu.tsx": { + "no-restricted-imports": { + "count": 2 + }, "react-refresh/only-export-components": { "count": 1 }, @@ -5673,7 +5905,15 @@ "count": 1 } }, + "app/components/workflow/header/view-history.tsx": { + "no-restricted-imports": { + "count": 2 + } + }, "app/components/workflow/header/view-workflow-history.tsx": { + "no-restricted-imports": { + "count": 2 + }, "ts/no-explicit-any": { "count": 1 } @@ -5767,6 +6007,9 @@ } }, "app/components/workflow/nodes/_base/components/agent-strategy-selector.tsx": { + "no-restricted-imports": { + "count": 2 + }, "ts/no-explicit-any": { "count": 4 } @@ -5862,6 +6105,9 @@ } }, "app/components/workflow/nodes/_base/components/error-handle/error-handle-type-selector.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 2 } @@ -5979,6 +6225,9 @@ } }, "app/components/workflow/nodes/_base/components/next-step/operator.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 1 } @@ -6004,6 +6253,11 @@ "count": 3 } }, + "app/components/workflow/nodes/_base/components/panel-operator/index.tsx": { + "no-restricted-imports": { + "count": 2 + } + }, "app/components/workflow/nodes/_base/components/prompt/editor.tsx": { "tailwindcss/no-unnecessary-whitespace": { "count": 2 @@ -6087,6 +6341,9 @@ } }, "app/components/workflow/nodes/_base/components/variable/var-reference-picker.tsx": { + "no-restricted-imports": { + "count": 2 + }, "react-hooks-extra/no-direct-set-state-in-use-effect": { "count": 2 }, @@ -6106,6 +6363,9 @@ } }, "app/components/workflow/nodes/_base/components/variable/var-reference-vars.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 6 }, @@ -6113,6 +6373,11 @@ "count": 3 } }, + "app/components/workflow/nodes/_base/components/variable/var-type-picker.tsx": { + "no-restricted-imports": { + "count": 2 + } + }, "app/components/workflow/nodes/_base/components/variable/variable-label/base/variable-label.tsx": { "tailwindcss/enforce-consistent-class-order": { "count": 2 @@ -6248,6 +6513,9 @@ } }, "app/components/workflow/nodes/assigner/components/operation-selector.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 3 } @@ -6292,6 +6560,11 @@ "count": 1 } }, + "app/components/workflow/nodes/code/dependency-picker.tsx": { + "no-restricted-imports": { + "count": 2 + } + }, "app/components/workflow/nodes/code/use-config.ts": { "react-hooks-extra/no-direct-set-state-in-use-effect": { "count": 2 @@ -6433,6 +6706,9 @@ } }, "app/components/workflow/nodes/human-input/components/button-style-dropdown.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 1 } @@ -6453,11 +6729,17 @@ } }, "app/components/workflow/nodes/human-input/components/delivery-method/method-selector.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 14 } }, "app/components/workflow/nodes/human-input/components/delivery-method/recipient/email-input.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 1 } @@ -6477,6 +6759,11 @@ "count": 6 } }, + "app/components/workflow/nodes/human-input/components/delivery-method/recipient/member-selector.tsx": { + "no-restricted-imports": { + "count": 2 + } + }, "app/components/workflow/nodes/human-input/components/delivery-method/test-email-sender.tsx": { "tailwindcss/enforce-consistent-class-order": { "count": 21 @@ -6543,6 +6830,11 @@ "count": 1 } }, + "app/components/workflow/nodes/if-else/components/condition-add.tsx": { + "no-restricted-imports": { + "count": 2 + } + }, "app/components/workflow/nodes/if-else/components/condition-files-list-value.tsx": { "tailwindcss/enforce-consistent-class-order": { "count": 3 @@ -6561,7 +6853,20 @@ "count": 2 } }, + "app/components/workflow/nodes/if-else/components/condition-list/condition-operator.tsx": { + "no-restricted-imports": { + "count": 2 + } + }, + "app/components/workflow/nodes/if-else/components/condition-list/condition-var-selector.tsx": { + "no-restricted-imports": { + "count": 2 + } + }, "app/components/workflow/nodes/if-else/components/condition-number-input.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 1 }, @@ -6618,6 +6923,9 @@ } }, "app/components/workflow/nodes/knowledge-base/components/chunk-structure/selector.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 1 } @@ -6666,11 +6974,17 @@ } }, "app/components/workflow/nodes/knowledge-retrieval/components/metadata/add-condition.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 2 } }, "app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-common-variable-selector.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 4 } @@ -6688,7 +7002,20 @@ "count": 1 } }, + "app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-operator.tsx": { + "no-restricted-imports": { + "count": 2 + } + }, + "app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-value-method.tsx": { + "no-restricted-imports": { + "count": 2 + } + }, "app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-variable-selector.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 2 } @@ -6699,6 +7026,9 @@ } }, "app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-filter/metadata-filter-selector.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 2 } @@ -6709,11 +7039,17 @@ } }, "app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-trigger.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 1 } }, "app/components/workflow/nodes/knowledge-retrieval/components/retrieval-config.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/no-unnecessary-whitespace": { "count": 2 } @@ -6801,6 +7137,9 @@ } }, "app/components/workflow/nodes/llm/components/json-schema-config-modal/json-importer.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 2 }, @@ -6817,6 +7156,9 @@ } }, "app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-generator/index.tsx": { + "no-restricted-imports": { + "count": 2 + }, "react-hooks-extra/no-direct-set-state-in-use-effect": { "count": 2 } @@ -6860,6 +7202,9 @@ } }, "app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/type-selector.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 2 } @@ -6910,6 +7255,11 @@ "count": 1 } }, + "app/components/workflow/nodes/loop/components/condition-add.tsx": { + "no-restricted-imports": { + "count": 2 + } + }, "app/components/workflow/nodes/loop/components/condition-files-list-value.tsx": { "tailwindcss/enforce-consistent-class-order": { "count": 3 @@ -6928,7 +7278,20 @@ "count": 2 } }, + "app/components/workflow/nodes/loop/components/condition-list/condition-operator.tsx": { + "no-restricted-imports": { + "count": 2 + } + }, + "app/components/workflow/nodes/loop/components/condition-list/condition-var-selector.tsx": { + "no-restricted-imports": { + "count": 2 + } + }, "app/components/workflow/nodes/loop/components/condition-number-input.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 1 }, @@ -7233,6 +7596,11 @@ "count": 1 } }, + "app/components/workflow/nodes/variable-assigner/components/add-variable/index.tsx": { + "no-restricted-imports": { + "count": 2 + } + }, "app/components/workflow/nodes/variable-assigner/components/node-variable-item.tsx": { "tailwindcss/enforce-consistent-class-order": { "count": 1 @@ -7268,10 +7636,23 @@ } }, "app/components/workflow/note-node/note-editor/toolbar/color-picker.tsx": { + "no-restricted-imports": { + "count": 2 + }, "react-refresh/only-export-components": { "count": 1 } }, + "app/components/workflow/note-node/note-editor/toolbar/font-size-selector.tsx": { + "no-restricted-imports": { + "count": 2 + } + }, + "app/components/workflow/note-node/note-editor/toolbar/operator.tsx": { + "no-restricted-imports": { + "count": 2 + } + }, "app/components/workflow/note-node/note-editor/utils.ts": { "regexp/no-useless-quantifier": { "count": 1 @@ -7288,6 +7669,9 @@ } }, "app/components/workflow/operator/more-actions.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 6 } @@ -7298,6 +7682,9 @@ } }, "app/components/workflow/operator/zoom-in-out.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 2 } @@ -7352,6 +7739,11 @@ "count": 6 } }, + "app/components/workflow/panel/chat-variable-panel/components/variable-modal-trigger.tsx": { + "no-restricted-imports": { + "count": 2 + } + }, "app/components/workflow/panel/chat-variable-panel/components/variable-modal.tsx": { "react-hooks-extra/no-direct-set-state-in-use-effect": { "count": 8 @@ -7364,6 +7756,9 @@ } }, "app/components/workflow/panel/chat-variable-panel/components/variable-type-select.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 6 }, @@ -7420,6 +7815,11 @@ "count": 1 } }, + "app/components/workflow/panel/env-panel/variable-trigger.tsx": { + "no-restricted-imports": { + "count": 2 + } + }, "app/components/workflow/panel/global-variable-panel/index.tsx": { "tailwindcss/enforce-consistent-class-order": { "count": 2 @@ -7445,6 +7845,11 @@ "count": 1 } }, + "app/components/workflow/panel/version-history-panel/context-menu/index.tsx": { + "no-restricted-imports": { + "count": 2 + } + }, "app/components/workflow/panel/version-history-panel/context-menu/menu-item.tsx": { "tailwindcss/enforce-consistent-class-order": { "count": 1 @@ -7477,6 +7882,9 @@ } }, "app/components/workflow/panel/version-history-panel/filter/index.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/no-unnecessary-whitespace": { "count": 1 } @@ -7523,6 +7931,9 @@ } }, "app/components/workflow/run/agent-log/agent-log-nav-more.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 1 } @@ -7887,6 +8298,9 @@ } }, "app/components/workflow/workflow-preview/components/zoom-in-out.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 2 } @@ -7915,6 +8329,9 @@ } }, "app/education-apply/search-input.tsx": { + "no-restricted-imports": { + "count": 2 + }, "tailwindcss/enforce-consistent-class-order": { "count": 1 }, diff --git a/web/eslint.config.mjs b/web/eslint.config.mjs index cf7825fc61..4372546f3c 100644 --- a/web/eslint.config.mjs +++ b/web/eslint.config.mjs @@ -145,4 +145,34 @@ export default antfu( 'hyoban/no-dependency-version-prefix': 'error', }, }, + { + name: 'dify/base-ui-primitives', + files: ['app/components/base/ui/**/*.ts', 'app/components/base/ui/**/*.tsx'], + rules: { + 'react-refresh/only-export-components': 'off', + }, + }, + { + name: 'dify/overlay-migration', + files: [GLOB_TS, GLOB_TSX], + ignores: [ + 'app/components/base/**', + ...GLOB_TESTS, + ], + rules: { + 'no-restricted-imports': ['error', { + paths: [{ + name: '@/app/components/base/portal-to-follow-elem', + message: 'Deprecated: use semantic overlay primitives from @/app/components/base/ui/ instead. See issue #32767.', + }], + patterns: [{ + group: [ + '**/portal-to-follow-elem', + '**/portal-to-follow-elem/index', + ], + message: 'Deprecated: use semantic overlay primitives from @/app/components/base/ui/ instead. See issue #32767.', + }], + }], + }, + }, ) diff --git a/web/package.json b/web/package.json index f0d4fdd1fc..9f7c61f40e 100644 --- a/web/package.json +++ b/web/package.json @@ -63,6 +63,7 @@ "dependencies": { "@amplitude/analytics-browser": "2.33.1", "@amplitude/plugin-session-replay-browser": "1.23.6", + "@base-ui/react": "1.2.0", "@emoji-mart/data": "1.2.1", "@floating-ui/react": "0.26.28", "@formatjs/intl-localematcher": "0.5.10", diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index a754e0aa7a..4f72ba60c5 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -60,6 +60,9 @@ importers: '@amplitude/plugin-session-replay-browser': specifier: 1.23.6 version: 1.23.6(@amplitude/rrweb@2.0.0-alpha.35)(rollup@4.56.0) + '@base-ui/react': + specifier: 1.2.0 + version: 1.2.0(@types/react@19.2.9)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@emoji-mart/data': specifier: 1.2.1 version: 1.2.1 @@ -900,6 +903,27 @@ packages: resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} engines: {node: '>=6.9.0'} + '@base-ui/react@1.2.0': + resolution: {integrity: sha512-O6aEQHcm+QyGTFY28xuwRD3SEJGZOBDpyjN2WvpfWYFVhg+3zfXPysAILqtM0C1kWC82MccOE/v1j+GHXE4qIw==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@types/react': ^17 || ^18 || ^19 + react: ^17 || ^18 || ^19 + react-dom: ^17 || ^18 || ^19 + peerDependenciesMeta: + '@types/react': + optional: true + + '@base-ui/utils@0.2.5': + resolution: {integrity: sha512-oYC7w0gp76RI5MxprlGLV0wze0SErZaRl3AAkeP3OnNB/UBMb6RqNf6ZSIlxOc9Qp68Ab3C2VOcJQyRs7Xc7Vw==} + peerDependencies: + '@types/react': ^17 || ^18 || ^19 + react: ^17 || ^18 || ^19 + react-dom: ^17 || ^18 || ^19 + peerDependenciesMeta: + '@types/react': + optional: true + '@bcoe/v8-coverage@1.0.2': resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} engines: {node: '>=18'} @@ -6838,6 +6862,9 @@ packages: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} + reselect@5.1.1: + resolution: {integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==} + reserved-identifiers@1.2.0: resolution: {integrity: sha512-yE7KUfFvaBFzGPs5H3Ops1RevfUEsDc5Iz65rOwWg4lE8HJSYtle77uul3+573457oHvBKuHYDl/xqUkKpEEdw==} engines: {node: '>=18'} @@ -8336,6 +8363,30 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 + '@base-ui/react@1.2.0(@types/react@19.2.9)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@babel/runtime': 7.28.6 + '@base-ui/utils': 0.2.5(@types/react@19.2.9)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@floating-ui/react-dom': 2.1.6(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + '@floating-ui/utils': 0.2.10 + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + tabbable: 6.4.0 + use-sync-external-store: 1.6.0(react@19.2.4) + optionalDependencies: + '@types/react': 19.2.9 + + '@base-ui/utils@0.2.5(@types/react@19.2.9)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': + dependencies: + '@babel/runtime': 7.28.6 + '@floating-ui/utils': 0.2.10 + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + reselect: 5.1.1 + use-sync-external-store: 1.6.0(react@19.2.4) + optionalDependencies: + '@types/react': 19.2.9 + '@bcoe/v8-coverage@1.0.2': {} '@braintree/sanitize-url@7.1.1': {} @@ -15158,6 +15209,8 @@ snapshots: require-from-string@2.0.2: {} + reselect@5.1.1: {} + reserved-identifiers@1.2.0: {} resize-observer-polyfill@1.5.1: {} diff --git a/web/tailwind-common-config.ts b/web/tailwind-common-config.ts index cbd58e2809..e6976c0960 100644 --- a/web/tailwind-common-config.ts +++ b/web/tailwind-common-config.ts @@ -161,6 +161,13 @@ const config = { 'progress-bar-indeterminate-stripe': 'var(--color-progress-bar-indeterminate-stripe)', 'chat-answer-human-input-form-divider-bg': 'var(--color-chat-answer-human-input-form-divider-bg)', }, + zIndex: { + dropdown: '1000', + popover: '1100', + modal: '1200', + toast: '1300', + tooltip: '1400', + }, animation: { 'spin-slow': 'spin 2s linear infinite', },