mirror of
https://github.com/langgenius/dify.git
synced 2026-10-04 13:47:55 +08:00
refactor(ui): expose compound Infotip primitives and migrate consumers (#42681)
This commit is contained in:
94 files changed
+1329
-787
No files matched your search
@@ -44,7 +44,7 @@ Import `styles.css` once from the consumer's root stylesheet or entrypoint.
|
||||
| Layout | `./scroll-area`, `./separator` |
|
||||
| Media | `./avatar` |
|
||||
| Navigation | `./breadcrumb`, `./file-tree`, `./pagination`, `./tabs` |
|
||||
| Overlay and menu | `./alert-dialog`, `./context-menu`, `./dialog`, `./drawer`, `./dropdown-menu`, `./popover`, `./preview-card`, `./tooltip` |
|
||||
| Overlay and menu | `./alert-dialog`, `./context-menu`, `./dialog`, `./drawer`, `./dropdown-menu`, `./infotip`, `./popover`, `./preview-card`, `./tooltip` |
|
||||
| Search and pick | `./autocomplete`, `./combobox`, `./select` |
|
||||
|
||||
Utilities:
|
||||
|
||||
@@ -172,6 +172,10 @@
|
||||
"./separator": {
|
||||
"types": "./src/separator/index.tsx",
|
||||
"import": "./src/separator/index.tsx"
|
||||
},
|
||||
"./infotip": {
|
||||
"types": "./src/infotip/index.tsx",
|
||||
"import": "./src/infotip/index.tsx"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import * as React from 'react'
|
||||
import { createPortal } from 'react-dom'
|
||||
import { render } from 'vitest-browser-react'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '..'
|
||||
|
||||
describe('Infotip composition', () => {
|
||||
it('preserves caller handlers and prevents an enclosing action from firing', async () => {
|
||||
const parentClick = vi.fn()
|
||||
const triggerClick = vi.fn()
|
||||
const triggerRef = React.createRef<HTMLButtonElement>()
|
||||
const screen = await render(
|
||||
<button type="button" onClick={parentClick}>
|
||||
Parent action
|
||||
{createPortal(
|
||||
<Infotip>
|
||||
<InfotipTrigger aria-label="Details" onClick={triggerClick} ref={triggerRef} />
|
||||
<InfotipContent aria-label="Details">Explanation</InfotipContent>
|
||||
</Infotip>,
|
||||
document.body,
|
||||
)}
|
||||
</button>,
|
||||
)
|
||||
await screen.getByRole('button', { name: 'Details' }).click()
|
||||
await expect
|
||||
.element(screen.getByRole('dialog', { name: 'Details' }))
|
||||
.toHaveTextContent('Explanation')
|
||||
expect(triggerRef.current?.tagName).toBe('BUTTON')
|
||||
expect(triggerClick).toHaveBeenCalledOnce()
|
||||
expect(parentClick).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('lets a caller cancel opening without losing trigger event composition', async () => {
|
||||
const onOpenChange = vi.fn()
|
||||
const screen = await render(
|
||||
<Infotip onOpenChange={onOpenChange}>
|
||||
<InfotipTrigger
|
||||
aria-label="Unavailable details"
|
||||
openOnHover={false}
|
||||
onClick={(event) => event.preventBaseUIHandler()}
|
||||
/>
|
||||
<InfotipContent aria-label="Details">Explanation</InfotipContent>
|
||||
</Infotip>,
|
||||
)
|
||||
await screen.getByRole('button', { name: 'Unavailable details' }).click()
|
||||
expect(onOpenChange).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,47 @@
|
||||
import type * as React from 'react'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
|
||||
export function infotipTypeContracts(
|
||||
triggerRef: React.Ref<HTMLButtonElement>,
|
||||
popupRef: React.Ref<HTMLDivElement>,
|
||||
) {
|
||||
const composition = (
|
||||
<Infotip<{ title: string }>>
|
||||
{({ payload }) => (
|
||||
<>
|
||||
<InfotipTrigger
|
||||
aria-label="Details"
|
||||
ref={triggerRef}
|
||||
payload={{ title: 'Details' }}
|
||||
iconVariant="information"
|
||||
iconSize="small"
|
||||
className={(state) => (state.open ? 'text-text-accent' : undefined)}
|
||||
style={(state) => ({ opacity: state.disabled ? 0.5 : 1 })}
|
||||
/>
|
||||
<InfotipContent
|
||||
ref={popupRef}
|
||||
aria-label="Details"
|
||||
placement="bottom-start"
|
||||
sideOffset={({ side }) => (side === 'top' ? 4 : 8)}
|
||||
className={(state) => (state.open ? 'w-60' : undefined)}
|
||||
style={(state) => ({ opacity: state.open ? 1 : 0 })}
|
||||
>
|
||||
{payload?.title}
|
||||
</InfotipContent>
|
||||
</>
|
||||
)}
|
||||
</Infotip>
|
||||
)
|
||||
const labelledTrigger = <InfotipTrigger aria-labelledby="visible-label" />
|
||||
// @ts-expect-error An icon-only trigger requires an accessible name.
|
||||
const unnamed = <InfotipTrigger />
|
||||
// @ts-expect-error The icon is owned by InfotipTrigger.
|
||||
const children = <InfotipTrigger aria-label="Details">Custom trigger</InfotipTrigger>
|
||||
// @ts-expect-error InfotipTrigger always renders a native button.
|
||||
const render = <InfotipTrigger aria-label="Details" render={<div />} />
|
||||
const state = (
|
||||
// @ts-expect-error Popup state is separate from trigger state.
|
||||
<InfotipContent className={(state) => String(state.disabled)}>Details</InfotipContent>
|
||||
)
|
||||
return { composition, labelledTrigger, unnamed, children, render, state }
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react-vite'
|
||||
import * as React from 'react'
|
||||
import { expect, userEvent, waitFor, within } from 'storybook/test'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '.'
|
||||
import { Button } from '../button'
|
||||
|
||||
const meta = {
|
||||
title: 'Base/UI/Infotip',
|
||||
component: Infotip,
|
||||
parameters: {
|
||||
layout: 'centered',
|
||||
docs: {
|
||||
description: {
|
||||
component:
|
||||
'An information button that opens explanatory content using Popover semantics. Compose the root, icon trigger, and content. Name the trigger and the dialog; use aria-labelledby when a visible heading exists. The popup remains available to keyboard and touch users and may contain links.',
|
||||
},
|
||||
},
|
||||
},
|
||||
} satisfies Meta<typeof Infotip>
|
||||
|
||||
export default meta
|
||||
type Story = StoryObj<typeof meta>
|
||||
|
||||
export const Default: Story = {
|
||||
render: () => (
|
||||
<div className="flex items-center gap-1 text-text-secondary">
|
||||
Document processing
|
||||
<Infotip>
|
||||
<InfotipTrigger aria-label="Document processing" />
|
||||
<InfotipContent aria-label="Document processing">
|
||||
Documents are processed in the order they are received. Larger files can take longer to
|
||||
finish indexing.
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
),
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement)
|
||||
const body = within(canvasElement.ownerDocument.body)
|
||||
const trigger = canvas.getByRole('button', { name: 'Document processing' })
|
||||
await userEvent.hover(trigger)
|
||||
const popup = await body.findByRole('dialog', { name: 'Document processing' })
|
||||
await userEvent.hover(popup)
|
||||
await waitFor(() => expect(popup).toBeVisible())
|
||||
await userEvent.keyboard('{Escape}')
|
||||
await waitFor(() => expect(body.queryByRole('dialog')).not.toBeInTheDocument())
|
||||
},
|
||||
}
|
||||
|
||||
export const WithLink: Story = {
|
||||
render: () => (
|
||||
<Infotip>
|
||||
<InfotipTrigger aria-label="Agent instructions" iconVariant="information" />
|
||||
<InfotipContent aria-label="Agent instructions" className="w-60">
|
||||
Describe the agent’s task and the rules it should follow.{' '}
|
||||
<a href="https://docs.dify.ai" className="text-text-accent underline">
|
||||
Read the documentation
|
||||
</a>
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
),
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement)
|
||||
const body = within(canvasElement.ownerDocument.body)
|
||||
const trigger = canvas.getByRole('button', { name: 'Agent instructions' })
|
||||
trigger.focus()
|
||||
await userEvent.keyboard('{Enter}')
|
||||
const popup = await body.findByRole('dialog', { name: 'Agent instructions' })
|
||||
await waitFor(() =>
|
||||
expect(within(popup).getByRole('link', { name: 'Read the documentation' })).toHaveFocus(),
|
||||
)
|
||||
await userEvent.keyboard('{Escape}')
|
||||
await waitFor(() => expect(body.queryByRole('dialog')).not.toBeInTheDocument())
|
||||
expect(trigger).toHaveFocus()
|
||||
},
|
||||
}
|
||||
|
||||
function ControlledExample() {
|
||||
const [open, setOpen] = React.useState(false)
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<Button onClick={() => setOpen(true)}>Show explanation</Button>
|
||||
<Infotip open={open} onOpenChange={setOpen}>
|
||||
<InfotipTrigger aria-label="Processing error" iconVariant="warning" />
|
||||
<InfotipContent aria-label="Processing error">
|
||||
The file could not be processed. Review its contents before trying again.
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export const Controlled: Story = {
|
||||
render: () => <ControlledExample />,
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement)
|
||||
const body = within(canvasElement.ownerDocument.body)
|
||||
await userEvent.click(canvas.getByRole('button', { name: 'Show explanation' }))
|
||||
await waitFor(() =>
|
||||
expect(body.getByRole('dialog', { name: 'Processing error' })).toBeVisible(),
|
||||
)
|
||||
await userEvent.keyboard('{Escape}')
|
||||
await waitFor(() => expect(body.queryByRole('dialog')).not.toBeInTheDocument())
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
'use client'
|
||||
|
||||
import type * as React from 'react'
|
||||
import type { Placement } from '../placement'
|
||||
import { Popover as BasePopover } from '@base-ui/react/popover'
|
||||
import { cn } from '../cn'
|
||||
import { iconButtonVariants } from '../icon-button'
|
||||
import { resolveClassName } from '../internals/resolve-class-name'
|
||||
import { floatingPopupAnimationClassName } from '../overlay-shared'
|
||||
import { parsePlacement } from '../placement'
|
||||
|
||||
const iconClassNames = {
|
||||
question: 'i-ri-question-line',
|
||||
information: 'i-ri-information-line',
|
||||
warning: 'i-ri-error-warning-line',
|
||||
} as const
|
||||
|
||||
const iconSizeClassNames = {
|
||||
small: 'size-3',
|
||||
medium: 'size-3.5',
|
||||
large: 'size-4',
|
||||
} as const
|
||||
|
||||
const Infotip = BasePopover.Root
|
||||
type InfotipProps<Payload = unknown> = BasePopover.Root.Props<Payload>
|
||||
|
||||
type InfotipTriggerProps<Payload = unknown> = Omit<
|
||||
BasePopover.Trigger.Props<Payload>,
|
||||
'children' | 'render' | 'nativeButton' | 'aria-label' | 'aria-labelledby'
|
||||
> &
|
||||
(
|
||||
| { 'aria-label': string; 'aria-labelledby'?: never }
|
||||
| { 'aria-label'?: never; 'aria-labelledby': string }
|
||||
) & {
|
||||
iconVariant?: keyof typeof iconClassNames
|
||||
iconSize?: keyof typeof iconSizeClassNames
|
||||
}
|
||||
|
||||
function InfotipTrigger<Payload = unknown>({
|
||||
className,
|
||||
iconVariant = 'question',
|
||||
iconSize = 'medium',
|
||||
openOnHover = true,
|
||||
delay = 300,
|
||||
closeDelay = 200,
|
||||
onClick,
|
||||
...props
|
||||
}: InfotipTriggerProps<Payload>) {
|
||||
return (
|
||||
<BasePopover.Trigger
|
||||
{...props}
|
||||
openOnHover={openOnHover}
|
||||
delay={delay}
|
||||
closeDelay={closeDelay}
|
||||
onClick={(event) => {
|
||||
event.stopPropagation()
|
||||
onClick?.(event)
|
||||
}}
|
||||
className={(state) =>
|
||||
cn(
|
||||
iconButtonVariants({ variant: null, size: 'xs' }),
|
||||
'shrink-0 border-0 bg-transparent text-text-quaternary outline-hidden hover:text-text-tertiary',
|
||||
resolveClassName(className, state),
|
||||
)
|
||||
}
|
||||
>
|
||||
<span aria-hidden className={cn(iconClassNames[iconVariant], iconSizeClassNames[iconSize])} />
|
||||
</BasePopover.Trigger>
|
||||
)
|
||||
}
|
||||
|
||||
type InfotipContentProps = Omit<BasePopover.Popup.Props, 'children'> &
|
||||
Pick<BasePopover.Positioner.Props, 'sideOffset' | 'alignOffset'> & {
|
||||
children: React.ReactNode
|
||||
placement?: Placement
|
||||
}
|
||||
|
||||
function InfotipContent({
|
||||
placement = 'top',
|
||||
sideOffset = 8,
|
||||
alignOffset = 0,
|
||||
className,
|
||||
...props
|
||||
}: InfotipContentProps) {
|
||||
const { side, align } = parsePlacement(placement)
|
||||
return (
|
||||
<BasePopover.Portal>
|
||||
<BasePopover.Positioner
|
||||
side={side}
|
||||
align={align}
|
||||
sideOffset={sideOffset}
|
||||
alignOffset={alignOffset}
|
||||
className="z-50 outline-hidden"
|
||||
>
|
||||
<BasePopover.Popup
|
||||
{...props}
|
||||
className={(state) =>
|
||||
cn(
|
||||
'outline-hidden focus:outline-hidden focus-visible:outline-hidden',
|
||||
floatingPopupAnimationClassName,
|
||||
'max-w-75 rounded-lg border-[0.5px] border-components-panel-border bg-components-tooltip-bg px-3 py-2 system-xs-regular text-text-secondary shadow-[0px_12px_16px_-4px_var(--color-shadow-shadow-5),0px_4px_6px_-2px_var(--color-shadow-shadow-1)] backdrop-blur-[5px]',
|
||||
resolveClassName(className, state),
|
||||
)
|
||||
}
|
||||
/>
|
||||
</BasePopover.Positioner>
|
||||
</BasePopover.Portal>
|
||||
)
|
||||
}
|
||||
|
||||
export { Infotip, InfotipContent, InfotipTrigger }
|
||||
export type { InfotipContentProps, InfotipProps, InfotipTriggerProps }
|
||||
+1
-1
@@ -17,7 +17,7 @@ Web owns application-specific requirements and consumes shared architecture guid
|
||||
- Give save and submit flows a real form boundary with visible labels and accessible errors. Use Dify UI `Form` when its structured submission and validation contract is the owner; otherwise use a native form. Follow the canonical [form contract].
|
||||
- Follow the canonical [Button contract] and [IconButton contract] for action semantics, loading, accessible names, and primitive composition. Do not add a Web wrapper that hides those contracts.
|
||||
- Follow [Accessible names and descriptions] when choosing or changing visible labels, ARIA naming, descriptions, or visually hidden text. Web owns localization and feature-specific status announcements; do not redefine the Dify UI naming contract locally.
|
||||
- Follow the [Dify UI overlay contract] for primitive selection, portals, focus, and layering. Reuse the Web `Infotip` composite for an info glyph that opens explanatory content. Do not introduce a generic Web wrapper that recreates Dify UI overlay behavior.
|
||||
- Follow the [Dify UI overlay contract] for primitive selection, portals, focus, and layering. Use `@langgenius/dify-ui/infotip` for an info glyph that opens explanatory content. Do not introduce a generic Web wrapper that recreates Dify UI overlay behavior.
|
||||
- For custom SVG icons, follow `../packages/iconify-collections/README.md`; do not add generated React icons under `app/components/base/icons/src/`.
|
||||
- `docs/test.md` is the single source of truth for Web automated-test policy. Skills may route and execute that policy but must not redefine it.
|
||||
|
||||
|
||||
@@ -7,11 +7,11 @@ import type {
|
||||
import type { AccessMode } from '@/models/access-control'
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import { DialogDescription, DialogTitle } from '@langgenius/dify-ui/dialog'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { RadioGroup } from '@langgenius/dify-ui/radio-group'
|
||||
import { useId } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { AccessMode as AccessModeValue } from '@/models/access-control'
|
||||
import { Infotip } from '../../base/infotip'
|
||||
import AccessControlDialog from './access-control-dialog'
|
||||
import AccessControlItem from './access-control-item'
|
||||
import SpecificGroupsOrMembers, { WebAppSSONotEnabledTip } from './specific-groups-or-members'
|
||||
@@ -111,15 +111,22 @@ export function AccessControlForm({
|
||||
{t(($) => $['accessControlDialog.accessItems.anyone'], { ns: 'app' })}
|
||||
</p>
|
||||
{publicAccessDisabled && (
|
||||
<Infotip
|
||||
aria-label={t(($) => $['accessControlDialog.webAppPublicAccessDisabledTip'], {
|
||||
ns: 'app',
|
||||
})}
|
||||
className="h-4 w-4 shrink-0 text-text-warning-secondary hover:text-text-warning-secondary"
|
||||
>
|
||||
{t(($) => $['accessControlDialog.webAppPublicAccessDisabledTip'], {
|
||||
ns: 'app',
|
||||
})}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={t(($) => $['accessControlDialog.webAppPublicAccessDisabledTip'], {
|
||||
ns: 'app',
|
||||
})}
|
||||
className="h-4 w-4 shrink-0 text-text-warning-secondary hover:text-text-warning-secondary"
|
||||
/>
|
||||
<InfotipContent
|
||||
aria-label={t(($) => $['accessControlDialog.webAppPublicAccessDisabledTip'], {
|
||||
ns: 'app',
|
||||
})}
|
||||
>
|
||||
{t(($) => $['accessControlDialog.webAppPublicAccessDisabledTip'], {
|
||||
ns: 'app',
|
||||
})}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -3,10 +3,10 @@ import type { AccessControlAccount, AccessControlGroup } from '@/models/access-c
|
||||
import { Avatar } from '@langgenius/dify-ui/avatar'
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import { IconButton } from '@langgenius/dify-ui/icon-button'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { LoadingPlaceholder } from '@/app/components/base/loading-placeholder'
|
||||
import { AccessMode } from '@/models/access-control'
|
||||
import { Infotip } from '../../base/infotip'
|
||||
import AddMemberOrGroupDialog from './add-member-or-group-pop'
|
||||
|
||||
export type AccessControlSubjects = {
|
||||
@@ -222,12 +222,13 @@ export function WebAppSSONotEnabledTip() {
|
||||
const tip = t(($) => $['accessControlDialog.webAppSSONotEnabledTip'], { ns: 'app' })
|
||||
|
||||
return (
|
||||
<Infotip
|
||||
aria-label={tip}
|
||||
className="text-text-warning-secondary hover:text-text-warning-secondary"
|
||||
iconSize="large"
|
||||
>
|
||||
{tip}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={tip}
|
||||
className="text-text-warning-secondary hover:text-text-warning-secondary"
|
||||
iconSize="large"
|
||||
/>
|
||||
<InfotipContent aria-label={tip}>{tip}</InfotipContent>
|
||||
</Infotip>
|
||||
)
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import type { ExternalDataTool } from '@/models/common'
|
||||
import type { PromptRole, PromptVariable } from '@/models/debug'
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { RiDeleteBinLine, RiErrorWarningFill } from '@remixicon/react'
|
||||
import { useBoolean } from 'ahooks'
|
||||
import copy from 'copy-to-clipboard'
|
||||
@@ -14,7 +15,6 @@ import { useContext } from 'use-context-selector'
|
||||
import { ADD_EXTERNAL_DATA_TOOL } from '@/app/components/app/configuration/config-var'
|
||||
import { toast } from '@/app/components/app/configuration/toast'
|
||||
import { Copy, CopyCheck } from '@/app/components/base/icons/src/vender/line/files'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import PromptEditor from '@/app/components/base/prompt-editor'
|
||||
import { INSERT_VARIABLE_VALUE_BLOCK_COMMAND } from '@/app/components/base/prompt-editor/plugins/variable-block'
|
||||
import ConfigContext from '@/context/debug-configuration'
|
||||
@@ -189,12 +189,17 @@ const AdvancedPromptInput: FC<Props> = ({
|
||||
<div className="text-sm font-semibold text-indigo-800 uppercase">
|
||||
{t(($) => $['pageTitle.line1'], { ns: 'appDebug' })}
|
||||
</div>
|
||||
<Infotip
|
||||
aria-label={t(($) => $.promptTip, { ns: 'appDebug' })}
|
||||
className="ml-1"
|
||||
popupClassName="w-[180px]"
|
||||
>
|
||||
{t(($) => $.promptTip, { ns: 'appDebug' })}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={t(($) => $.promptTip, { ns: 'appDebug' })}
|
||||
className="ml-1"
|
||||
/>
|
||||
<InfotipContent
|
||||
aria-label={t(($) => $.promptTip, { ns: 'appDebug' })}
|
||||
className="w-[180px]"
|
||||
>
|
||||
{t(($) => $.promptTip, { ns: 'appDebug' })}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -4,6 +4,7 @@ import type { ExternalDataTool } from '@/models/common'
|
||||
import type { PromptVariable } from '@/models/debug'
|
||||
import type { GenRes } from '@/service/debug'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { useBoolean } from 'ahooks'
|
||||
import { noop } from 'es-toolkit/function'
|
||||
import { produce } from 'immer'
|
||||
@@ -16,7 +17,6 @@ import AutomaticBtn from '@/app/components/app/configuration/config/automatic/au
|
||||
import GetAutomaticResModal from '@/app/components/app/configuration/config/automatic/get-automatic-res'
|
||||
import { toast } from '@/app/components/app/configuration/toast'
|
||||
import { useFeaturesStore } from '@/app/components/base/features/hooks'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import PromptEditor from '@/app/components/base/prompt-editor'
|
||||
import { PROMPT_EDITOR_UPDATE_VALUE_BY_EVENT_EMITTER } from '@/app/components/base/prompt-editor/plugins/update-block'
|
||||
import { INSERT_VARIABLE_VALUE_BLOCK_COMMAND } from '@/app/components/base/prompt-editor/plugins/variable-block'
|
||||
@@ -206,12 +206,17 @@ const Prompt: FC<ISimplePromptInput> = ({
|
||||
{title}
|
||||
</h2>
|
||||
{!readonly && (
|
||||
<Infotip
|
||||
aria-label={t(($) => $.promptTip, { ns: 'appDebug' })}
|
||||
className="ml-1"
|
||||
popupClassName="w-[180px]"
|
||||
>
|
||||
{t(($) => $.promptTip, { ns: 'appDebug' })}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={t(($) => $.promptTip, { ns: 'appDebug' })}
|
||||
className="ml-1"
|
||||
/>
|
||||
<InfotipContent
|
||||
aria-label={t(($) => $.promptTip, { ns: 'appDebug' })}
|
||||
className="w-[180px]"
|
||||
>
|
||||
{t(($) => $.promptTip, { ns: 'appDebug' })}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -6,6 +6,7 @@ import type { SelectorTranslate } from '@/app/components/app/configuration/utils
|
||||
import type { FileEntity } from '@/app/components/base/file-uploader/types'
|
||||
import type { InputVar, UploadFileSetting } from '@/app/components/workflow/types'
|
||||
import { Checkbox } from '@langgenius/dify-ui/checkbox'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { Input } from '@langgenius/dify-ui/input'
|
||||
import { NumberField, NumberFieldGroup, NumberFieldInput } from '@langgenius/dify-ui/number-field'
|
||||
import {
|
||||
@@ -26,7 +27,6 @@ import * as React from 'react'
|
||||
import { Trans } from 'react-i18next'
|
||||
import { getStringSelectorTranslate } from '@/app/components/app/configuration/utils'
|
||||
import { FileUploaderInAttachmentWrapper } from '@/app/components/base/file-uploader'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor'
|
||||
import FileUploadSetting from '@/app/components/workflow/nodes/_base/components/file-upload-setting'
|
||||
import { CodeLanguage } from '@/app/components/workflow/nodes/code/types'
|
||||
@@ -409,21 +409,24 @@ const ConfigModalFormFields: FC<ConfigModalFormFieldsProps> = ({
|
||||
</span>
|
||||
</label>
|
||||
<div className="flex items-center gap-1">
|
||||
<Infotip aria-label={hiddenDescriptionAriaLabel}>
|
||||
<Trans
|
||||
i18nKey={($) => $['variableConfig.hiddenDescription']}
|
||||
ns="appDebug"
|
||||
components={{
|
||||
docLink: (
|
||||
<a
|
||||
href={docLink('/use-dify/nodes/user-input#hide-and-pre-fill-input-fields')}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-text-accent hover:underline"
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
<Infotip>
|
||||
<InfotipTrigger aria-label={hiddenDescriptionAriaLabel} />
|
||||
<InfotipContent aria-label={hiddenDescriptionAriaLabel}>
|
||||
<Trans
|
||||
i18nKey={($) => $['variableConfig.hiddenDescription']}
|
||||
ns="appDebug"
|
||||
components={{
|
||||
docLink: (
|
||||
<a
|
||||
href={docLink('/use-dify/nodes/user-input#hide-and-pre-fill-input-fields')}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-text-accent hover:underline"
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
AlertDialogTitle,
|
||||
} from '@langgenius/dify-ui/alert-dialog'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { useBoolean } from 'ahooks'
|
||||
import { produce } from 'immer'
|
||||
import * as React from 'react'
|
||||
@@ -21,7 +22,6 @@ import { useTranslation } from 'react-i18next'
|
||||
import { ReactSortable } from 'react-sortablejs'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import { toast } from '@/app/components/app/configuration/toast'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import { useKeyboardSortable } from '@/app/components/base/keyboard-sortable/use-keyboard-sortable'
|
||||
import { InputVarType } from '@/app/components/workflow/types'
|
||||
import ConfigContext from '@/context/debug-configuration'
|
||||
@@ -315,11 +315,14 @@ const ConfigVar: FC<IConfigVarProps> = ({ promptVariables, readonly, onPromptVar
|
||||
<div className="flex items-center">
|
||||
<h2 className="mr-1">{t(($) => $.variableTitle, { ns: 'appDebug' })}</h2>
|
||||
{!readonly && (
|
||||
<Infotip
|
||||
aria-label={t(($) => $.variableTip, { ns: 'appDebug' })}
|
||||
popupClassName="w-[180px]"
|
||||
>
|
||||
{t(($) => $.variableTip, { ns: 'appDebug' })}
|
||||
<Infotip>
|
||||
<InfotipTrigger aria-label={t(($) => $.variableTip, { ns: 'appDebug' })} />
|
||||
<InfotipContent
|
||||
aria-label={t(($) => $.variableTip, { ns: 'appDebug' })}
|
||||
className="w-[180px]"
|
||||
>
|
||||
{t(($) => $.variableTip, { ns: 'appDebug' })}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { Switch } from '@langgenius/dify-ui/switch'
|
||||
import { noop } from 'es-toolkit/function'
|
||||
import { produce } from 'immer'
|
||||
@@ -11,7 +12,6 @@ import { useContext } from 'use-context-selector'
|
||||
// import { Resolution } from '@/types/app'
|
||||
import { useFeatures, useFeaturesStore } from '@/app/components/base/features/hooks'
|
||||
import { Vision } from '@/app/components/base/icons/src/vender/features'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import OptionCard from '@/app/components/workflow/nodes/_base/components/option-card'
|
||||
import { SupportUploadFileTypes } from '@/app/components/workflow/types'
|
||||
// import OptionCard from '@/app/components/workflow/nodes/_base/components/option-card'
|
||||
@@ -75,11 +75,14 @@ const ConfigVision: FC = () => {
|
||||
<h2 id={titleId} className="mr-1 system-sm-semibold text-text-secondary">
|
||||
{t(($) => $['vision.name'], { ns: 'appDebug' })}
|
||||
</h2>
|
||||
<Infotip
|
||||
aria-label={t(($) => $['vision.description'], { ns: 'appDebug' })}
|
||||
popupClassName="w-[180px]"
|
||||
>
|
||||
{t(($) => $['vision.description'], { ns: 'appDebug' })}
|
||||
<Infotip>
|
||||
<InfotipTrigger aria-label={t(($) => $['vision.description'], { ns: 'appDebug' })} />
|
||||
<InfotipContent
|
||||
aria-label={t(($) => $['vision.description'], { ns: 'appDebug' })}
|
||||
className="w-[180px]"
|
||||
>
|
||||
{t(($) => $['vision.description'], { ns: 'appDebug' })}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
<div className="flex shrink-0 items-center">
|
||||
@@ -89,17 +92,24 @@ const ConfigVision: FC = () => {
|
||||
<div className="system-xs-medium-uppercase text-text-tertiary">
|
||||
{t(($) => $['vision.visionSettings.resolution'], { ns: 'appDebug' })}
|
||||
</div>
|
||||
<Infotip
|
||||
aria-label={t(($) => $['vision.visionSettings.resolutionTooltip'], {
|
||||
ns: 'appDebug',
|
||||
})}
|
||||
popupClassName="w-[180px]"
|
||||
>
|
||||
{t(($) => $['vision.visionSettings.resolutionTooltip'], { ns: 'appDebug' })
|
||||
.split('\n')
|
||||
.map((item) => (
|
||||
<div key={item}>{item}</div>
|
||||
))}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={t(($) => $['vision.visionSettings.resolutionTooltip'], {
|
||||
ns: 'appDebug',
|
||||
})}
|
||||
/>
|
||||
<InfotipContent
|
||||
aria-label={t(($) => $['vision.visionSettings.resolutionTooltip'], {
|
||||
ns: 'appDebug',
|
||||
})}
|
||||
className="w-[180px]"
|
||||
>
|
||||
{t(($) => $['vision.visionSettings.resolutionTooltip'], { ns: 'appDebug' })
|
||||
.split('\n')
|
||||
.map((item) => (
|
||||
<div key={item}>{item}</div>
|
||||
))}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
|
||||
@@ -3,13 +3,13 @@ import type { FC } from 'react'
|
||||
import type { FileUpload } from '@/app/components/base/features/types'
|
||||
import { Field, FieldItem } from '@langgenius/dify-ui/field'
|
||||
import { Fieldset, FieldsetLegend } from '@langgenius/dify-ui/fieldset'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { RadioGroup, RadioItem } from '@langgenius/dify-ui/radio-group'
|
||||
import { produce } from 'immer'
|
||||
import * as React from 'react'
|
||||
import { useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useFeatures, useFeaturesStore } from '@/app/components/base/features/hooks'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import ParamItem from '@/app/components/base/param-item'
|
||||
import { Resolution, TransferMethod } from '@/types/app'
|
||||
|
||||
@@ -70,17 +70,24 @@ const ParamConfigContent: FC = () => {
|
||||
<FieldsetLegend className="m-0 py-0 text-[13px] leading-4.5 font-semibold text-text-secondary">
|
||||
{t(($) => $['vision.visionSettings.resolution'], { ns: 'appDebug' })}
|
||||
</FieldsetLegend>
|
||||
<Infotip
|
||||
aria-label={t(($) => $['vision.visionSettings.resolutionTooltip'], {
|
||||
ns: 'appDebug',
|
||||
})}
|
||||
popupClassName="w-[180px]"
|
||||
>
|
||||
{t(($) => $['vision.visionSettings.resolutionTooltip'], { ns: 'appDebug' })
|
||||
.split('\n')
|
||||
.map((item) => (
|
||||
<div key={item}>{item}</div>
|
||||
))}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={t(($) => $['vision.visionSettings.resolutionTooltip'], {
|
||||
ns: 'appDebug',
|
||||
})}
|
||||
/>
|
||||
<InfotipContent
|
||||
aria-label={t(($) => $['vision.visionSettings.resolutionTooltip'], {
|
||||
ns: 'appDebug',
|
||||
})}
|
||||
className="w-[180px]"
|
||||
>
|
||||
{t(($) => $['vision.visionSettings.resolutionTooltip'], { ns: 'appDebug' })
|
||||
.split('\n')
|
||||
.map((item) => (
|
||||
<div key={item}>{item}</div>
|
||||
))}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import * as React from 'react'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
|
||||
type Props = Readonly<{
|
||||
className?: string
|
||||
@@ -23,8 +23,11 @@ const ItemPanel: FC<Props> = ({ className, icon, name, description, children })
|
||||
<div className="flex items-center">
|
||||
{icon}
|
||||
<div className="mr-1 ml-3 text-sm/6 font-semibold text-text-secondary">{name}</div>
|
||||
<Infotip aria-label={description} popupClassName="w-[180px]">
|
||||
{description}
|
||||
<Infotip>
|
||||
<InfotipTrigger aria-label={description} />
|
||||
<InfotipContent aria-label={description} className="w-[180px]">
|
||||
{description}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
<div>{children}</div>
|
||||
|
||||
@@ -6,6 +6,7 @@ import type { ToolWithProvider } from '@/app/components/workflow/types'
|
||||
import type { AgentTool } from '@/types/app'
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@langgenius/dify-ui/popover'
|
||||
import { StatusDot } from '@langgenius/dify-ui/status-dot'
|
||||
import { Switch } from '@langgenius/dify-ui/switch'
|
||||
@@ -21,7 +22,6 @@ import { OperationButton } from '@/app/components/app/configuration/base/operati
|
||||
import AppIcon from '@/app/components/base/app-icon'
|
||||
import { DefaultToolIcon } from '@/app/components/base/icons/src/public/other'
|
||||
import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import { parseToolProviderType } from '@/app/components/tools/provider-type'
|
||||
import { CollectionType } from '@/app/components/tools/types'
|
||||
import {
|
||||
@@ -163,8 +163,15 @@ const AgentTools: FC = () => {
|
||||
title={
|
||||
<div className="flex items-center">
|
||||
<h2 className="mr-1">{t(($) => $['agent.tools.name'], { ns: 'appDebug' })}</h2>
|
||||
<Infotip aria-label={t(($) => $['agent.tools.description'], { ns: 'appDebug' })}>
|
||||
{t(($) => $['agent.tools.description'], { ns: 'appDebug' })}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={t(($) => $['agent.tools.description'], { ns: 'appDebug' })}
|
||||
/>
|
||||
<InfotipContent
|
||||
aria-label={t(($) => $['agent.tools.description'], { ns: 'appDebug' })}
|
||||
>
|
||||
{t(($) => $['agent.tools.description'], { ns: 'appDebug' })}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
}
|
||||
@@ -239,24 +246,26 @@ const AgentTools: FC = () => {
|
||||
</span>
|
||||
<span className="text-text-tertiary">{item.tool_label}</span>
|
||||
{!item.isDeleted && !readonly && (
|
||||
<Infotip
|
||||
aria-label={item.tool_name}
|
||||
className="ml-0.5 opacity-0 group-hover:opacity-100 focus-visible:opacity-100"
|
||||
popupClassName="w-[220px]"
|
||||
>
|
||||
<div>
|
||||
<div className="mb-1.5 text-text-secondary">{item.tool_name}</div>
|
||||
<div className="mb-1.5 text-text-tertiary">
|
||||
{t(($) => $.toolNameUsageTip, { ns: 'tools' })}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={item.tool_name}
|
||||
className="ml-0.5 opacity-0 group-hover:opacity-100 focus-visible:opacity-100"
|
||||
/>
|
||||
<InfotipContent aria-label={item.tool_name} className="w-[220px]">
|
||||
<div>
|
||||
<div className="mb-1.5 text-text-secondary">{item.tool_name}</div>
|
||||
<div className="mb-1.5 text-text-tertiary">
|
||||
{t(($) => $.toolNameUsageTip, { ns: 'tools' })}
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
className="cursor-pointer rounded-sm border-none bg-transparent p-0 text-left text-text-accent outline-hidden hover:underline focus-visible:ring-1 focus-visible:ring-components-input-border-hover"
|
||||
onClick={() => void writeTextToClipboard(item.tool_name)}
|
||||
>
|
||||
{t(($) => $.copyToolName, { ns: 'tools' })}
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
className="cursor-pointer rounded-sm border-none bg-transparent p-0 text-left text-text-accent outline-hidden hover:underline focus-visible:ring-1 focus-visible:ring-components-input-border-hover"
|
||||
onClick={() => void writeTextToClipboard(item.tool_name)}
|
||||
>
|
||||
{t(($) => $.copyToolName, { ns: 'tools' })}
|
||||
</button>
|
||||
</div>
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { Switch } from '@langgenius/dify-ui/switch'
|
||||
import { produce } from 'immer'
|
||||
import * as React from 'react'
|
||||
@@ -8,7 +9,6 @@ import { useTranslation } from 'react-i18next'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import { useFeatures, useFeaturesStore } from '@/app/components/base/features/hooks'
|
||||
import { Microphone01 } from '@/app/components/base/icons/src/vender/features'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import { SupportUploadFileTypes } from '@/app/components/workflow/types'
|
||||
import ConfigContext from '@/context/debug-configuration'
|
||||
|
||||
@@ -54,11 +54,16 @@ const ConfigAudio: FC = () => {
|
||||
<div className="mr-1 system-sm-semibold text-text-secondary">
|
||||
{t(($) => $['feature.audioUpload.title'], { ns: 'appDebug' })}
|
||||
</div>
|
||||
<Infotip
|
||||
aria-label={t(($) => $['feature.audioUpload.description'], { ns: 'appDebug' })}
|
||||
popupClassName="w-[180px]"
|
||||
>
|
||||
{t(($) => $['feature.audioUpload.description'], { ns: 'appDebug' })}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={t(($) => $['feature.audioUpload.description'], { ns: 'appDebug' })}
|
||||
/>
|
||||
<InfotipContent
|
||||
aria-label={t(($) => $['feature.audioUpload.description'], { ns: 'appDebug' })}
|
||||
className="w-[180px]"
|
||||
>
|
||||
{t(($) => $['feature.audioUpload.description'], { ns: 'appDebug' })}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
{!readonly && (
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { Switch } from '@langgenius/dify-ui/switch'
|
||||
import { produce } from 'immer'
|
||||
import * as React from 'react'
|
||||
@@ -8,7 +9,6 @@ import { useTranslation } from 'react-i18next'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import { useFeatures, useFeaturesStore } from '@/app/components/base/features/hooks'
|
||||
import { Document } from '@/app/components/base/icons/src/vender/features'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import { SupportUploadFileTypes } from '@/app/components/workflow/types'
|
||||
import ConfigContext from '@/context/debug-configuration'
|
||||
|
||||
@@ -55,11 +55,16 @@ const ConfigDocument: FC = () => {
|
||||
<div className="mr-1 system-sm-semibold text-text-secondary">
|
||||
{t(($) => $['feature.documentUpload.title'], { ns: 'appDebug' })}
|
||||
</div>
|
||||
<Infotip
|
||||
aria-label={t(($) => $['feature.documentUpload.description'], { ns: 'appDebug' })}
|
||||
popupClassName="w-[180px]"
|
||||
>
|
||||
{t(($) => $['feature.documentUpload.description'], { ns: 'appDebug' })}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={t(($) => $['feature.documentUpload.description'], { ns: 'appDebug' })}
|
||||
/>
|
||||
<InfotipContent
|
||||
aria-label={t(($) => $['feature.documentUpload.description'], { ns: 'appDebug' })}
|
||||
className="w-[180px]"
|
||||
>
|
||||
{t(($) => $['feature.documentUpload.description'], { ns: 'appDebug' })}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
{!readonly && (
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
import type { FC } from 'react'
|
||||
import type { Props } from './var-picker'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import * as React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { BracketsX } from '@/app/components/base/icons/src/vender/line/development'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import VarPicker from './var-picker'
|
||||
|
||||
const ContextVar: FC<Props> = (props) => {
|
||||
@@ -29,11 +29,16 @@ const ContextVar: FC<Props> = (props) => {
|
||||
<div className="mr-1 text-sm font-medium text-text-secondary">
|
||||
{t(($) => $['feature.dataSet.queryVariable.title'], { ns: 'appDebug' })}
|
||||
</div>
|
||||
<Infotip
|
||||
aria-label={t(($) => $['feature.dataSet.queryVariable.tip'], { ns: 'appDebug' })}
|
||||
popupClassName="w-[180px]"
|
||||
>
|
||||
{t(($) => $['feature.dataSet.queryVariable.tip'], { ns: 'appDebug' })}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={t(($) => $['feature.dataSet.queryVariable.tip'], { ns: 'appDebug' })}
|
||||
/>
|
||||
<InfotipContent
|
||||
aria-label={t(($) => $['feature.dataSet.queryVariable.tip'], { ns: 'appDebug' })}
|
||||
className="w-[180px]"
|
||||
>
|
||||
{t(($) => $['feature.dataSet.queryVariable.tip'], { ns: 'appDebug' })}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
|
||||
|
||||
+26
-17
@@ -6,12 +6,12 @@ import type { ModelConfig } from '@/app/components/workflow/types'
|
||||
import type { DataSet } from '@/models/datasets'
|
||||
import type { DatasetConfigs } from '@/models/debug'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { Separator } from '@langgenius/dify-ui/separator'
|
||||
import { Switch } from '@langgenius/dify-ui/switch'
|
||||
import { memo, useCallback, useEffect, useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { toast } from '@/app/components/app/configuration/toast'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import ScoreThresholdItem from '@/app/components/base/param-item/score-threshold-item'
|
||||
import TopKItem from '@/app/components/base/param-item/top-k-item'
|
||||
import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
@@ -244,12 +244,11 @@ const ConfigContent: FC<Props> = ({
|
||||
onClick={() => handleRerankModeChange(option.value)}
|
||||
>
|
||||
<div className="truncate">{option.label}</div>
|
||||
<Infotip
|
||||
aria-label={option.tips}
|
||||
className="ml-0.5 size-3.5"
|
||||
popupClassName="w-[200px]"
|
||||
>
|
||||
{option.tips}
|
||||
<Infotip>
|
||||
<InfotipTrigger aria-label={option.tips} className="ml-0.5 size-3.5" />
|
||||
<InfotipContent aria-label={option.tips} className="w-[200px]">
|
||||
{option.tips}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
))}
|
||||
@@ -268,12 +267,17 @@ const ConfigContent: FC<Props> = ({
|
||||
<div className="ml-1 system-sm-semibold leading-8 text-text-secondary">
|
||||
{t(($) => $['modelProvider.rerankModel.key'], { ns: 'common' })}
|
||||
</div>
|
||||
<Infotip
|
||||
aria-label={t(($) => $['modelProvider.rerankModel.tip'], { ns: 'common' })}
|
||||
className="ml-1"
|
||||
popupClassName="w-[200px]"
|
||||
>
|
||||
{t(($) => $['modelProvider.rerankModel.tip'], { ns: 'common' })}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={t(($) => $['modelProvider.rerankModel.tip'], { ns: 'common' })}
|
||||
className="ml-1"
|
||||
/>
|
||||
<InfotipContent
|
||||
aria-label={t(($) => $['modelProvider.rerankModel.tip'], { ns: 'common' })}
|
||||
className="w-[200px]"
|
||||
>
|
||||
{t(($) => $['modelProvider.rerankModel.tip'], { ns: 'common' })}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
{showRerankModel && (
|
||||
@@ -344,10 +348,15 @@ const ConfigContent: FC<Props> = ({
|
||||
<div className="text-[13px] leading-8 font-medium text-text-primary">
|
||||
{t(($) => $['modelProvider.systemReasoningModel.key'], { ns: 'common' })}
|
||||
</div>
|
||||
<Infotip
|
||||
aria-label={t(($) => $['modelProvider.systemReasoningModel.tip'], { ns: 'common' })}
|
||||
>
|
||||
{t(($) => $['modelProvider.systemReasoningModel.tip'], { ns: 'common' })}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={t(($) => $['modelProvider.systemReasoningModel.tip'], { ns: 'common' })}
|
||||
/>
|
||||
<InfotipContent
|
||||
aria-label={t(($) => $['modelProvider.systemReasoningModel.tip'], { ns: 'common' })}
|
||||
>
|
||||
{t(($) => $['modelProvider.systemReasoningModel.tip'], { ns: 'common' })}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
<ModelParameterModal
|
||||
|
||||
@@ -4,11 +4,11 @@ import type { Dayjs } from 'dayjs'
|
||||
import type { SelectorParam } from 'i18next'
|
||||
import type { FC } from 'react'
|
||||
import type { ChartRow } from './app-chart-utils'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import ReactECharts from 'echarts-for-react'
|
||||
import * as React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import { LoadingPlaceholder } from '@/app/components/base/loading-placeholder'
|
||||
import { consoleQuery } from '@/service/console'
|
||||
import {
|
||||
@@ -102,8 +102,11 @@ const Chart: React.FC<IChartProps> = ({
|
||||
{title}
|
||||
</div>
|
||||
{explanation && (
|
||||
<Infotip aria-label={explanation} className="ml-1" popupClassName="w-[240px]">
|
||||
{explanation}
|
||||
<Infotip>
|
||||
<InfotipTrigger aria-label={explanation} className="ml-1" />
|
||||
<InfotipContent aria-label={explanation} className="w-[240px]">
|
||||
{explanation}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -5,6 +5,7 @@ import type { InputForm } from '../type'
|
||||
import type { FileUpload } from '@/app/components/base/features/types'
|
||||
import type { SpeechToTextTarget } from '@/app/components/base/voice-input/types'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { noop } from 'es-toolkit/function'
|
||||
import { decode } from 'html-entities'
|
||||
import { useCallback, useRef, useState } from 'react'
|
||||
@@ -14,7 +15,6 @@ import FeatureBar from '@/app/components/base/features/new-feature-panel/feature
|
||||
import { FileListInChatInput } from '@/app/components/base/file-uploader'
|
||||
import { useFile } from '@/app/components/base/file-uploader/hooks'
|
||||
import { FileContextProvider, useFileStore } from '@/app/components/base/file-uploader/store'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import VoiceInput from '@/app/components/base/voice-input'
|
||||
import { toast } from '@/app/notifications'
|
||||
import { TransferMethod } from '@/types/app'
|
||||
@@ -379,13 +379,18 @@ const ChatInputArea = ({
|
||||
<div className="flex items-center gap-1">
|
||||
<div className="min-w-0 flex-1 body-xs-medium text-text-accent">{footerNotice}</div>
|
||||
{shouldShowFooterNoticeTooltip && (
|
||||
<Infotip
|
||||
aria-label={footerNoticeAriaLabel}
|
||||
className="ml-auto size-5 rounded-md text-text-accent hover:bg-state-base-hover hover:text-text-accent"
|
||||
iconVariant="information"
|
||||
popupClassName="max-w-80 text-start wrap-break-word"
|
||||
>
|
||||
{footerNoticeTooltip}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={footerNoticeAriaLabel}
|
||||
className="ml-auto size-5 rounded-md text-text-accent hover:bg-state-base-hover hover:text-text-accent"
|
||||
iconVariant="information"
|
||||
/>
|
||||
<InfotipContent
|
||||
aria-label={footerNoticeAriaLabel}
|
||||
className="max-w-80 text-start wrap-break-word"
|
||||
>
|
||||
{footerNoticeTooltip}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
)}
|
||||
</div>
|
||||
|
||||
+6
-3
@@ -1,7 +1,7 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import * as React from 'react'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
|
||||
export const Item: FC<{ title: string; tooltip: string; children: React.JSX.Element }> = ({
|
||||
title,
|
||||
@@ -12,8 +12,11 @@ export const Item: FC<{ title: string; tooltip: string; children: React.JSX.Elem
|
||||
<div>
|
||||
<div className="mb-1 flex items-center space-x-1">
|
||||
<div className="py-1 system-sm-semibold text-text-secondary">{title}</div>
|
||||
<Infotip aria-label={tooltip} popupClassName="max-w-[200px] system-sm-regular">
|
||||
{tooltip}
|
||||
<Infotip>
|
||||
<InfotipTrigger aria-label={tooltip} />
|
||||
<InfotipContent aria-label={tooltip} className="max-w-[200px] system-sm-regular">
|
||||
{tooltip}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
<div>{children}</div>
|
||||
|
||||
@@ -5,6 +5,7 @@ import { Button } from '@langgenius/dify-ui/button'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Dialog, DialogContent } from '@langgenius/dify-ui/dialog'
|
||||
import { IconButton } from '@langgenius/dify-ui/icon-button'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { Separator } from '@langgenius/dify-ui/separator'
|
||||
import { useBoolean } from 'ahooks'
|
||||
import { produce } from 'immer'
|
||||
@@ -14,7 +15,6 @@ import { useTranslation } from 'react-i18next'
|
||||
import { ReactSortable } from 'react-sortablejs'
|
||||
import ConfirmAddVar from '@/app/components/app/configuration/config-prompt/confirm-add-var'
|
||||
import { getInputKeys } from '@/app/components/base/block-input'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import { useKeyboardSortable } from '@/app/components/base/keyboard-sortable/use-keyboard-sortable'
|
||||
import PromptEditor from '@/app/components/base/prompt-editor'
|
||||
import { checkKeys, getNewVar } from '@/utils/var'
|
||||
@@ -147,14 +147,21 @@ const OpeningSettingModal = ({
|
||||
<div className="text-sm font-medium text-text-primary">
|
||||
{t(($) => $['openingStatement.openingQuestion'], { ns: 'appDebug' })}
|
||||
</div>
|
||||
<Infotip
|
||||
aria-label={t(($) => $['openingStatement.openingQuestionDescription'], {
|
||||
ns: 'appDebug',
|
||||
})}
|
||||
className="size-3.5"
|
||||
popupClassName="max-w-[220px] system-sm-regular"
|
||||
>
|
||||
{t(($) => $['openingStatement.openingQuestionDescription'], { ns: 'appDebug' })}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={t(($) => $['openingStatement.openingQuestionDescription'], {
|
||||
ns: 'appDebug',
|
||||
})}
|
||||
className="size-3.5"
|
||||
/>
|
||||
<InfotipContent
|
||||
aria-label={t(($) => $['openingStatement.openingQuestionDescription'], {
|
||||
ns: 'appDebug',
|
||||
})}
|
||||
className="max-w-[220px] system-sm-regular"
|
||||
>
|
||||
{t(($) => $['openingStatement.openingQuestionDescription'], { ns: 'appDebug' })}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
<div className="text-xs leading-4.5 font-medium text-text-tertiary">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { Switch } from '@langgenius/dify-ui/switch'
|
||||
import * as React from 'react'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
|
||||
type Props = Readonly<{
|
||||
icon: React.ReactNode
|
||||
@@ -40,11 +40,14 @@ const FeatureCard = ({
|
||||
<div className="flex grow items-center system-sm-semibold text-text-secondary">
|
||||
<span id={titleId}>{title}</span>
|
||||
{!!tooltip && (
|
||||
<Infotip
|
||||
aria-label={typeof tooltip === 'string' ? tooltip : String(title)}
|
||||
className="ml-0.5 size-3.5"
|
||||
>
|
||||
{tooltip}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={typeof tooltip === 'string' ? tooltip : String(title)}
|
||||
className="ml-0.5 size-3.5"
|
||||
/>
|
||||
<InfotipContent aria-label={typeof tooltip === 'string' ? tooltip : String(title)}>
|
||||
{tooltip}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
)}
|
||||
</div>
|
||||
|
||||
+15
-10
@@ -1,6 +1,7 @@
|
||||
'use client'
|
||||
import type { OnFeaturesChange } from '@/app/components/base/features/types'
|
||||
import type { I18nKeysWithPrefix } from '@/types/i18n'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import {
|
||||
Select,
|
||||
SelectItem,
|
||||
@@ -19,7 +20,6 @@ import { useTranslation } from 'react-i18next'
|
||||
import { replace } from 'string-ts'
|
||||
import { AudioBtn } from '@/app/components/base/audio-btn'
|
||||
import { useFeatures, useFeaturesStore } from '@/app/components/base/features/hooks'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import { languages } from '@/i18n/language'
|
||||
import { useParams } from '@/next/navigation'
|
||||
import { consoleQuery } from '@/service/console'
|
||||
@@ -107,15 +107,20 @@ const VoiceParamConfig = ({ onClose, onChange }: VoiceParamConfigProps) => {
|
||||
<div className="mb-3">
|
||||
<div className="mb-1 flex items-center py-1 system-sm-semibold text-text-secondary">
|
||||
{t(($) => $['voice.voiceSettings.language'], { ns: 'appDebug' })}
|
||||
<Infotip
|
||||
aria-label={t(($) => $['voice.voiceSettings.resolutionTooltip'], { ns: 'appDebug' })}
|
||||
popupClassName="w-[180px]"
|
||||
>
|
||||
{t(($) => $['voice.voiceSettings.resolutionTooltip'], { ns: 'appDebug' })
|
||||
.split('\n')
|
||||
.map((item) => (
|
||||
<div key={item}>{item}</div>
|
||||
))}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={t(($) => $['voice.voiceSettings.resolutionTooltip'], { ns: 'appDebug' })}
|
||||
/>
|
||||
<InfotipContent
|
||||
aria-label={t(($) => $['voice.voiceSettings.resolutionTooltip'], { ns: 'appDebug' })}
|
||||
className="w-[180px]"
|
||||
>
|
||||
{t(($) => $['voice.voiceSettings.resolutionTooltip'], { ns: 'appDebug' })
|
||||
.split('\n')
|
||||
.map((item) => (
|
||||
<div key={item}>{item}</div>
|
||||
))}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
<Select
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { FieldState, FormSchema, TypeWithI18N } from '@/app/components/base
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Field, FieldItem, FieldLabel } from '@langgenius/dify-ui/field'
|
||||
import { Fieldset, FieldsetLegend } from '@langgenius/dify-ui/fieldset'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { Input } from '@langgenius/dify-ui/input'
|
||||
import { InputGroup, InputGroupAddon, InputGroupInput } from '@langgenius/dify-ui/input-group'
|
||||
import { NumberField, NumberFieldGroup, NumberFieldInput } from '@langgenius/dify-ui/number-field'
|
||||
@@ -23,7 +24,6 @@ import { useTranslation } from 'react-i18next'
|
||||
import { CheckboxList } from '@/app/components/base/checkbox-list'
|
||||
import { CopyFeedback } from '@/app/components/base/copy-feedback'
|
||||
import { FormItemValidateStatusEnum, FormTypeEnum } from '@/app/components/base/form/types'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import { useRenderI18nObject } from '@/hooks/use-i18n'
|
||||
import { useTriggerPluginDynamicOptions } from '@/service/use-triggers'
|
||||
|
||||
@@ -311,8 +311,11 @@ const BaseField = ({
|
||||
</span>
|
||||
)}
|
||||
{translatedTooltip && (
|
||||
<Infotip aria-label={translatedTooltip} className="ml-0.5" popupClassName="w-[200px]">
|
||||
{translatedTooltip}
|
||||
<Infotip>
|
||||
<InfotipTrigger aria-label={translatedTooltip} className="ml-0.5" />
|
||||
<InfotipContent aria-label={translatedTooltip} className="w-[200px]">
|
||||
{translatedTooltip}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -2,10 +2,10 @@ import type { LabelProps } from '../label'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Field, FieldItem, FieldLabel } from '@langgenius/dify-ui/field'
|
||||
import { Fieldset, FieldsetLegend } from '@langgenius/dify-ui/fieldset'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { RadioGroup, RadioItem } from '@langgenius/dify-ui/radio-group'
|
||||
import { useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import { TransferMethod } from '@/types/app'
|
||||
import { useFieldContext } from '../..'
|
||||
|
||||
@@ -72,12 +72,11 @@ const UploadMethodField = ({ label, labelOptions, className }: UploadMethodField
|
||||
<span className="ml-1 system-xs-regular text-text-destructive-secondary">*</span>
|
||||
)}
|
||||
{labelOptions?.tooltip && (
|
||||
<Infotip
|
||||
aria-label={labelOptions.tooltip}
|
||||
className="ml-0.5 size-4"
|
||||
popupClassName="w-[200px]"
|
||||
>
|
||||
{labelOptions.tooltip}
|
||||
<Infotip>
|
||||
<InfotipTrigger aria-label={labelOptions.tooltip} className="ml-0.5 size-4" />
|
||||
<InfotipContent aria-label={labelOptions.tooltip} className="w-[200px]">
|
||||
{labelOptions.tooltip}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Infotip } from '../../infotip'
|
||||
|
||||
export type LabelProps = {
|
||||
htmlFor: string
|
||||
@@ -28,8 +28,11 @@ const Label = ({ htmlFor, label, isRequired, showOptional, tooltip, className }:
|
||||
<div className="ml-1 system-xs-regular text-text-destructive-secondary">*</div>
|
||||
)}
|
||||
{tooltip && (
|
||||
<Infotip aria-label={tooltip} className="ml-0.5 size-4" popupClassName="w-[200px]">
|
||||
{tooltip}
|
||||
<Infotip>
|
||||
<InfotipTrigger aria-label={tooltip} className="ml-0.5 size-4" />
|
||||
<InfotipContent aria-label={tooltip} className="w-[200px]">
|
||||
{tooltip}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
import type { ReactNode } from 'react'
|
||||
import { render, screen, waitFor } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import { createPortal } from 'react-dom'
|
||||
import { Infotip } from '../index'
|
||||
|
||||
function ClickBoundary({ children, onClick }: { children: ReactNode; onClick: () => void }) {
|
||||
return (
|
||||
<button type="button" aria-label="Parent action" onClick={onClick}>
|
||||
{createPortal(children, document.body)}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
describe('Infotip', () => {
|
||||
it('should open with keyboard activation', async () => {
|
||||
const user = userEvent.setup()
|
||||
render(<Infotip aria-label="Rate limits">Rate limit details</Infotip>)
|
||||
|
||||
screen.getByRole('button', { name: 'Rate limits' }).focus()
|
||||
await user.keyboard('{Enter}')
|
||||
|
||||
expect(await screen.findByRole('dialog')).toHaveTextContent('Rate limit details')
|
||||
})
|
||||
|
||||
it('should close the dialog with Escape', async () => {
|
||||
const user = userEvent.setup()
|
||||
render(<Infotip aria-label="Rate limits">Rate limit details</Infotip>)
|
||||
|
||||
await user.click(screen.getByRole('button', { name: 'Rate limits' }))
|
||||
expect(await screen.findByRole('dialog')).toBeInTheDocument()
|
||||
|
||||
await user.keyboard('{Escape}')
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByRole('dialog')).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
it('should not propagate trigger clicks', async () => {
|
||||
const user = userEvent.setup()
|
||||
const parentClick = vi.fn()
|
||||
render(
|
||||
<ClickBoundary onClick={parentClick}>
|
||||
<Infotip aria-label="Rate limits">Rate limit details</Infotip>
|
||||
</ClickBoundary>,
|
||||
)
|
||||
|
||||
await user.click(screen.getByRole('button', { name: 'Rate limits' }))
|
||||
|
||||
expect(parentClick).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
@@ -1,106 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import type { MouseEvent, ReactNode } from 'react'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@langgenius/dify-ui/popover'
|
||||
|
||||
const iconClassNames = {
|
||||
question: 'i-ri-question-line',
|
||||
information: 'i-ri-information-line',
|
||||
warning: 'i-ri-error-warning-line',
|
||||
} as const
|
||||
|
||||
const iconSizeClassNames = {
|
||||
small: 'size-3',
|
||||
medium: 'size-3.5',
|
||||
large: 'size-4',
|
||||
} as const
|
||||
|
||||
type InfotipIconVariant = keyof typeof iconClassNames
|
||||
type InfotipIconSize = keyof typeof iconSizeClassNames
|
||||
|
||||
/**
|
||||
* Infotip — an info glyph that reveals a long-form explanation on hover / keyboard activation / tap.
|
||||
*
|
||||
* Implements the pattern Base UI calls an "infotip":
|
||||
* https://base-ui.com/react/components/tooltip#infotips
|
||||
*
|
||||
* > "Popups that open when hovering an info icon should use Popover with the
|
||||
* > `openOnHover` prop on the trigger instead of a tooltip. This way, touch
|
||||
* > users and screen reader users can access the content."
|
||||
*
|
||||
* Use whenever the trigger is an info glyph whose sole purpose is to open a
|
||||
* popup (help text, documentation-style explanation). Do NOT use `Tooltip` for
|
||||
* this — Tooltip is reserved for ephemeral, non-interactive visual labels that
|
||||
* are unreachable on touch devices and by screen readers.
|
||||
*
|
||||
* Base UI rule of thumb:
|
||||
*
|
||||
* > "If the trigger's purpose is to open the popup itself, it's a popover.
|
||||
* > If the trigger's purpose is unrelated to opening the popup, it's a tooltip."
|
||||
*
|
||||
* For hover-revealed supplementary previews of a link / row trigger that has
|
||||
* its own primary click destination, use `PreviewCard` instead.
|
||||
*/
|
||||
|
||||
type InfotipProps = {
|
||||
/** Popup content. Rich nodes are allowed. */
|
||||
children: ReactNode
|
||||
/** Accessible name for the icon-only trigger. */
|
||||
'aria-label': string
|
||||
/** Extra classes on the trigger for contextual layout and color. */
|
||||
className?: string
|
||||
/** Icon glyph. Defaults to `question`. */
|
||||
iconVariant?: InfotipIconVariant
|
||||
/** Icon size. Defaults to `medium` (14px). */
|
||||
iconSize?: InfotipIconSize
|
||||
/** Extra classes on the popup body (width / padding / whitespace overrides). */
|
||||
popupClassName?: string
|
||||
/** Distance between the trigger and popup. */
|
||||
sideOffset?: number
|
||||
}
|
||||
|
||||
export function Infotip({
|
||||
children,
|
||||
'aria-label': ariaLabel,
|
||||
className,
|
||||
iconVariant = 'question',
|
||||
iconSize = 'medium',
|
||||
popupClassName,
|
||||
sideOffset,
|
||||
}: InfotipProps) {
|
||||
const handleClick = (event: MouseEvent<HTMLButtonElement>) => {
|
||||
event.stopPropagation()
|
||||
}
|
||||
|
||||
return (
|
||||
<Popover>
|
||||
<PopoverTrigger
|
||||
openOnHover
|
||||
delay={300}
|
||||
closeDelay={200}
|
||||
aria-label={ariaLabel}
|
||||
onClick={handleClick}
|
||||
className={cn(
|
||||
'inline-flex size-4 shrink-0 cursor-pointer items-center justify-center rounded-sm border-0 bg-transparent p-0 text-text-quaternary outline-hidden hover:text-text-tertiary focus-visible:ring-2 focus-visible:ring-state-accent-solid',
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<span
|
||||
aria-hidden
|
||||
className={cn(iconClassNames[iconVariant], iconSizeClassNames[iconSize])}
|
||||
/>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent
|
||||
placement="top"
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
'max-w-75 rounded-lg bg-components-tooltip-bg px-3 py-2 system-xs-regular text-text-secondary shadow-[0px_12px_16px_-4px_var(--color-shadow-shadow-5),0px_4px_6px_-2px_var(--color-shadow-shadow-1)] backdrop-blur-[5px]',
|
||||
popupClassName,
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
)
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import { Fieldset, FieldsetLegend } from '@langgenius/dify-ui/fieldset'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import {
|
||||
NumberField,
|
||||
NumberFieldControls,
|
||||
@@ -18,7 +19,6 @@ import {
|
||||
SliderTrack,
|
||||
} from '@langgenius/dify-ui/slider'
|
||||
import { Switch } from '@langgenius/dify-ui/switch'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
|
||||
type Props = Readonly<{
|
||||
className?: string
|
||||
@@ -71,8 +71,11 @@ const ParamItem: FC<Props> = ({
|
||||
)}
|
||||
<span className="mr-1 system-sm-semibold text-text-secondary">{name}</span>
|
||||
{!noTooltip && tip && (
|
||||
<Infotip aria-label={tip} popupClassName="w-[200px]">
|
||||
{tip}
|
||||
<Infotip>
|
||||
<InfotipTrigger aria-label={tip} />
|
||||
<InfotipContent aria-label={tip} className="w-[200px]">
|
||||
{tip}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { RiAedFill } from '@remixicon/react'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { useAtomValue } from 'jotai'
|
||||
import { useId } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import { deploymentEditionAtom } from '@/features/system-features/state'
|
||||
import { consoleQuery } from '@/service/console'
|
||||
|
||||
@@ -13,6 +14,7 @@ type PriorityLabelProps = {
|
||||
|
||||
const PriorityLabel = ({ className }: PriorityLabelProps) => {
|
||||
const { t } = useTranslation()
|
||||
const titleId = useId()
|
||||
const deploymentEdition = useAtomValue(deploymentEditionAtom)
|
||||
const { data: plan } = useQuery(
|
||||
consoleQuery.features.get.queryOptions({
|
||||
@@ -33,18 +35,21 @@ const PriorityLabel = ({ className }: PriorityLabelProps) => {
|
||||
)}
|
||||
{t(($) => $[`plansCommon.priority.${label}`], { ns: 'billing' })}
|
||||
</span>
|
||||
<Infotip
|
||||
aria-label={t(($) => $['plansCommon.documentProcessingPriority'], { ns: 'billing' })}
|
||||
>
|
||||
<div className="font-semibold text-text-primary">
|
||||
{t(($) => $['plansCommon.documentProcessingPriority'], { ns: 'billing' })}:{' '}
|
||||
{t(($) => $[`plansCommon.priority.${label}`], { ns: 'billing' })}
|
||||
</div>
|
||||
{label !== 'top-priority' && (
|
||||
<div className="mt-1">
|
||||
{t(($) => $['plansCommon.documentProcessingPriorityTip'], { ns: 'billing' })}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={t(($) => $['plansCommon.documentProcessingPriority'], { ns: 'billing' })}
|
||||
/>
|
||||
<InfotipContent aria-labelledby={titleId}>
|
||||
<div id={titleId} className="font-semibold text-text-primary">
|
||||
{t(($) => $['plansCommon.documentProcessingPriority'], { ns: 'billing' })}:{' '}
|
||||
{t(($) => $[`plansCommon.priority.${label}`], { ns: 'billing' })}
|
||||
</div>
|
||||
)}
|
||||
{label !== 'top-priority' && (
|
||||
<div className="mt-1">
|
||||
{t(($) => $['plansCommon.documentProcessingPriorityTip'], { ns: 'billing' })}
|
||||
</div>
|
||||
)}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
import type { MeterTone } from '@langgenius/dify-ui/meter'
|
||||
import type { ComponentType, FC, ReactNode } from 'react'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { Meter, MeterIndicator, MeterTrack } from '@langgenius/dify-ui/meter'
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
|
||||
import * as React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import { NUM_INFINITE } from '../config'
|
||||
|
||||
type Props = Readonly<{
|
||||
@@ -167,8 +167,11 @@ const UsageInfo: FC<Props> = ({
|
||||
<dt className="flex items-center gap-1 system-xs-medium text-text-tertiary">
|
||||
{name}
|
||||
{tooltip && (
|
||||
<Infotip aria-label={tooltip} popupClassName="w-[180px] max-w-[180px]">
|
||||
{tooltip}
|
||||
<Infotip>
|
||||
<InfotipTrigger aria-label={tooltip} />
|
||||
<InfotipContent aria-label={tooltip} className="w-[180px] max-w-[180px]">
|
||||
{tooltip}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
)}
|
||||
</dt>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import type { FC } from 'react'
|
||||
import type { RetrievalConfig } from '@/types/app'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { RadioGroup } from '@langgenius/dify-ui/radio-group'
|
||||
import { Switch } from '@langgenius/dify-ui/switch'
|
||||
import * as React from 'react'
|
||||
@@ -9,7 +10,6 @@ import { useCallback, useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import WeightedScore from '@/app/components/app/configuration/dataset-config/params-config/weighted-score'
|
||||
import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import ScoreThresholdItem from '@/app/components/base/param-item/score-threshold-item'
|
||||
import TopKItem from '@/app/components/base/param-item/top-k-item'
|
||||
import RadioCard from '@/app/components/base/radio-card'
|
||||
@@ -128,11 +128,16 @@ const RetrievalParamConfig: FC<Props> = ({
|
||||
<span className="mr-0.5 system-sm-semibold text-text-secondary">
|
||||
{t(($) => $['modelProvider.rerankModel.key'], { ns: 'common' })}
|
||||
</span>
|
||||
<Infotip
|
||||
aria-label={t(($) => $['modelProvider.rerankModel.tip'], { ns: 'common' })}
|
||||
popupClassName="w-[200px]"
|
||||
>
|
||||
{t(($) => $['modelProvider.rerankModel.tip'], { ns: 'common' })}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={t(($) => $['modelProvider.rerankModel.tip'], { ns: 'common' })}
|
||||
/>
|
||||
<InfotipContent
|
||||
aria-label={t(($) => $['modelProvider.rerankModel.tip'], { ns: 'common' })}
|
||||
className="w-[200px]"
|
||||
>
|
||||
{t(($) => $['modelProvider.rerankModel.tip'], { ns: 'common' })}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+11
-6
@@ -1,11 +1,11 @@
|
||||
import type { AppIconType } from '@/types/app'
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { RiAddLine, RiCloseLine } from '@remixicon/react'
|
||||
import * as React from 'react'
|
||||
import { useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import AppIcon from '@/app/components/base/app-icon'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import { LoadingPlaceholder } from '@/app/components/base/loading-placeholder'
|
||||
import WorkflowPreview from '@/app/components/workflow/workflow-preview'
|
||||
import { usePipelineTemplateById } from '@/service/use-pipeline'
|
||||
@@ -99,11 +99,16 @@ const Details = ({ id, type, onApplyTemplate, onClose }: DetailsProps) => {
|
||||
<span className="system-sm-semibold-uppercase text-text-secondary">
|
||||
{t(($) => $['details.structure'], { ns: 'datasetPipeline' })}
|
||||
</span>
|
||||
<Infotip
|
||||
aria-label={t(($) => $['details.structureTooltip'], { ns: 'datasetPipeline' })}
|
||||
popupClassName="max-w-[240px]"
|
||||
>
|
||||
{t(($) => $['details.structureTooltip'], { ns: 'datasetPipeline' })}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={t(($) => $['details.structureTooltip'], { ns: 'datasetPipeline' })}
|
||||
/>
|
||||
<InfotipContent
|
||||
aria-label={t(($) => $['details.structureTooltip'], { ns: 'datasetPipeline' })}
|
||||
className="max-w-[240px]"
|
||||
>
|
||||
{t(($) => $['details.structureTooltip'], { ns: 'datasetPipeline' })}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
<ChunkStructureCard {...chunkStructureConfig[pipelineTemplateInfo.chunk_structure]} />
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { FC } from 'react'
|
||||
import type { IndexingStatusResponse } from '@/models/datasets'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { RiCheckboxCircleFill } from '@remixicon/react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import NotionIcon from '@/app/components/base/notion-icon'
|
||||
import PriorityLabel from '@/app/components/billing/priority-label'
|
||||
import { DataSourceType } from '@/models/datasets'
|
||||
@@ -28,15 +28,20 @@ const StatusIcon: FC<{ status: string; error?: string }> = ({ status, error }) =
|
||||
const errorLabel = error || t(($) => $.error, { ns: 'common' })
|
||||
|
||||
return (
|
||||
<Infotip
|
||||
aria-label={t(($) => $.error, { ns: 'common' })}
|
||||
iconVariant="warning"
|
||||
iconSize="large"
|
||||
className="text-text-destructive"
|
||||
popupClassName="wrap-anywhere whitespace-pre-wrap"
|
||||
sideOffset={4}
|
||||
>
|
||||
{errorLabel}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={t(($) => $.error, { ns: 'common' })}
|
||||
iconVariant="warning"
|
||||
iconSize="large"
|
||||
className="text-text-destructive"
|
||||
/>
|
||||
<InfotipContent
|
||||
aria-label={t(($) => $.error, { ns: 'common' })}
|
||||
className="wrap-anywhere whitespace-pre-wrap"
|
||||
sideOffset={4}
|
||||
>
|
||||
{errorLabel}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
)
|
||||
}
|
||||
|
||||
+11
-6
@@ -7,11 +7,11 @@ import type {
|
||||
} from '@/models/datasets'
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import { Checkbox } from '@langgenius/dify-ui/checkbox'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { Separator } from '@langgenius/dify-ui/separator'
|
||||
import { RiAlertFill, RiSearchEyeLine } from '@remixicon/react'
|
||||
import { useSuspenseQuery } from '@tanstack/react-query'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import SummaryIndexSetting from '@/app/components/datasets/settings/summary-index-setting'
|
||||
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
|
||||
import { ChunkingMode } from '@/models/datasets'
|
||||
@@ -189,11 +189,16 @@ export const GeneralChunkingOptions: FC<GeneralChunkingOptionsProps> = ({
|
||||
onSelect={onDocLanguageChange}
|
||||
disabled={currentDocForm !== ChunkingMode.qa}
|
||||
/>
|
||||
<Infotip
|
||||
aria-label={t(($) => $['stepTwo.QATip'], { ns: 'datasetCreation' })}
|
||||
className="size-3.5"
|
||||
>
|
||||
{t(($) => $['stepTwo.QATip'], { ns: 'datasetCreation' })}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={t(($) => $['stepTwo.QATip'], { ns: 'datasetCreation' })}
|
||||
className="size-3.5"
|
||||
/>
|
||||
<InfotipContent
|
||||
aria-label={t(($) => $['stepTwo.QATip'], { ns: 'datasetCreation' })}
|
||||
>
|
||||
{t(($) => $['stepTwo.QATip'], { ns: 'datasetCreation' })}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
{currentDocForm === ChunkingMode.qa && (
|
||||
|
||||
@@ -6,6 +6,7 @@ import type {
|
||||
} from '@langgenius/dify-ui/number-field'
|
||||
import type { FC, PropsWithChildren, ReactNode } from 'react'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { Input } from '@langgenius/dify-ui/input'
|
||||
import {
|
||||
NumberField,
|
||||
@@ -18,7 +19,6 @@ import {
|
||||
} from '@langgenius/dify-ui/number-field'
|
||||
import { useId, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import { env } from '@/env'
|
||||
|
||||
const FormField: FC<PropsWithChildren<{ label: ReactNode }>> = (props) => {
|
||||
@@ -54,11 +54,16 @@ export const DelimiterInput: FC<
|
||||
<label htmlFor={inputId} className="mr-0.5 system-sm-semibold">
|
||||
{t(($) => $['stepTwo.separator'], { ns: 'datasetCreation' })}
|
||||
</label>
|
||||
<Infotip
|
||||
aria-label={tooltip || t(($) => $['stepTwo.separatorTip'], { ns: 'datasetCreation' })}
|
||||
popupClassName="max-w-[200px]"
|
||||
>
|
||||
{tooltip || t(($) => $['stepTwo.separatorTip'], { ns: 'datasetCreation' })}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={tooltip || t(($) => $['stepTwo.separatorTip'], { ns: 'datasetCreation' })}
|
||||
/>
|
||||
<InfotipContent
|
||||
aria-label={tooltip || t(($) => $['stepTwo.separatorTip'], { ns: 'datasetCreation' })}
|
||||
className="max-w-[200px]"
|
||||
>
|
||||
{tooltip || t(($) => $['stepTwo.separatorTip'], { ns: 'datasetCreation' })}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
}
|
||||
@@ -185,11 +190,16 @@ export const OverlapInput: FC<LabeledCompoundNumberInputProps> = (props) => {
|
||||
label={
|
||||
<div className="mb-1 flex items-center">
|
||||
<span className="system-sm-semibold">{label}</span>
|
||||
<Infotip
|
||||
aria-label={t(($) => $['stepTwo.overlapTip'], { ns: 'datasetCreation' })}
|
||||
popupClassName="max-w-[200px]"
|
||||
>
|
||||
{t(($) => $['stepTwo.overlapTip'], { ns: 'datasetCreation' })}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={t(($) => $['stepTwo.overlapTip'], { ns: 'datasetCreation' })}
|
||||
/>
|
||||
<InfotipContent
|
||||
aria-label={t(($) => $['stepTwo.overlapTip'], { ns: 'datasetCreation' })}
|
||||
className="max-w-[200px]"
|
||||
>
|
||||
{t(($) => $['stepTwo.overlapTip'], { ns: 'datasetCreation' })}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use client'
|
||||
import { Checkbox } from '@langgenius/dify-ui/checkbox'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
|
||||
type Props = Readonly<{
|
||||
className?: string
|
||||
@@ -35,8 +35,11 @@ export default function CheckboxWithLabel({
|
||||
</label>
|
||||
<div className="ml-1 flex min-w-0 items-center">
|
||||
{tooltip && (
|
||||
<Infotip aria-label={tooltip} popupClassName="w-[200px]">
|
||||
{tooltip}
|
||||
<Infotip>
|
||||
<InfotipTrigger aria-label={tooltip} />
|
||||
<InfotipContent aria-label={tooltip} className="w-[200px]">
|
||||
{tooltip}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import * as React from 'react'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import Input from './text-input'
|
||||
|
||||
type Props = Readonly<{
|
||||
@@ -43,8 +43,11 @@ const Field: FC<Props> = ({
|
||||
<span className="ml-0.5 text-xs font-semibold text-text-destructive">*</span>
|
||||
)}
|
||||
{tooltip && (
|
||||
<Infotip aria-label={tooltip} className="ml-0.5" popupClassName="w-[200px]">
|
||||
{tooltip}
|
||||
<Infotip>
|
||||
<InfotipTrigger aria-label={tooltip} className="ml-0.5" />
|
||||
<InfotipContent aria-label={tooltip} className="w-[200px]">
|
||||
{tooltip}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
)}
|
||||
</div>
|
||||
|
||||
+6
-3
@@ -1,7 +1,7 @@
|
||||
'use client'
|
||||
import { Checkbox } from '@langgenius/dify-ui/checkbox'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
|
||||
type CheckboxWithLabelProps = {
|
||||
className?: string
|
||||
@@ -35,8 +35,11 @@ export default function CheckboxWithLabel({
|
||||
</label>
|
||||
<div className="ml-1 flex min-w-0 items-center">
|
||||
{tooltip && (
|
||||
<Infotip aria-label={tooltip} popupClassName="w-[200px]">
|
||||
{tooltip}
|
||||
<Infotip>
|
||||
<InfotipTrigger aria-label={tooltip} />
|
||||
<InfotipContent aria-label={tooltip} className="w-[200px]">
|
||||
{tooltip}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { OperationName } from '../types'
|
||||
import type { CommonResponse } from '@/models/common'
|
||||
import type { DocumentDisplayStatus } from '@/models/datasets'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { StatusDot } from '@langgenius/dify-ui/status-dot'
|
||||
import { Switch } from '@langgenius/dify-ui/switch'
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
|
||||
@@ -10,7 +11,6 @@ import { useDebounceFn } from 'ahooks'
|
||||
import * as React from 'react'
|
||||
import { useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import { toast } from '@/app/notifications'
|
||||
import {
|
||||
useDocumentDelete,
|
||||
@@ -109,12 +109,11 @@ const StatusItem = ({
|
||||
{statusItem.text}
|
||||
</span>
|
||||
{errorMessage && (
|
||||
<Infotip
|
||||
aria-label={errorMessage}
|
||||
className="ml-1"
|
||||
popupClassName="max-w-[260px] break-all"
|
||||
>
|
||||
{errorMessage}
|
||||
<Infotip>
|
||||
<InfotipTrigger aria-label={errorMessage} className="ml-1" />
|
||||
<InfotipContent aria-label={errorMessage} className="max-w-[260px] break-all">
|
||||
{errorMessage}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
)}
|
||||
{scene === 'detail' && (
|
||||
|
||||
@@ -5,6 +5,7 @@ import { Button } from '@langgenius/dify-ui/button'
|
||||
import { Checkbox } from '@langgenius/dify-ui/checkbox'
|
||||
import { Dialog, DialogClose, DialogContent, DialogTitle } from '@langgenius/dify-ui/dialog'
|
||||
import { IconButton } from '@langgenius/dify-ui/icon-button'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { Separator } from '@langgenius/dify-ui/separator'
|
||||
import { produce } from 'immer'
|
||||
import * as React from 'react'
|
||||
@@ -12,7 +13,6 @@ import { useCallback, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { toast } from '@/app/notifications'
|
||||
import { useCreateMetaData } from '@/service/knowledge/use-metadata'
|
||||
import { Infotip } from '../../../base/infotip'
|
||||
import useCheckMetadataName from '../hooks/use-check-metadata-name'
|
||||
import { DatasetMetadataPicker } from '../metadata-dataset/dataset-metadata-picker'
|
||||
import { UpdateType } from '../types'
|
||||
@@ -204,14 +204,21 @@ const EditMetadataBatchModal: FC<Props> = ({
|
||||
{t(($) => $[`${i18nPrefix}.applyToAllSelectDocument`], { ns: 'dataset' })}
|
||||
</span>
|
||||
</label>
|
||||
<Infotip
|
||||
aria-label={t(($) => $[`${i18nPrefix}.applyToAllSelectDocumentTip`], {
|
||||
ns: 'dataset',
|
||||
})}
|
||||
className="p-px text-text-tertiary"
|
||||
popupClassName="max-w-[240px]"
|
||||
>
|
||||
{t(($) => $[`${i18nPrefix}.applyToAllSelectDocumentTip`], { ns: 'dataset' })}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={t(($) => $[`${i18nPrefix}.applyToAllSelectDocumentTip`], {
|
||||
ns: 'dataset',
|
||||
})}
|
||||
className="p-px text-text-tertiary"
|
||||
/>
|
||||
<InfotipContent
|
||||
aria-label={t(($) => $[`${i18nPrefix}.applyToAllSelectDocumentTip`], {
|
||||
ns: 'dataset',
|
||||
})}
|
||||
className="max-w-[240px]"
|
||||
>
|
||||
{t(($) => $[`${i18nPrefix}.applyToAllSelectDocumentTip`], { ns: 'dataset' })}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
DrawerTitle,
|
||||
DrawerViewport,
|
||||
} from '@langgenius/dify-ui/drawer'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { Input } from '@langgenius/dify-ui/input'
|
||||
import { Switch } from '@langgenius/dify-ui/switch'
|
||||
import { RiAddLine, RiDeleteBinLine, RiEditLine } from '@remixicon/react'
|
||||
@@ -30,7 +31,6 @@ import { useBoolean, useHover } from 'ahooks'
|
||||
import * as React from 'react'
|
||||
import { useCallback, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import { CreateMetadataModal } from '@/app/components/datasets/metadata/metadata-dataset/create-metadata-modal'
|
||||
import { toast } from '@/app/notifications'
|
||||
import { getIconClassName } from '../utils/get-icon'
|
||||
@@ -265,11 +265,20 @@ const DatasetMetadataDrawer: FC<Props> = ({
|
||||
<div className="mr-0.5 ml-2 system-sm-semibold text-text-secondary">
|
||||
{t(($) => $[`${i18nPrefix}.builtIn`], { ns: 'dataset' })}
|
||||
</div>
|
||||
<Infotip
|
||||
aria-label={t(($) => $[`${i18nPrefix}.builtInDescription`], { ns: 'dataset' })}
|
||||
popupClassName="max-w-[100px]"
|
||||
>
|
||||
{t(($) => $[`${i18nPrefix}.builtInDescription`], { ns: 'dataset' })}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={t(($) => $[`${i18nPrefix}.builtInDescription`], {
|
||||
ns: 'dataset',
|
||||
})}
|
||||
/>
|
||||
<InfotipContent
|
||||
aria-label={t(($) => $[`${i18nPrefix}.builtInDescription`], {
|
||||
ns: 'dataset',
|
||||
})}
|
||||
className="max-w-[100px]"
|
||||
>
|
||||
{t(($) => $[`${i18nPrefix}.builtInDescription`], { ns: 'dataset' })}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
import type { FC } from 'react'
|
||||
import type { BuiltInMetadataItem, MetadataItemWithValue } from '../types'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { Separator } from '@langgenius/dify-ui/separator'
|
||||
import { RiDeleteBinLine } from '@remixicon/react'
|
||||
import * as React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import useTimestamp from '@/hooks/use-timestamp'
|
||||
import { useRouter } from '@/next/navigation'
|
||||
import InputCombined from '../edit-metadata-batch/input-combined'
|
||||
@@ -70,8 +70,11 @@ const InfoGroup: FC<Props> = ({
|
||||
{title}
|
||||
</div>
|
||||
{titleTooltip && (
|
||||
<Infotip aria-label={titleTooltip} popupClassName="max-w-[240px]">
|
||||
{titleTooltip}
|
||||
<Infotip>
|
||||
<InfotipTrigger aria-label={titleTooltip} />
|
||||
<InfotipContent aria-label={titleTooltip} className="max-w-[240px]">
|
||||
{titleTooltip}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Fieldset, FieldsetLegend } from '@langgenius/dify-ui/fieldset'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import {
|
||||
NumberField,
|
||||
NumberFieldControls,
|
||||
@@ -18,7 +19,6 @@ import {
|
||||
import * as React from 'react'
|
||||
import { useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
|
||||
const MIN_KEYWORD_NUMBER = 0
|
||||
const MAX_KEYWORD_NUMBER = 50
|
||||
@@ -44,8 +44,9 @@ const KeyWordNumber = ({ keywordNumber, onKeywordNumberChange }: KeyWordNumberPr
|
||||
<FieldsetLegend className="sr-only">{label}</FieldsetLegend>
|
||||
<div className="flex grow items-center gap-x-0.5">
|
||||
<div className="truncate system-xs-medium text-text-secondary">{label}</div>
|
||||
<Infotip aria-label={label} className="size-3.5">
|
||||
{label}
|
||||
<Infotip>
|
||||
<InfotipTrigger aria-label={label} className="size-3.5" />
|
||||
<InfotipContent aria-label={label}>{label}</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
<Slider
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import type { DefaultModel } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import type { SummaryIndexSetting as SummaryIndexSettingType } from '@/models/datasets'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { Switch } from '@langgenius/dify-ui/switch'
|
||||
import { Textarea } from '@langgenius/dify-ui/textarea'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { memo, useCallback, useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import { ModelSelector } from '@/app/components/header/account-setting/model-provider-page/model-selector'
|
||||
import { consoleQuery } from '@/service/console'
|
||||
@@ -73,11 +73,16 @@ const SummaryIndexSetting = ({
|
||||
<div className="flex h-6 items-center justify-between">
|
||||
<div className="flex items-center system-sm-semibold-uppercase text-text-secondary">
|
||||
{t(($) => $['form.summaryAutoGen'], { ns: 'datasetSettings' })}
|
||||
<Infotip
|
||||
aria-label={t(($) => $['form.summaryAutoGenTip'], { ns: 'datasetSettings' })}
|
||||
className="ml-1"
|
||||
>
|
||||
{t(($) => $['form.summaryAutoGenTip'], { ns: 'datasetSettings' })}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={t(($) => $['form.summaryAutoGenTip'], { ns: 'datasetSettings' })}
|
||||
className="ml-1"
|
||||
/>
|
||||
<InfotipContent
|
||||
aria-label={t(($) => $['form.summaryAutoGenTip'], { ns: 'datasetSettings' })}
|
||||
>
|
||||
{t(($) => $['form.summaryAutoGenTip'], { ns: 'datasetSettings' })}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
<Switch
|
||||
|
||||
@@ -13,6 +13,7 @@ import type { NodeOutPutVar } from '@/app/components/workflow/types'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Field, FieldItem, FieldLabel } from '@langgenius/dify-ui/field'
|
||||
import { Fieldset, FieldsetLegend } from '@langgenius/dify-ui/fieldset'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { Radio, RadioGroup } from '@langgenius/dify-ui/radio-group'
|
||||
import {
|
||||
Select,
|
||||
@@ -24,7 +25,6 @@ import {
|
||||
SelectTrigger,
|
||||
} from '@langgenius/dify-ui/select'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import { AppSelector } from '@/app/components/plugins/plugin-detail-panel/app-selector'
|
||||
import ModelParameterModal from '@/app/components/plugins/plugin-detail-panel/model-selector'
|
||||
import MultipleToolSelector from '@/app/components/plugins/plugin-detail-panel/multiple-tool-selector'
|
||||
@@ -158,8 +158,11 @@ function Form<
|
||||
const infotip = formSchema.tooltip
|
||||
const infotipText = infotip?.[language] || infotip?.en_US
|
||||
const infotipContent = infotipText && (
|
||||
<Infotip aria-label={infotipText} className="ml-1" popupClassName="w-[200px] max-w-[200px]">
|
||||
{infotipText}
|
||||
<Infotip>
|
||||
<InfotipTrigger aria-label={infotipText} className="ml-1" />
|
||||
<InfotipContent aria-label={infotipText} className="w-[200px] max-w-[200px]">
|
||||
{infotipText}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
)
|
||||
if (override) {
|
||||
|
||||
+12
-7
@@ -3,6 +3,7 @@ import type { Node, NodeOutPutVar } from '@/app/components/workflow/types'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Field, FieldItem, FieldLabel } from '@langgenius/dify-ui/field'
|
||||
import { Fieldset, FieldsetLegend } from '@langgenius/dify-ui/fieldset'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { Radio, RadioGroup } from '@langgenius/dify-ui/radio-group'
|
||||
import {
|
||||
Select,
|
||||
@@ -25,7 +26,6 @@ import {
|
||||
import { Switch } from '@langgenius/dify-ui/switch'
|
||||
import { useEffect, useId, useMemo, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import PromptEditor from '@/app/components/base/prompt-editor'
|
||||
import TagInput from '@/app/components/base/tag-input'
|
||||
import { BlockEnum } from '@/app/components/workflow/types'
|
||||
@@ -447,12 +447,17 @@ function ParameterItem({
|
||||
{sliderLabel}
|
||||
</div>
|
||||
{parameterRule.help && (
|
||||
<Infotip
|
||||
aria-label={parameterRule.help[language] || parameterRule.help.en_US}
|
||||
className="mr-1"
|
||||
popupClassName="w-[150px] whitespace-pre-wrap"
|
||||
>
|
||||
{parameterRule.help[language] || parameterRule.help.en_US}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={parameterRule.help[language] || parameterRule.help.en_US}
|
||||
className="mr-1"
|
||||
/>
|
||||
<InfotipContent
|
||||
aria-label={parameterRule.help[language] || parameterRule.help.en_US}
|
||||
className="w-[150px] whitespace-pre-wrap"
|
||||
>
|
||||
{parameterRule.help[language] || parameterRule.help.en_US}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
)}
|
||||
</div>
|
||||
|
||||
+5
-2
@@ -1,7 +1,7 @@
|
||||
import type { UsagePriority } from '../use-credential-panel-state'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import { PreferredProviderTypeEnum } from '../../declarations'
|
||||
|
||||
type UsagePrioritySectionProps = {
|
||||
@@ -38,7 +38,10 @@ export default function UsagePrioritySection({
|
||||
<span className="truncate system-sm-medium text-text-secondary">
|
||||
{t(($) => $['modelProvider.card.usagePriority'], { ns: 'common' })}
|
||||
</span>
|
||||
<Infotip aria-label={usagePriorityTip}>{usagePriorityTip}</Infotip>
|
||||
<Infotip>
|
||||
<InfotipTrigger aria-label={usagePriorityTip} />
|
||||
<InfotipContent aria-label={usagePriorityTip}>{usagePriorityTip}</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
<div className="flex shrink-0 items-center gap-1">
|
||||
{options.map((option) => {
|
||||
|
||||
+12
-7
@@ -9,6 +9,7 @@ import type {
|
||||
ModelProvider,
|
||||
} from '../declarations'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { StatusDot } from '@langgenius/dify-ui/status-dot'
|
||||
import { Switch } from '@langgenius/dify-ui/switch'
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
|
||||
@@ -17,7 +18,6 @@ import { useCallback, useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Badge from '@/app/components/base/badge/index'
|
||||
import GridMask from '@/app/components/base/grid-mask'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import UpgradeBtn from '@/app/components/billing/upgrade-btn'
|
||||
import s from '@/app/components/custom/style.module.css'
|
||||
import { AddCredentialInLoadBalancing } from '@/app/components/header/account-setting/model-provider-page/model-auth'
|
||||
@@ -188,12 +188,17 @@ const ModelLoadBalancingConfigs = ({
|
||||
<div className="grow">
|
||||
<div className="flex items-center gap-1 text-sm text-text-primary">
|
||||
{t(($) => $['modelProvider.loadBalancing'], { ns: 'common' })}
|
||||
<Infotip
|
||||
aria-label={t(($) => $['modelProvider.loadBalancingInfo'], { ns: 'common' })}
|
||||
className="size-3"
|
||||
iconSize="small"
|
||||
>
|
||||
{t(($) => $['modelProvider.loadBalancingInfo'], { ns: 'common' })}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={t(($) => $['modelProvider.loadBalancingInfo'], { ns: 'common' })}
|
||||
className="size-3"
|
||||
iconSize="small"
|
||||
/>
|
||||
<InfotipContent
|
||||
aria-label={t(($) => $['modelProvider.loadBalancingInfo'], { ns: 'common' })}
|
||||
>
|
||||
{t(($) => $['modelProvider.loadBalancingInfo'], { ns: 'common' })}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
<div className="text-xs text-text-tertiary">
|
||||
|
||||
+6
-7
@@ -4,12 +4,12 @@ import { Button } from '@langgenius/dify-ui/button'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Dialog, DialogClose, DialogContent, DialogTitle } from '@langgenius/dify-ui/dialog'
|
||||
import { IconButton } from '@langgenius/dify-ui/icon-button'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { useAtomValue } from 'jotai'
|
||||
import { parseAsStringLiteral, useQueryState } from 'nuqs'
|
||||
import { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import { toast } from '@/app/notifications'
|
||||
import { workspacePermissionKeysAtom } from '@/context/permission-state'
|
||||
import { consoleClient, consoleQuery } from '@/service/console'
|
||||
@@ -214,12 +214,11 @@ const SystemModel: FC<SystemModelSelectorProps> = ({
|
||||
return (
|
||||
<div className="flex min-h-6 items-center text-[13px] font-medium text-text-secondary">
|
||||
{t(($) => $[labelKey], { ns: 'common' })}
|
||||
<Infotip
|
||||
aria-label={tipText}
|
||||
className="ml-0.5 text-text-tertiary"
|
||||
popupClassName="w-[261px]"
|
||||
>
|
||||
{tipText}
|
||||
<Infotip>
|
||||
<InfotipTrigger aria-label={tipText} className="ml-0.5 text-text-tertiary" />
|
||||
<InfotipContent aria-label={tipText} className="w-[261px]">
|
||||
{tipText}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -7,11 +7,11 @@ import type {
|
||||
} from '@dify/contracts/api/console/workflow-run-archives/types.gen'
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { skipToken, useMutation, useQuery, useSuspenseQuery } from '@tanstack/react-query'
|
||||
import { useQueryState } from 'nuqs'
|
||||
import { useEffect, useId, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import { SkeletonRectangle } from '@/app/components/base/skeleton'
|
||||
import {
|
||||
pricingQueryParamName,
|
||||
@@ -422,12 +422,17 @@ function WorkflowArchiveMonthRow({ archive }: { archive: WorkflowRunArchiveMonth
|
||||
)}
|
||||
<span id={downloadActionLabelId}>{buttonContent}</span>
|
||||
</Button>
|
||||
<Infotip
|
||||
aria-label={`${t(($) => $['operation.learnMore'], { ns: 'common' })}: ${archiveMonth}`}
|
||||
iconVariant={isFailed ? 'warning' : 'information'}
|
||||
popupClassName="wrap-anywhere whitespace-pre-wrap"
|
||||
>
|
||||
{downloadHint}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={`${t(($) => $['operation.learnMore'], { ns: 'common' })}: ${archiveMonth}`}
|
||||
iconVariant={isFailed ? 'warning' : 'information'}
|
||||
/>
|
||||
<InfotipContent
|
||||
aria-label={`${t(($) => $['operation.learnMore'], { ns: 'common' })}: ${archiveMonth}`}
|
||||
className="wrap-anywhere whitespace-pre-wrap"
|
||||
>
|
||||
{downloadHint}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
@@ -4,10 +4,10 @@ import type { NodeOutPutVar } from '@/app/components/workflow/types'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Collapsible, CollapsiblePanel, CollapsibleTrigger } from '@langgenius/dify-ui/collapsible'
|
||||
import { IconButton } from '@langgenius/dify-ui/icon-button'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { Separator } from '@langgenius/dify-ui/separator'
|
||||
import * as React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import ToolSelector from '@/app/components/plugins/plugin-detail-panel/tool-selector'
|
||||
import { useMCPToolAvailability } from '@/app/components/workflow/nodes/_base/components/mcp-tool-availability'
|
||||
import { useRefWithInit } from '@/hooks/use-ref-with-init'
|
||||
@@ -153,11 +153,14 @@ const MultipleToolSelector = ({
|
||||
</>
|
||||
)}
|
||||
{tooltip ? (
|
||||
<Infotip
|
||||
aria-label={typeof tooltip === 'string' ? tooltip : label}
|
||||
className="size-3.5"
|
||||
>
|
||||
{tooltip}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={typeof tooltip === 'string' ? tooltip : label}
|
||||
className="size-3.5"
|
||||
/>
|
||||
<InfotipContent aria-label={typeof tooltip === 'string' ? tooltip : label}>
|
||||
{tooltip}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
@@ -2,6 +2,7 @@ import type { TriggerSubscriptionBuilder } from '@/app/components/workflow/block
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { IconButton } from '@langgenius/dify-ui/icon-button'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
@@ -14,7 +15,6 @@ import { useBoolean } from 'ahooks'
|
||||
import { useCallback, useMemo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Badge from '@/app/components/base/badge'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import { toast } from '@/app/notifications'
|
||||
import { openOAuthPopup } from '@/hooks/use-oauth'
|
||||
import {
|
||||
@@ -184,13 +184,20 @@ export const CreateSubscriptionButton = ({
|
||||
ns: 'pluginTrigger',
|
||||
}),
|
||||
extra: (
|
||||
<Infotip
|
||||
aria-label={t(($) => $['subscription.addType.options.manual.tip'], {
|
||||
ns: 'pluginTrigger',
|
||||
})}
|
||||
className="size-3.5"
|
||||
>
|
||||
{t(($) => $['subscription.addType.options.manual.tip'], { ns: 'pluginTrigger' })}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={t(($) => $['subscription.addType.options.manual.tip'], {
|
||||
ns: 'pluginTrigger',
|
||||
})}
|
||||
className="size-3.5"
|
||||
/>
|
||||
<InfotipContent
|
||||
aria-label={t(($) => $['subscription.addType.options.manual.tip'], {
|
||||
ns: 'pluginTrigger',
|
||||
})}
|
||||
>
|
||||
{t(($) => $['subscription.addType.options.manual.tip'], { ns: 'pluginTrigger' })}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
),
|
||||
show: supportedMethods.includes(SupportedCreationMethods.MANUAL),
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
'use client'
|
||||
import type { PluginDetail } from '@/app/components/plugins/types'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import * as React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import { CreateSubscriptionButton } from './create'
|
||||
import { CreateButtonType } from './create/types'
|
||||
import SubscriptionCard from './subscription-card'
|
||||
@@ -31,11 +31,16 @@ export const SubscriptionListView: React.FC<SubscriptionListViewProps> = ({
|
||||
<span className="system-sm-semibold-uppercase text-text-secondary">
|
||||
{t(($) => $['subscription.listNum'], { ns: 'pluginTrigger', num: subscriptionCount })}
|
||||
</span>
|
||||
<Infotip
|
||||
aria-label={t(($) => $['subscription.list.tip'], { ns: 'pluginTrigger' })}
|
||||
className="size-3.5"
|
||||
>
|
||||
{t(($) => $['subscription.list.tip'], { ns: 'pluginTrigger' })}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={t(($) => $['subscription.list.tip'], { ns: 'pluginTrigger' })}
|
||||
className="size-3.5"
|
||||
/>
|
||||
<InfotipContent
|
||||
aria-label={t(($) => $['subscription.list.tip'], { ns: 'pluginTrigger' })}
|
||||
>
|
||||
{t(($) => $['subscription.list.tip'], { ns: 'pluginTrigger' })}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
import type { TriggerSubscription } from '@/app/components/workflow/block-selector/types'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { IconButton } from '@langgenius/dify-ui/icon-button'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import * as React from 'react'
|
||||
import { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import { CreateSubscriptionButton } from './create'
|
||||
import { CreateButtonType } from './create/types'
|
||||
import { DeleteConfirm } from './delete-confirm'
|
||||
@@ -33,11 +33,16 @@ export const SubscriptionSelectorView: React.FC<SubscriptionSelectorProps> = ({
|
||||
<span className="system-sm-semibold-uppercase text-text-secondary">
|
||||
{t(($) => $['subscription.listNum'], { ns: 'pluginTrigger', num: subscriptionCount })}
|
||||
</span>
|
||||
<Infotip
|
||||
aria-label={t(($) => $['subscription.list.tip'], { ns: 'pluginTrigger' })}
|
||||
className="size-3.5"
|
||||
>
|
||||
{t(($) => $['subscription.list.tip'], { ns: 'pluginTrigger' })}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={t(($) => $['subscription.list.tip'], { ns: 'pluginTrigger' })}
|
||||
className="size-3.5"
|
||||
/>
|
||||
<InfotipContent
|
||||
aria-label={t(($) => $['subscription.list.tip'], { ns: 'pluginTrigger' })}
|
||||
>
|
||||
{t(($) => $['subscription.list.tip'], { ns: 'pluginTrigger' })}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
<CreateSubscriptionButton buttonType={CreateButtonType.ICON_BUTTON} shape="circle" />
|
||||
|
||||
+6
-3
@@ -4,6 +4,7 @@ import type { ToolFormSchema } from '@/app/components/tools/utils/to-form-schema
|
||||
import type { SchemaRoot } from '@/app/components/workflow/nodes/llm/types'
|
||||
import type { NodeOutPutVar, ValueSelector } from '@/app/components/workflow/types'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { Input } from '@langgenius/dify-ui/input'
|
||||
import {
|
||||
Select,
|
||||
@@ -19,7 +20,6 @@ import { useSuspenseQuery } from '@tanstack/react-query'
|
||||
import { useBoolean } from 'ahooks'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||
import { AppSelector } from '@/app/components/plugins/plugin-detail-panel/app-selector'
|
||||
@@ -148,8 +148,11 @@ const ReasoningConfigForm: React.FC<Props> = ({
|
||||
const fieldTitle = getFieldTitle(label, language)
|
||||
const tooltipText = tooltip?.[language] || tooltip?.en_US
|
||||
const tooltipContent = tooltipText && (
|
||||
<Infotip aria-label={tooltipText} className="ml-0.5 size-4" popupClassName="w-[200px]">
|
||||
{tooltipText}
|
||||
<Infotip>
|
||||
<InfotipTrigger aria-label={tooltipText} className="ml-0.5 size-4" />
|
||||
<InfotipContent aria-label={tooltipText} className="w-[200px]">
|
||||
{tooltipText}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
)
|
||||
const varInput = value[variable]!.value
|
||||
|
||||
@@ -3,12 +3,12 @@ import type { Node } from '@/app/components/workflow/types'
|
||||
import type { InputVar, RAGPipelineVariables } from '@/models/pipeline'
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { Separator } from '@langgenius/dify-ui/separator'
|
||||
import { RiCloseLine, RiEyeLine } from '@remixicon/react'
|
||||
import { memo, useCallback, useMemo, useRef } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useNodes } from 'reactflow'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import { useHooksStore } from '@/app/components/workflow/hooks-store'
|
||||
import { useNodesSyncDraft } from '@/app/components/workflow/hooks/use-nodes-sync-draft'
|
||||
import { useStore } from '@/app/components/workflow/store'
|
||||
@@ -133,13 +133,20 @@ const InputFieldPanel = () => {
|
||||
<span className="system-sm-semibold-uppercase text-text-secondary">
|
||||
{t(($) => $['inputFieldPanel.uniqueInputs.title'], { ns: 'datasetPipeline' })}
|
||||
</span>
|
||||
<Infotip
|
||||
aria-label={t(($) => $['inputFieldPanel.uniqueInputs.tooltip'], {
|
||||
ns: 'datasetPipeline',
|
||||
})}
|
||||
popupClassName="max-w-[240px]"
|
||||
>
|
||||
{t(($) => $['inputFieldPanel.uniqueInputs.tooltip'], { ns: 'datasetPipeline' })}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={t(($) => $['inputFieldPanel.uniqueInputs.tooltip'], {
|
||||
ns: 'datasetPipeline',
|
||||
})}
|
||||
/>
|
||||
<InfotipContent
|
||||
aria-label={t(($) => $['inputFieldPanel.uniqueInputs.tooltip'], {
|
||||
ns: 'datasetPipeline',
|
||||
})}
|
||||
className="max-w-[240px]"
|
||||
>
|
||||
{t(($) => $['inputFieldPanel.uniqueInputs.tooltip'], { ns: 'datasetPipeline' })}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
<div className="flex flex-col gap-y-1 py-1">
|
||||
|
||||
+15
-6
@@ -1,6 +1,6 @@
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import * as React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
|
||||
const GlobalInputs = () => {
|
||||
const { t } = useTranslation()
|
||||
@@ -10,11 +10,20 @@ const GlobalInputs = () => {
|
||||
<span className="system-sm-semibold-uppercase text-text-secondary">
|
||||
{t(($) => $['inputFieldPanel.globalInputs.title'], { ns: 'datasetPipeline' })}
|
||||
</span>
|
||||
<Infotip
|
||||
aria-label={t(($) => $['inputFieldPanel.globalInputs.tooltip'], { ns: 'datasetPipeline' })}
|
||||
popupClassName="w-[240px]"
|
||||
>
|
||||
{t(($) => $['inputFieldPanel.globalInputs.tooltip'], { ns: 'datasetPipeline' })}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={t(($) => $['inputFieldPanel.globalInputs.tooltip'], {
|
||||
ns: 'datasetPipeline',
|
||||
})}
|
||||
/>
|
||||
<InfotipContent
|
||||
aria-label={t(($) => $['inputFieldPanel.globalInputs.tooltip'], {
|
||||
ns: 'datasetPipeline',
|
||||
})}
|
||||
className="w-[240px]"
|
||||
>
|
||||
{t(($) => $['inputFieldPanel.globalInputs.tooltip'], { ns: 'datasetPipeline' })}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
} from '@langgenius/dify-ui/drawer'
|
||||
import { Field, FieldItem, FieldLabel } from '@langgenius/dify-ui/field'
|
||||
import { Fieldset, FieldsetLegend } from '@langgenius/dify-ui/fieldset'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { Input } from '@langgenius/dify-ui/input'
|
||||
import { Radio, RadioGroup } from '@langgenius/dify-ui/radio-group'
|
||||
import {
|
||||
@@ -25,7 +26,6 @@ import {
|
||||
} from '@langgenius/dify-ui/scroll-area'
|
||||
import { useId, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import { AuthHeaderPrefix, AuthType } from '@/app/components/tools/types'
|
||||
|
||||
type Props = Readonly<{
|
||||
@@ -213,14 +213,21 @@ export default function ConfigCredential({ positionCenter, credential, onChange,
|
||||
<label htmlFor={apiKeyHeaderInputId}>
|
||||
{t(($) => $['createTool.authMethod.key'], { ns: 'tools' })}
|
||||
</label>
|
||||
<Infotip
|
||||
aria-label={t(($) => $['createTool.authMethod.keyTooltip'], {
|
||||
ns: 'tools',
|
||||
})}
|
||||
className="ml-0.5 size-4"
|
||||
popupClassName="w-[261px] text-text-tertiary"
|
||||
>
|
||||
{t(($) => $['createTool.authMethod.keyTooltip'], { ns: 'tools' })}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={t(($) => $['createTool.authMethod.keyTooltip'], {
|
||||
ns: 'tools',
|
||||
})}
|
||||
className="ml-0.5 size-4"
|
||||
/>
|
||||
<InfotipContent
|
||||
aria-label={t(($) => $['createTool.authMethod.keyTooltip'], {
|
||||
ns: 'tools',
|
||||
})}
|
||||
className="w-[261px] text-text-tertiary"
|
||||
>
|
||||
{t(($) => $['createTool.authMethod.keyTooltip'], { ns: 'tools' })}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
<Input
|
||||
@@ -273,16 +280,23 @@ export default function ConfigCredential({ positionCenter, credential, onChange,
|
||||
<label htmlFor={apiKeyQueryInputId}>
|
||||
{t(($) => $['createTool.authMethod.queryParam'], { ns: 'tools' })}
|
||||
</label>
|
||||
<Infotip
|
||||
aria-label={t(($) => $['createTool.authMethod.queryParamTooltip'], {
|
||||
ns: 'tools',
|
||||
})}
|
||||
className="ml-0.5 size-4"
|
||||
popupClassName="w-[261px] text-text-tertiary"
|
||||
>
|
||||
{t(($) => $['createTool.authMethod.queryParamTooltip'], {
|
||||
ns: 'tools',
|
||||
})}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={t(($) => $['createTool.authMethod.queryParamTooltip'], {
|
||||
ns: 'tools',
|
||||
})}
|
||||
className="ml-0.5 size-4"
|
||||
/>
|
||||
<InfotipContent
|
||||
aria-label={t(($) => $['createTool.authMethod.queryParamTooltip'], {
|
||||
ns: 'tools',
|
||||
})}
|
||||
className="w-[261px] text-text-tertiary"
|
||||
>
|
||||
{t(($) => $['createTool.authMethod.queryParamTooltip'], {
|
||||
ns: 'tools',
|
||||
})}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
<Input
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
DrawerViewport,
|
||||
} from '@langgenius/dify-ui/drawer'
|
||||
import { Field, FieldError, FieldLabel } from '@langgenius/dify-ui/field'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { Input } from '@langgenius/dify-ui/input'
|
||||
import { Textarea } from '@langgenius/dify-ui/textarea'
|
||||
import { produce } from 'immer'
|
||||
@@ -29,7 +30,6 @@ import { useMemo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import AppIcon from '@/app/components/base/app-icon'
|
||||
import AppIconPicker from '@/app/components/base/app-icon-picker'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import LabelSelector from '@/app/components/tools/labels/selector'
|
||||
import ConfirmModal from '@/app/components/tools/workflow-tool/confirm-modal'
|
||||
import MethodSelector from '@/app/components/tools/workflow-tool/method-selector'
|
||||
@@ -88,8 +88,11 @@ type WorkflowToolDrawerFrameProps = {
|
||||
|
||||
const InfoTooltip = ({ children }: { children: string }) => {
|
||||
return (
|
||||
<Infotip aria-label={children} className="ml-1 size-3.5" popupClassName="w-[180px]">
|
||||
{children}
|
||||
<Infotip>
|
||||
<InfotipTrigger aria-label={children} className="ml-1 size-3.5" />
|
||||
<InfotipContent aria-label={children} className="w-[180px]">
|
||||
{children}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
)
|
||||
}
|
||||
@@ -182,33 +185,38 @@ const WorkflowToolOutputName = React.memo(
|
||||
</span>
|
||||
)}
|
||||
{hasReservedNameConflict || hasDuplicateNameConflict ? (
|
||||
<Infotip
|
||||
aria-label={issueLabel}
|
||||
className="text-text-warning-secondary"
|
||||
iconSize="small"
|
||||
iconVariant="warning"
|
||||
popupClassName={hasDuplicateNameConflict ? 'w-60' : 'w-45'}
|
||||
>
|
||||
<div className="space-y-2">
|
||||
{hasReservedNameConflict ? <p>{reservedOutputDuplicateTip}</p> : null}
|
||||
{hasDuplicateNameConflict ? (
|
||||
<div className="space-y-1.5">
|
||||
<p>{duplicateOutputTip}</p>
|
||||
{sources.length > 0 ? (
|
||||
<ul className="space-y-1">
|
||||
{sources.map((source) => {
|
||||
const sourceTitle = getSourceNodeDisplayName(source, sources)
|
||||
return (
|
||||
<li key={source.nodeId} className="wrap-break-word">
|
||||
{sourceNodeLabel}: <span translate="no">{sourceTitle}</span>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={issueLabel}
|
||||
className="text-text-warning-secondary"
|
||||
iconSize="small"
|
||||
iconVariant="warning"
|
||||
/>
|
||||
<InfotipContent
|
||||
aria-label={issueLabel}
|
||||
className={hasDuplicateNameConflict ? 'w-60' : 'w-45'}
|
||||
>
|
||||
<div className="space-y-2">
|
||||
{hasReservedNameConflict ? <p>{reservedOutputDuplicateTip}</p> : null}
|
||||
{hasDuplicateNameConflict ? (
|
||||
<div className="space-y-1.5">
|
||||
<p>{duplicateOutputTip}</p>
|
||||
{sources.length > 0 ? (
|
||||
<ul className="space-y-1">
|
||||
{sources.map((source) => {
|
||||
const sourceTitle = getSourceNodeDisplayName(source, sources)
|
||||
return (
|
||||
<li key={source.nodeId} className="wrap-break-word">
|
||||
{sourceNodeLabel}: <span translate="no">{sourceTitle}</span>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
@@ -7,11 +7,11 @@ import type {
|
||||
ListRef,
|
||||
} from '@/app/components/workflow/block-selector/marketplace-plugin/list'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@langgenius/dify-ui/popover'
|
||||
import { useSuspenseQuery } from '@tanstack/react-query'
|
||||
import { memo, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { memo, useEffect, useId, useMemo, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import { SearchInput } from '@/app/components/base/search-input'
|
||||
import useGetIcon from '@/app/components/plugins/install-plugin/base/use-get-icon'
|
||||
import { useMarketplacePlugins } from '@/app/components/plugins/marketplace/hooks'
|
||||
@@ -32,25 +32,30 @@ const DEFAULT_TAGS: ListProps['tags'] = []
|
||||
|
||||
const NotFoundWarn = (props: { title: string; description: ReactNode }) => {
|
||||
const { title, description } = props
|
||||
const titleId = useId()
|
||||
|
||||
const { t } = useTranslation()
|
||||
return (
|
||||
<Infotip
|
||||
aria-label={title}
|
||||
iconVariant="warning"
|
||||
iconSize="large"
|
||||
className="text-text-destructive"
|
||||
popupClassName="w-45"
|
||||
>
|
||||
<div className="space-y-1">
|
||||
<h3 className="font-semibold text-text-primary">{title}</h3>
|
||||
<p>{description}</p>
|
||||
<p>
|
||||
<Link href="/plugins" className="text-text-accent">
|
||||
{t(($) => $['nodes.agent.linkToPlugin'], { ns: 'workflow' })}
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={title}
|
||||
iconVariant="warning"
|
||||
iconSize="large"
|
||||
className="text-text-destructive"
|
||||
/>
|
||||
<InfotipContent aria-labelledby={titleId} className="w-45">
|
||||
<div className="space-y-1">
|
||||
<h3 id={titleId} className="font-semibold text-text-primary">
|
||||
{title}
|
||||
</h3>
|
||||
<p>{description}</p>
|
||||
<p>
|
||||
<Link href="/plugins" className="text-text-accent">
|
||||
{t(($) => $['nodes.agent.linkToPlugin'], { ns: 'workflow' })}
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
)
|
||||
}
|
||||
|
||||
+10
-3
@@ -1,7 +1,7 @@
|
||||
import type { DefaultValueForm } from './types'
|
||||
import type { CommonNodeType, Node } from '@/app/components/workflow/types'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import {
|
||||
Collapse,
|
||||
CollapseActions,
|
||||
@@ -43,8 +43,15 @@ const ErrorHandle = ({ id, data }: ErrorHandleProps) => {
|
||||
</CollapseTitle>
|
||||
{!!error_strategy && <CollapseIndicator />}
|
||||
</CollapseTrigger>
|
||||
<Infotip aria-label={t(($) => $['nodes.common.errorHandle.tip'], { ns: 'workflow' })}>
|
||||
{t(($) => $['nodes.common.errorHandle.tip'], { ns: 'workflow' })}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={t(($) => $['nodes.common.errorHandle.tip'], { ns: 'workflow' })}
|
||||
/>
|
||||
<InfotipContent
|
||||
aria-label={t(($) => $['nodes.common.errorHandle.tip'], { ns: 'workflow' })}
|
||||
>
|
||||
{t(($) => $['nodes.common.errorHandle.tip'], { ns: 'workflow' })}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
<CollapseActions>
|
||||
<div className="pr-4">
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
'use client'
|
||||
import type { FC, ReactNode } from 'react'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { RiArrowDownSLine } from '@remixicon/react'
|
||||
import * as React from 'react'
|
||||
import { useState } from 'react'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
|
||||
type Props = Readonly<{
|
||||
className?: string
|
||||
@@ -66,8 +66,9 @@ const Field: FC<Props> = ({
|
||||
{title} {required && <span className="text-text-destructive">*</span>}
|
||||
</div>
|
||||
{!!tooltip && !!tooltipLabel && (
|
||||
<Infotip aria-label={tooltipLabel} className="ml-1">
|
||||
{tooltip}
|
||||
<Infotip>
|
||||
<InfotipTrigger aria-label={tooltipLabel} className="ml-1" />
|
||||
<InfotipContent aria-label={tooltipLabel}>{tooltip}</InfotipContent>
|
||||
</Infotip>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { ReactNode } from 'react'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { memo, useState } from 'react'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
|
||||
export type FieldTitleProps = {
|
||||
title?: string
|
||||
@@ -57,8 +57,9 @@ export const FieldTitle = memo(
|
||||
/>
|
||||
)}
|
||||
{tooltip && (
|
||||
<Infotip aria-label={tooltip} className="ml-1">
|
||||
{tooltip}
|
||||
<Infotip>
|
||||
<InfotipTrigger aria-label={tooltip} className="ml-1" />
|
||||
<InfotipContent aria-label={tooltip}>{tooltip}</InfotipContent>
|
||||
</Infotip>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
import type { VariantProps } from 'class-variance-authority'
|
||||
import type { FC } from 'react'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { cva } from 'class-variance-authority'
|
||||
import * as React from 'react'
|
||||
import { useCallback } from 'react'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
|
||||
const variants = cva([], {
|
||||
variants: {
|
||||
@@ -63,8 +63,11 @@ const OptionCard: FC<Props> = ({
|
||||
>
|
||||
<span>{title}</span>
|
||||
{tooltip && (
|
||||
<Infotip aria-label={tooltip} popupClassName="w-[240px]">
|
||||
{tooltip}
|
||||
<Infotip>
|
||||
<InfotipTrigger aria-label={tooltip} />
|
||||
<InfotipContent aria-label={tooltip} className="w-[240px]">
|
||||
{tooltip}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,8 @@ import type { SelectorParam } from 'i18next'
|
||||
import type { ReactElement } from 'react'
|
||||
import type { IterationNodeType } from '@/app/components/workflow/nodes/iteration/types'
|
||||
import type { NodeProps } from '@/app/components/workflow/types'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { useId } from 'react'
|
||||
import { BlockEnum, NodeRunningStatus } from '@/app/components/workflow/types'
|
||||
|
||||
type HeaderMetaProps = {
|
||||
@@ -19,6 +20,7 @@ export type WorkflowTranslator = (
|
||||
) => string
|
||||
|
||||
export const NodeHeaderMeta = ({ data, hasVarValue, isLoading, loopIndex, t }: HeaderMetaProps) => {
|
||||
const titleId = useId()
|
||||
return (
|
||||
<>
|
||||
{data.type === BlockEnum.Iteration && (data as IterationNodeType).is_parallel && (
|
||||
@@ -26,14 +28,18 @@ export const NodeHeaderMeta = ({ data, hasVarValue, isLoading, loopIndex, t }: H
|
||||
<span className="rounded-[5px] border border-text-warning px-1.25 py-0.75 system-2xs-medium-uppercase text-text-warning">
|
||||
{t(($) => $['nodes.iteration.parallelModeUpper'], { ns: 'workflow' })}
|
||||
</span>
|
||||
<Infotip
|
||||
aria-label={t(($) => $['nodes.iteration.parallelModeEnableTitle'], { ns: 'workflow' })}
|
||||
popupClassName="w-45"
|
||||
>
|
||||
<div className="font-semibold text-text-primary">
|
||||
{t(($) => $['nodes.iteration.parallelModeEnableTitle'], { ns: 'workflow' })}
|
||||
</div>
|
||||
{t(($) => $['nodes.iteration.parallelModeEnableDesc'], { ns: 'workflow' })}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={t(($) => $['nodes.iteration.parallelModeEnableTitle'], {
|
||||
ns: 'workflow',
|
||||
})}
|
||||
/>
|
||||
<InfotipContent aria-labelledby={titleId} className="w-45">
|
||||
<div id={titleId} className="font-semibold text-text-primary">
|
||||
{t(($) => $['nodes.iteration.parallelModeEnableTitle'], { ns: 'workflow' })}
|
||||
</div>
|
||||
{t(($) => $['nodes.iteration.parallelModeEnableDesc'], { ns: 'workflow' })}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -4,12 +4,12 @@ import type { AgentOutputTypeOptionValue } from '@/app/components/base/prompt-ed
|
||||
import type { WorkflowNodesMap } from '@/app/components/base/prompt-editor/types'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Field, FieldLabel } from '@langgenius/dify-ui/field'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
|
||||
import { useBoolean } from 'ahooks'
|
||||
import { $insertNodes } from 'lexical'
|
||||
import { useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import PromptEditor from '@/app/components/base/prompt-editor'
|
||||
import { $createCustomTextNode } from '@/app/components/base/prompt-editor/plugins/custom-text/node'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
@@ -97,17 +97,24 @@ export function AgentTaskField({
|
||||
<FieldLabel className="min-w-0 py-1 system-sm-semibold-uppercase! text-text-secondary">
|
||||
{t(($) => $[`${i18nPrefix}.task.label`], { ns: 'workflow' })}
|
||||
</FieldLabel>
|
||||
<Infotip aria-label={t(($) => $[`${i18nPrefix}.task.tooltip`], { ns: 'workflow' })}>
|
||||
<span>{t(($) => $[`${i18nPrefix}.task.tooltip`], { ns: 'workflow' })}</span>{' '}
|
||||
<a
|
||||
href={docLink('/use-dify/nodes/agent#give-it-a-task')}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="inline-flex items-center gap-0.5 rounded-sm text-text-accent hover:underline focus-visible:ring-2 focus-visible:ring-state-accent-solid focus-visible:outline-hidden"
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={t(($) => $[`${i18nPrefix}.task.tooltip`], { ns: 'workflow' })}
|
||||
/>
|
||||
<InfotipContent
|
||||
aria-label={t(($) => $[`${i18nPrefix}.task.tooltip`], { ns: 'workflow' })}
|
||||
>
|
||||
{t(($) => $[`${i18nPrefix}.task.learnMore`], { ns: 'workflow' })}
|
||||
<span aria-hidden className="i-ri-external-link-line size-3" />
|
||||
</a>
|
||||
<span>{t(($) => $[`${i18nPrefix}.task.tooltip`], { ns: 'workflow' })}</span>{' '}
|
||||
<a
|
||||
href={docLink('/use-dify/nodes/agent#give-it-a-task')}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="inline-flex items-center gap-0.5 rounded-sm text-text-accent hover:underline focus-visible:ring-2 focus-visible:ring-state-accent-solid focus-visible:outline-hidden"
|
||||
>
|
||||
{t(($) => $[`${i18nPrefix}.task.learnMore`], { ns: 'workflow' })}
|
||||
<span aria-hidden className="i-ri-external-link-line size-3" />
|
||||
</a>
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
<div
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { DeliveryMethod, DeliveryMethodType, FormInputItem } from '../../types'
|
||||
import type { Node, NodeOutPutVar } from '@/app/components/workflow/types'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { produce } from 'immer'
|
||||
import * as React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import { useNodesSyncDraft } from '../../../../hooks/use-nodes-sync-draft'
|
||||
import MethodItem from './method-item'
|
||||
import MethodSelector from './method-selector'
|
||||
@@ -66,10 +66,15 @@ const DeliveryMethodForm: React.FC<Props> = ({
|
||||
<div className="system-sm-semibold-uppercase text-text-secondary">
|
||||
{t(($) => $[`${i18nPrefix}.deliveryMethod.title`], { ns: 'workflow' })}
|
||||
</div>
|
||||
<Infotip
|
||||
aria-label={t(($) => $[`${i18nPrefix}.deliveryMethod.tooltip`], { ns: 'workflow' })}
|
||||
>
|
||||
{t(($) => $[`${i18nPrefix}.deliveryMethod.tooltip`], { ns: 'workflow' })}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={t(($) => $[`${i18nPrefix}.deliveryMethod.tooltip`], { ns: 'workflow' })}
|
||||
/>
|
||||
<InfotipContent
|
||||
aria-label={t(($) => $[`${i18nPrefix}.deliveryMethod.tooltip`], { ns: 'workflow' })}
|
||||
>
|
||||
{t(($) => $[`${i18nPrefix}.deliveryMethod.tooltip`], { ns: 'workflow' })}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
{!readonly && (
|
||||
|
||||
@@ -4,6 +4,7 @@ import type { NodePanelProps, Var } from '@/app/components/workflow/types'
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { IconButton } from '@langgenius/dify-ui/icon-button'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { Separator } from '@langgenius/dify-ui/separator'
|
||||
import {
|
||||
RiAddLine,
|
||||
@@ -16,7 +17,6 @@ import copy from 'copy-to-clipboard'
|
||||
import * as React from 'react'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import OutputVars, { VarItem } from '@/app/components/workflow/nodes/_base/components/output-vars'
|
||||
import Split from '@/app/components/workflow/nodes/_base/components/split'
|
||||
import useAvailableVarList from '@/app/components/workflow/nodes/_base/hooks/use-available-var-list'
|
||||
@@ -115,10 +115,15 @@ const Panel: FC<NodePanelProps<HumanInputNodeType>> = ({ id, data }) => {
|
||||
<div className="system-sm-semibold-uppercase text-text-secondary">
|
||||
{t(($) => $[`${i18nPrefix}.formContent.title`], { ns: 'workflow' })}
|
||||
</div>
|
||||
<Infotip
|
||||
aria-label={t(($) => $[`${i18nPrefix}.formContent.tooltip`], { ns: 'workflow' })}
|
||||
>
|
||||
{t(($) => $[`${i18nPrefix}.formContent.tooltip`], { ns: 'workflow' })}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={t(($) => $[`${i18nPrefix}.formContent.tooltip`], { ns: 'workflow' })}
|
||||
/>
|
||||
<InfotipContent
|
||||
aria-label={t(($) => $[`${i18nPrefix}.formContent.tooltip`], { ns: 'workflow' })}
|
||||
>
|
||||
{t(($) => $[`${i18nPrefix}.formContent.tooltip`], { ns: 'workflow' })}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
{!readOnly && (
|
||||
@@ -193,10 +198,15 @@ const Panel: FC<NodePanelProps<HumanInputNodeType>> = ({ id, data }) => {
|
||||
<div className="system-sm-semibold-uppercase text-text-secondary">
|
||||
{t(($) => $[`${i18nPrefix}.userActions.title`], { ns: 'workflow' })}
|
||||
</div>
|
||||
<Infotip
|
||||
aria-label={t(($) => $[`${i18nPrefix}.userActions.tooltip`], { ns: 'workflow' })}
|
||||
>
|
||||
{t(($) => $[`${i18nPrefix}.userActions.tooltip`], { ns: 'workflow' })}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={t(($) => $[`${i18nPrefix}.userActions.tooltip`], { ns: 'workflow' })}
|
||||
/>
|
||||
<InfotipContent
|
||||
aria-label={t(($) => $[`${i18nPrefix}.userActions.tooltip`], { ns: 'workflow' })}
|
||||
>
|
||||
{t(($) => $[`${i18nPrefix}.userActions.tooltip`], { ns: 'workflow' })}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
{!readOnly && (
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Fieldset, FieldsetLegend } from '@langgenius/dify-ui/fieldset'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import {
|
||||
NumberField,
|
||||
NumberFieldControls,
|
||||
@@ -19,7 +20,6 @@ import {
|
||||
import { memo, useCallback, useId } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Economic, HighQuality } from '@/app/components/base/icons/src/vender/knowledge'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import { Field } from '@/app/components/workflow/nodes/_base/components/layout'
|
||||
import { ChunkStructureEnum, IndexMethodEnum } from '../types'
|
||||
import OptionCard from './option-card'
|
||||
@@ -113,8 +113,11 @@ const IndexMethod = ({
|
||||
>
|
||||
{keywordNumberLabel}
|
||||
</label>
|
||||
<Infotip aria-label={keywordNumberLabel} className="ml-0.5 size-3.5">
|
||||
{keywordNumberLabel}
|
||||
<Infotip>
|
||||
<InfotipTrigger aria-label={keywordNumberLabel} className="ml-0.5 size-3.5" />
|
||||
<InfotipContent aria-label={keywordNumberLabel}>
|
||||
{keywordNumberLabel}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
<Slider
|
||||
|
||||
+7
-3
@@ -6,6 +6,7 @@ import type { HybridSearchModeOption, Option } from './type'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Field, FieldItem, FieldLabel } from '@langgenius/dify-ui/field'
|
||||
import { Fieldset, FieldsetLegend } from '@langgenius/dify-ui/fieldset'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { RadioControl, RadioGroup, RadioItem } from '@langgenius/dify-ui/radio-group'
|
||||
import { Switch } from '@langgenius/dify-ui/switch'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
@@ -18,7 +19,6 @@ import {
|
||||
OptionCardEffectPurple,
|
||||
OptionCardEffectTeal,
|
||||
} from '@/app/components/base/icons/src/public/knowledge'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import { DEFAULT_WEIGHTED_SCORE } from '@/models/datasets'
|
||||
import { HybridSearchModeEnum, RetrievalSearchMethodEnum } from '../../types'
|
||||
import RerankingModelSelector from './reranking-model-selector'
|
||||
@@ -282,8 +282,12 @@ export function SearchMethodOption({
|
||||
/>
|
||||
<span className="truncate">{rerankModelLabel}</span>
|
||||
</FieldLabel>
|
||||
<Infotip aria-label={rerankModelTip} className="ml-0.5 size-3.5 shrink-0">
|
||||
{rerankModelTip}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={rerankModelTip}
|
||||
className="ml-0.5 size-3.5 shrink-0"
|
||||
/>
|
||||
<InfotipContent aria-label={rerankModelTip}>{rerankModelTip}</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
</Field>
|
||||
|
||||
+7
-5
@@ -1,5 +1,6 @@
|
||||
import { Field, FieldLabel } from '@langgenius/dify-ui/field'
|
||||
import { Fieldset, FieldsetLegend } from '@langgenius/dify-ui/fieldset'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import {
|
||||
NumberField,
|
||||
NumberFieldControls,
|
||||
@@ -10,7 +11,6 @@ import {
|
||||
} from '@langgenius/dify-ui/number-field'
|
||||
import { Switch } from '@langgenius/dify-ui/switch'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import { env } from '@/env'
|
||||
|
||||
export type TopKFieldProps = {
|
||||
@@ -67,8 +67,9 @@ export function TopKAndScoreThreshold({
|
||||
<Field name="top_k" className="gap-0">
|
||||
<div className="mb-0.5 flex h-6 items-center">
|
||||
<FieldLabel className="py-0 system-xs-medium text-text-secondary">{topKLabel}</FieldLabel>
|
||||
<Infotip aria-label={topKTip} className="ml-0.5 size-3.5">
|
||||
{topKTip}
|
||||
<Infotip>
|
||||
<InfotipTrigger aria-label={topKTip} className="ml-0.5 size-3.5" />
|
||||
<InfotipContent aria-label={topKTip}>{topKTip}</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
<NumberField
|
||||
@@ -102,8 +103,9 @@ export function TopKAndScoreThreshold({
|
||||
/>
|
||||
<span className="grow truncate">{scoreThresholdLabel}</span>
|
||||
</FieldLabel>
|
||||
<Infotip aria-label={scoreThresholdTip} className="ml-0.5 size-3.5">
|
||||
{scoreThresholdTip}
|
||||
<Infotip>
|
||||
<InfotipTrigger aria-label={scoreThresholdTip} className="ml-0.5 size-3.5" />
|
||||
<InfotipContent aria-label={scoreThresholdTip}>{scoreThresholdTip}</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
</Field>
|
||||
|
||||
+11
-6
@@ -1,8 +1,8 @@
|
||||
import type { MetadataShape } from '@/app/components/workflow/nodes/knowledge-retrieval/types'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { noop } from 'es-toolkit/function'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import ModelParameterModal from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal'
|
||||
import {
|
||||
Collapse,
|
||||
@@ -58,11 +58,16 @@ const MetadataFilter = ({
|
||||
</CollapseTitle>
|
||||
{metadataFilterMode === MetadataFilteringModeEnum.automatic && <CollapseIndicator />}
|
||||
</CollapseTrigger>
|
||||
<Infotip
|
||||
aria-label={t(($) => $['nodes.knowledgeRetrieval.metadata.tip'], { ns: 'workflow' })}
|
||||
popupClassName="w-[200px]"
|
||||
>
|
||||
{t(($) => $['nodes.knowledgeRetrieval.metadata.tip'], { ns: 'workflow' })}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={t(($) => $['nodes.knowledgeRetrieval.metadata.tip'], { ns: 'workflow' })}
|
||||
/>
|
||||
<InfotipContent
|
||||
aria-label={t(($) => $['nodes.knowledgeRetrieval.metadata.tip'], { ns: 'workflow' })}
|
||||
className="w-[200px]"
|
||||
>
|
||||
{t(($) => $['nodes.knowledgeRetrieval.metadata.tip'], { ns: 'workflow' })}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
<CollapseActions>
|
||||
<div className="flex items-center pr-4">
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
import type { SelectorParam } from 'i18next'
|
||||
import type { FC } from 'react'
|
||||
import type { ModelConfig, Node, NodeOutPutVar, PromptItem, Variable } from '../../../types'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import * as React from 'react'
|
||||
import { useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import Editor from '@/app/components/workflow/nodes/_base/components/prompt/editor'
|
||||
import TypeSelector from '@/app/components/workflow/nodes/_base/components/selector'
|
||||
import { PromptRole } from '@/models/debug'
|
||||
@@ -127,8 +127,11 @@ const ConfigPromptItem: FC<Props> = ({
|
||||
)}
|
||||
|
||||
{roleDescription && (
|
||||
<Infotip aria-label={roleDescription} popupClassName="w-[180px]">
|
||||
{roleDescription}
|
||||
<Infotip>
|
||||
<InfotipTrigger aria-label={roleDescription} />
|
||||
<InfotipContent aria-label={roleDescription} className="w-[180px]">
|
||||
{roleDescription}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
)}
|
||||
</div>
|
||||
|
||||
+11
-6
@@ -2,12 +2,12 @@ import type { FC } from 'react'
|
||||
import type { FormValue } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import type { Model } from '@/types/app'
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { Textarea } from '@langgenius/dify-ui/textarea'
|
||||
import { RiCloseLine, RiSparklingFill } from '@remixicon/react'
|
||||
import * as React from 'react'
|
||||
import { useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import ModelParameterModal from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal'
|
||||
|
||||
type ModelInfo = {
|
||||
@@ -83,11 +83,16 @@ const PromptEditor: FC<PromptEditorProps> = ({
|
||||
<div className="flex flex-col gap-y-1 px-4 py-2">
|
||||
<div className="flex h-6 items-center system-sm-semibold-uppercase text-text-secondary">
|
||||
<span>{t(($) => $['nodes.llm.jsonSchema.instruction'], { ns: 'workflow' })}</span>
|
||||
<Infotip
|
||||
aria-label={t(($) => $['nodes.llm.jsonSchema.promptTooltip'], { ns: 'workflow' })}
|
||||
className="size-3.5"
|
||||
>
|
||||
{t(($) => $['nodes.llm.jsonSchema.promptTooltip'], { ns: 'workflow' })}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={t(($) => $['nodes.llm.jsonSchema.promptTooltip'], { ns: 'workflow' })}
|
||||
className="size-3.5"
|
||||
/>
|
||||
<InfotipContent
|
||||
aria-label={t(($) => $['nodes.llm.jsonSchema.promptTooltip'], { ns: 'workflow' })}
|
||||
>
|
||||
{t(($) => $['nodes.llm.jsonSchema.promptTooltip'], { ns: 'workflow' })}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { FC } from 'react'
|
||||
import type { LLMNodeType } from '../types'
|
||||
import type { Memory, Node, NodeOutPutVar } from '@/app/components/workflow/types'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import * as React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import MemoryConfig from '@/app/components/workflow/nodes/_base/components/memory-config'
|
||||
import Editor from '@/app/components/workflow/nodes/_base/components/prompt/editor'
|
||||
import { FlowType } from '@/types/common'
|
||||
@@ -71,8 +71,15 @@ const PanelMemorySection: FC<Props> = ({
|
||||
<div className="text-xs font-semibold text-text-secondary uppercase">
|
||||
{t(($) => $['nodes.common.memories.title'], { ns: 'workflow' })}
|
||||
</div>
|
||||
<Infotip aria-label={t(($) => $['nodes.common.memories.tip'], { ns: 'workflow' })}>
|
||||
{t(($) => $['nodes.common.memories.tip'], { ns: 'workflow' })}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={t(($) => $['nodes.common.memories.tip'], { ns: 'workflow' })}
|
||||
/>
|
||||
<InfotipContent
|
||||
aria-label={t(($) => $['nodes.common.memories.tip'], { ns: 'workflow' })}
|
||||
>
|
||||
{t(($) => $['nodes.common.memories.tip'], { ns: 'workflow' })}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
<div className="flex h-4.5 items-center rounded-[5px] border border-divider-deep bg-components-badge-bg-dimm px-1 text-xs font-semibold text-text-tertiary uppercase">
|
||||
@@ -84,11 +91,16 @@ const PanelMemorySection: FC<Props> = ({
|
||||
title={
|
||||
<div className="flex items-center space-x-1">
|
||||
<div className="text-xs font-semibold text-text-secondary uppercase">user</div>
|
||||
<Infotip
|
||||
aria-label={t(($) => $['nodes.llm.roleDescription.user'], { ns: 'workflow' })}
|
||||
popupClassName="w-[180px]"
|
||||
>
|
||||
{t(($) => $['nodes.llm.roleDescription.user'], { ns: 'workflow' })}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={t(($) => $['nodes.llm.roleDescription.user'], { ns: 'workflow' })}
|
||||
/>
|
||||
<InfotipContent
|
||||
aria-label={t(($) => $['nodes.llm.roleDescription.user'], { ns: 'workflow' })}
|
||||
className="w-[180px]"
|
||||
>
|
||||
{t(($) => $['nodes.llm.roleDescription.user'], { ns: 'workflow' })}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { FC } from 'react'
|
||||
import type { LLMNodeType, StructuredOutput } from '../types'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { Switch } from '@langgenius/dify-ui/switch'
|
||||
import * as React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import OutputVars, { VarItem } from '@/app/components/workflow/nodes/_base/components/output-vars'
|
||||
import Split from '@/app/components/workflow/nodes/_base/components/split'
|
||||
import { StructureOutput } from './structure-output'
|
||||
@@ -30,6 +30,7 @@ const PanelOutputSection: FC<Props> = ({
|
||||
handleStructureOutputChange,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const warningTitleId = React.useId()
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -40,29 +41,36 @@ const PanelOutputSection: FC<Props> = ({
|
||||
operations={
|
||||
<div className="mr-4 flex shrink-0 items-center">
|
||||
{!isModelSupportStructuredOutput && !!inputs.structured_output_enabled && (
|
||||
<Infotip
|
||||
aria-label={t(($) => $['structOutput.modelNotSupported'], { ns: 'app' })}
|
||||
iconVariant="warning"
|
||||
iconSize="large"
|
||||
className="mr-1 text-text-warning-secondary"
|
||||
popupClassName="w-58"
|
||||
>
|
||||
<div className="title-xs-semi-bold text-text-primary">
|
||||
{t(($) => $['structOutput.modelNotSupported'], { ns: 'app' })}
|
||||
</div>
|
||||
<div className="mt-1">
|
||||
{t(($) => $['structOutput.modelNotSupportedTip'], { ns: 'app' })}
|
||||
</div>
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={t(($) => $['structOutput.modelNotSupported'], { ns: 'app' })}
|
||||
iconVariant="warning"
|
||||
iconSize="large"
|
||||
className="mr-1 text-text-warning-secondary"
|
||||
/>
|
||||
<InfotipContent aria-labelledby={warningTitleId} className="w-58">
|
||||
<div id={warningTitleId} className="title-xs-semi-bold text-text-primary">
|
||||
{t(($) => $['structOutput.modelNotSupported'], { ns: 'app' })}
|
||||
</div>
|
||||
<div className="mt-1">
|
||||
{t(($) => $['structOutput.modelNotSupportedTip'], { ns: 'app' })}
|
||||
</div>
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
)}
|
||||
<div className="mr-0.5 system-xs-medium-uppercase text-text-tertiary">
|
||||
{t(($) => $['structOutput.structured'], { ns: 'app' })}
|
||||
</div>
|
||||
<Infotip
|
||||
aria-label={t(($) => $['structOutput.structuredTip'], { ns: 'app' })}
|
||||
popupClassName="w-[150px]"
|
||||
>
|
||||
{t(($) => $['structOutput.structuredTip'], { ns: 'app' })}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={t(($) => $['structOutput.structuredTip'], { ns: 'app' })}
|
||||
/>
|
||||
<InfotipContent
|
||||
aria-label={t(($) => $['structOutput.structuredTip'], { ns: 'app' })}
|
||||
className="w-[150px]"
|
||||
>
|
||||
{t(($) => $['structOutput.structuredTip'], { ns: 'app' })}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
<Switch
|
||||
className="ml-2"
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { FC } from 'react'
|
||||
import type { ParameterExtractorNodeType } from './types'
|
||||
import type { NodePanelProps } from '@/app/components/workflow/types'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import * as React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import ModelParameterModal from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal'
|
||||
import { FieldCollapse } from '@/app/components/workflow/nodes/_base/components/collapse'
|
||||
import Field from '@/app/components/workflow/nodes/_base/components/field'
|
||||
@@ -120,12 +120,17 @@ const Panel: FC<NodePanelProps<ParameterExtractorNodeType>> = ({ id, data }) =>
|
||||
<span className="uppercase">
|
||||
{t(($) => $[`${i18nPrefix}.instruction`], { ns: 'workflow' })}
|
||||
</span>
|
||||
<Infotip
|
||||
aria-label={t(($) => $[`${i18nPrefix}.instructionTip`], { ns: 'workflow' })}
|
||||
className="ml-0.5 size-3.5"
|
||||
popupClassName="w-[120px]"
|
||||
>
|
||||
{t(($) => $[`${i18nPrefix}.instructionTip`], { ns: 'workflow' })}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={t(($) => $[`${i18nPrefix}.instructionTip`], { ns: 'workflow' })}
|
||||
className="ml-0.5 size-3.5"
|
||||
/>
|
||||
<InfotipContent
|
||||
aria-label={t(($) => $[`${i18nPrefix}.instructionTip`], { ns: 'workflow' })}
|
||||
className="w-[120px]"
|
||||
>
|
||||
{t(($) => $[`${i18nPrefix}.instructionTip`], { ns: 'workflow' })}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
}
|
||||
|
||||
+12
-7
@@ -1,9 +1,9 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import type { Memory, Node, NodeOutPutVar } from '@/app/components/workflow/types'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import * as React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import Editor from '@/app/components/workflow/nodes/_base/components/prompt/editor'
|
||||
import MemoryConfig from '../../_base/components/memory-config'
|
||||
|
||||
@@ -50,12 +50,17 @@ const AdvancedSetting: FC<Props> = ({
|
||||
<span className="uppercase">
|
||||
{t(($) => $[`${i18nPrefix}.instruction`], { ns: 'workflow' })}
|
||||
</span>
|
||||
<Infotip
|
||||
aria-label={t(($) => $[`${i18nPrefix}.instructionTip`], { ns: 'workflow' })}
|
||||
className="ml-0.5 size-3.5"
|
||||
popupClassName="w-[120px]"
|
||||
>
|
||||
{t(($) => $[`${i18nPrefix}.instructionTip`], { ns: 'workflow' })}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={t(($) => $[`${i18nPrefix}.instructionTip`], { ns: 'workflow' })}
|
||||
className="ml-0.5 size-3.5"
|
||||
/>
|
||||
<InfotipContent
|
||||
aria-label={t(($) => $[`${i18nPrefix}.instructionTip`], { ns: 'workflow' })}
|
||||
className="w-[120px]"
|
||||
>
|
||||
{t(($) => $[`${i18nPrefix}.instructionTip`], { ns: 'workflow' })}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@ import type { CredentialFormSchema } from '@/app/components/header/account-setti
|
||||
import type { Tool } from '@/app/components/tools/types'
|
||||
import type { ToolWithProvider } from '@/app/components/workflow/types'
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { useId, useState } from 'react'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||
import { SchemaModal } from '@/app/components/plugins/plugin-detail-panel/tool-selector/components/schema-modal'
|
||||
@@ -102,12 +102,11 @@ const ToolFormItem: FC<Props> = ({
|
||||
<div className="ml-1 system-xs-regular text-text-destructive-secondary">*</div>
|
||||
)}
|
||||
{!showDescription && tooltip && (
|
||||
<Infotip
|
||||
aria-label={tooltip[language] || tooltip.en_US}
|
||||
className="ml-1"
|
||||
popupClassName="w-[200px]"
|
||||
>
|
||||
{tooltip[language] || tooltip.en_US}
|
||||
<Infotip>
|
||||
<InfotipTrigger aria-label={tooltip[language] || tooltip.en_US} className="ml-1" />
|
||||
<InfotipContent aria-label={tooltip[language] || tooltip.en_US} className="w-[200px]">
|
||||
{tooltip[language] || tooltip.en_US}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
)}
|
||||
{showSchemaButton && (
|
||||
|
||||
@@ -5,8 +5,8 @@ import type { Event } from '@/app/components/tools/types'
|
||||
import type { TriggerWithProvider } from '@/app/components/workflow/block-selector/types'
|
||||
import type { PluginTriggerVarInputs } from '@/app/components/workflow/nodes/trigger-plugin/types'
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { useId, useState } from 'react'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||
import { SchemaModal } from '@/app/components/plugins/plugin-detail-panel/tool-selector/components/schema-modal'
|
||||
@@ -59,12 +59,14 @@ const TriggerFormItem: FC<Props> = ({
|
||||
<div className="ml-1 system-xs-regular text-text-destructive-secondary">*</div>
|
||||
)}
|
||||
{!showDescription && tooltip && (
|
||||
<Infotip
|
||||
aria-label={tooltip[language] || tooltip.en_US}
|
||||
className="ml-1 size-4"
|
||||
popupClassName="w-[200px]"
|
||||
>
|
||||
{tooltip[language] || tooltip.en_US}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={tooltip[language] || tooltip.en_US}
|
||||
className="ml-1 size-4"
|
||||
/>
|
||||
<InfotipContent aria-label={tooltip[language] || tooltip.en_US} className="w-[200px]">
|
||||
{tooltip[language] || tooltip.en_US}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
)}
|
||||
{showSchemaButton && (
|
||||
|
||||
+16
-9
@@ -1,7 +1,7 @@
|
||||
import { Fieldset, FieldsetLegend } from '@langgenius/dify-ui/fieldset'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import * as React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
|
||||
type MonthlyDaysSelectorProps = {
|
||||
selectedDays: (number | 'last')[]
|
||||
@@ -55,14 +55,21 @@ const MonthlyDaysSelector = ({ selectedDays, onChange }: MonthlyDaysSelectorProp
|
||||
>
|
||||
{t(($) => $['nodes.triggerSchedule.lastDay'], { ns: 'workflow' })}
|
||||
</button>
|
||||
<Infotip
|
||||
aria-label={t(($) => $['nodes.triggerSchedule.lastDayTooltip'], {
|
||||
ns: 'workflow',
|
||||
})}
|
||||
className="mr-1 size-3"
|
||||
iconSize="small"
|
||||
>
|
||||
{t(($) => $['nodes.triggerSchedule.lastDayTooltip'], { ns: 'workflow' })}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={t(($) => $['nodes.triggerSchedule.lastDayTooltip'], {
|
||||
ns: 'workflow',
|
||||
})}
|
||||
className="mr-1 size-3"
|
||||
iconSize="small"
|
||||
/>
|
||||
<InfotipContent
|
||||
aria-label={t(($) => $['nodes.triggerSchedule.lastDayTooltip'], {
|
||||
ns: 'workflow',
|
||||
})}
|
||||
>
|
||||
{t(($) => $['nodes.triggerSchedule.lastDayTooltip'], { ns: 'workflow' })}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
) : (
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Button } from '@langgenius/dify-ui/button'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Field, FieldLabel } from '@langgenius/dify-ui/field'
|
||||
import { IconButton } from '@langgenius/dify-ui/icon-button'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { Input } from '@langgenius/dify-ui/input'
|
||||
import { NumberField, NumberFieldGroup, NumberFieldInput } from '@langgenius/dify-ui/number-field'
|
||||
import { Textarea } from '@langgenius/dify-ui/textarea'
|
||||
@@ -10,7 +11,6 @@ import * as React from 'react'
|
||||
import { useEffect } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { v4 as uuid4 } from 'uuid'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import { isLLMEnvironmentVariableValue } from '@/app/components/workflow/llm-environment-variable'
|
||||
import { LLMEnvironmentVariableValueField } from '@/app/components/workflow/llm-environment-variable-value-field'
|
||||
import { useWorkflowStore } from '@/app/components/workflow/store'
|
||||
@@ -199,12 +199,17 @@ const VariableModal = ({ env, onClose, onSave }: ModalPropsType) => {
|
||||
>
|
||||
Secret
|
||||
</button>
|
||||
<Infotip
|
||||
aria-label={t(($) => $['env.modal.secretTip'], { ns: 'workflow' })}
|
||||
className="absolute top-1/2 right-1 size-3.5 -translate-y-1/2"
|
||||
popupClassName="w-[240px]"
|
||||
>
|
||||
{t(($) => $['env.modal.secretTip'], { ns: 'workflow' })}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={t(($) => $['env.modal.secretTip'], { ns: 'workflow' })}
|
||||
className="absolute top-1/2 right-1 size-3.5 -translate-y-1/2"
|
||||
/>
|
||||
<InfotipContent
|
||||
aria-label={t(($) => $['env.modal.secretTip'], { ns: 'workflow' })}
|
||||
className="w-[240px]"
|
||||
>
|
||||
{t(($) => $['env.modal.secretTip'], { ns: 'workflow' })}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
<button
|
||||
|
||||
-5
@@ -1,4 +1,3 @@
|
||||
import type { ReactNode } from 'react'
|
||||
import type { PromptEditorProps } from '@/app/components/base/prompt-editor'
|
||||
import type { AgentTool } from '@/features/agent-v2/agent-composer/form-state'
|
||||
import { act, fireEvent, screen, waitFor } from '@testing-library/react'
|
||||
@@ -215,10 +214,6 @@ vi.mock('lexical', () => ({
|
||||
SELECTION_CHANGE_COMMAND: Symbol('selection-change-command'),
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/base/infotip', () => ({
|
||||
Infotip: ({ children }: { children: ReactNode }) => <span>{children}</span>,
|
||||
}))
|
||||
|
||||
vi.mock('foxact/use-clipboard', () => ({
|
||||
useClipboard: mockUseClipboard,
|
||||
}))
|
||||
|
||||
+6
-7
@@ -4,7 +4,7 @@ import type { ReactNode } from 'react'
|
||||
import type { AgentBuildDraftChangeSection } from '../build-draft-changes-context'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Collapsible, CollapsiblePanel, CollapsibleTrigger } from '@langgenius/dify-ui/collapsible'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { AgentBuildDraftChangeDot } from '../build-draft-change-dot'
|
||||
import { useIsAgentBuildDraftSectionChanged } from '../build-draft-changes-context'
|
||||
|
||||
@@ -75,12 +75,11 @@ export function ConfigureSection({
|
||||
</CollapsibleTrigger>
|
||||
</Heading>
|
||||
{hasTip && (
|
||||
<Infotip
|
||||
aria-label={tipAriaLabel}
|
||||
className="ml-0.5 size-3.5"
|
||||
popupClassName="max-w-64"
|
||||
>
|
||||
{tip}
|
||||
<Infotip>
|
||||
<InfotipTrigger aria-label={tipAriaLabel} className="ml-0.5 size-3.5" />
|
||||
<InfotipContent aria-label={tipAriaLabel} className="max-w-64">
|
||||
{tip}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
)}
|
||||
<span
|
||||
|
||||
+23
-16
@@ -12,12 +12,12 @@ import {
|
||||
FileTreeIcon,
|
||||
FileTreeLabel,
|
||||
} from '@langgenius/dify-ui/file-tree'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { noop } from 'es-toolkit/function'
|
||||
import { useAtomValue, useSetAtom } from 'jotai'
|
||||
import { useCallback, useRef, useState } from 'react'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
import { agentComposerDraftAtom } from '@/features/agent-v2/agent-composer/store'
|
||||
import {
|
||||
@@ -363,21 +363,28 @@ function AgentBuildNoteInfotip() {
|
||||
const docLink = useDocLink()
|
||||
|
||||
return (
|
||||
<Infotip
|
||||
aria-label={t(($) => $['agentDetail.configure.files.buildNote.tooltip'])}
|
||||
className="size-5 text-text-quaternary hover:text-text-quaternary"
|
||||
iconSize="large"
|
||||
popupClassName="w-[230px] rounded-xl px-4 py-3.5"
|
||||
>
|
||||
<p className="body-xs-regular text-text-secondary">
|
||||
<Trans
|
||||
i18nKey={($) => $['agentDetail.configure.files.buildNote.richTooltip']}
|
||||
ns="agentV2"
|
||||
components={{
|
||||
docLink: <DocsLink href={docLink('/use-dify/build/new-agent/build#the-build-note')} />,
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={t(($) => $['agentDetail.configure.files.buildNote.tooltip'])}
|
||||
className="size-5 text-text-quaternary hover:text-text-quaternary"
|
||||
iconSize="large"
|
||||
/>
|
||||
<InfotipContent
|
||||
aria-label={t(($) => $['agentDetail.configure.files.buildNote.tooltip'])}
|
||||
className="w-[230px] rounded-xl px-4 py-3.5"
|
||||
>
|
||||
<p className="body-xs-regular text-text-secondary">
|
||||
<Trans
|
||||
i18nKey={($) => $['agentDetail.configure.files.buildNote.richTooltip']}
|
||||
ns="agentV2"
|
||||
components={{
|
||||
docLink: (
|
||||
<DocsLink href={docLink('/use-dify/build/new-agent/build#the-build-note')} />
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
)
|
||||
}
|
||||
|
||||
+6
-3
@@ -16,6 +16,7 @@ import type {
|
||||
AgentTool,
|
||||
} from '@/features/agent-v2/agent-composer/form-state'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { Kbd } from '@langgenius/dify-ui/kbd'
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
|
||||
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
|
||||
@@ -34,7 +35,6 @@ import {
|
||||
} from 'lexical'
|
||||
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import PromptEditor from '@/app/components/base/prompt-editor'
|
||||
import BlockIcon from '@/app/components/workflow/block-icon'
|
||||
import { BlockEnum } from '@/app/components/workflow/types'
|
||||
@@ -1087,8 +1087,11 @@ export function AgentPromptEditor() {
|
||||
>
|
||||
{t(($) => $['agentDetail.configure.prompt.label'])}
|
||||
</h3>
|
||||
<Infotip aria-label={promptTip} popupClassName="max-w-64">
|
||||
<AgentConfigureTipContent type="prompt" />
|
||||
<Infotip>
|
||||
<InfotipTrigger aria-label={promptTip} />
|
||||
<InfotipContent aria-label={promptTip} className="max-w-64">
|
||||
<AgentConfigureTipContent type="prompt" />
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
<Tooltip>
|
||||
|
||||
+17
-13
@@ -12,11 +12,11 @@ import type {
|
||||
} from './working-directory-breadcrumb'
|
||||
import type { AgentFileNode } from '@/features/agent-v2/agent-composer/form-state'
|
||||
import { Dialog } from '@langgenius/dify-ui/dialog'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { Tabs, TabsIndicator, TabsList, TabsPanel, TabsTab } from '@langgenius/dify-ui/tabs'
|
||||
import { skipToken, useMutation, useQueries, useQuery } from '@tanstack/react-query'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import { toast } from '@/app/notifications'
|
||||
import { consoleClient, consoleQuery } from '@/service/console'
|
||||
import { downloadUrl } from '@/utils/download'
|
||||
@@ -575,12 +575,14 @@ export function AgentWorkingDirectoryPanel({
|
||||
>
|
||||
{t(($) => $['agentDetail.configure.workingDirectory.persistentFiles'])}
|
||||
</TabsTab>
|
||||
<Infotip
|
||||
aria-label={persistentFilesTooltip}
|
||||
iconVariant="information"
|
||||
popupClassName="w-64"
|
||||
>
|
||||
{persistentFilesTooltip}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={persistentFilesTooltip}
|
||||
iconVariant="information"
|
||||
/>
|
||||
<InfotipContent aria-label={persistentFilesTooltip} className="w-64">
|
||||
{persistentFilesTooltip}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
<div className="flex h-full items-center gap-0.5">
|
||||
@@ -590,12 +592,14 @@ export function AgentWorkingDirectoryPanel({
|
||||
>
|
||||
{t(($) => $['agentDetail.configure.workingDirectory.temporaryFiles'])}
|
||||
</TabsTab>
|
||||
<Infotip
|
||||
aria-label={temporaryFilesTooltip}
|
||||
iconVariant="information"
|
||||
popupClassName="w-64"
|
||||
>
|
||||
{temporaryFilesTooltip}
|
||||
<Infotip>
|
||||
<InfotipTrigger
|
||||
aria-label={temporaryFilesTooltip}
|
||||
iconVariant="information"
|
||||
/>
|
||||
<InfotipContent aria-label={temporaryFilesTooltip} className="w-64">
|
||||
{temporaryFilesTooltip}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
<TabsIndicator
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
import type { AgentMonitoringChartRow, AgentMonitoringChartType } from './chart-utils'
|
||||
import type { I18nKeysWithPrefix } from '@/types/i18n'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import ReactECharts from 'echarts-for-react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import { buildChartOptions, getChartValueField, getTokenSummary } from './chart-utils'
|
||||
|
||||
type AgentMonitoringChartProps = {
|
||||
@@ -51,7 +51,12 @@ export function AgentMonitoringChart({
|
||||
<h3 className="truncate system-md-semibold text-text-secondary">
|
||||
{t(($) => $[titleKey])}
|
||||
</h3>
|
||||
<Infotip aria-label={t(($) => $[explanationKey])}>{t(($) => $[explanationKey])}</Infotip>
|
||||
<Infotip>
|
||||
<InfotipTrigger aria-label={t(($) => $[explanationKey])} />
|
||||
<InfotipContent aria-label={t(($) => $[explanationKey])}>
|
||||
{t(($) => $[explanationKey])}
|
||||
</InfotipContent>
|
||||
</Infotip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ it('allows shortcut row composition and width constraints without restricting ot
|
||||
const diagnostics = lintFixture(`
|
||||
import { TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
|
||||
import { PopoverContent } from '@langgenius/dify-ui/popover'
|
||||
import { Infotip } from '@/app/components/base/infotip'
|
||||
import { InfotipContent } from '@langgenius/dify-ui/infotip'
|
||||
const examples = <>
|
||||
<TooltipContent className="flex items-center gap-1 max-w-60" />
|
||||
<TooltipContent className="w-58 max-w-65" />
|
||||
@@ -46,7 +46,7 @@ it('allows shortcut row composition and width constraints without restricting ot
|
||||
<TooltipContent />
|
||||
<TooltipTrigger className="border-0 bg-transparent" />
|
||||
<PopoverContent className="shadow-none" />
|
||||
<Infotip popupClassName="shadow-none" />
|
||||
<InfotipContent className="shadow-none" />
|
||||
<div className="shadow-none" />
|
||||
</>
|
||||
`)
|
||||
|
||||
Reference in new issue
Block a user