chore(web): migrate infotip-style tooltips to popover

Switch 13 infotip / static-trigger usages of @langgenius/dify-ui/tooltip
over to @langgenius/dify-ui/popover with openOnHover, so the descriptive
content is reachable on touch and via screen readers. Visual presentation
is preserved by setting popupClassName to match the original tooltip
styling.

Triggers that already own a primary click action (model selector rows,
variable chips, online-user avatars, etc.) are intentionally left on
Tooltip pending a separate selection-with-preview redesign, since
swapping in Popover would have click both fire that action and toggle
the popup.

Made-with: Cursor
This commit is contained in:
yyh 2026-04-19 16:07:06 +08:00
parent 25dd79fba7
commit 49b9b6baef
No known key found for this signature in database
14 changed files with 203 additions and 117 deletions

View File

@ -4,10 +4,10 @@ import type { AppPublisherProps } from './index'
import type { PublishWorkflowParams } from '@/types/workflow'
import { Button } from '@langgenius/dify-ui/button'
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from '@langgenius/dify-ui/tooltip'
Popover,
PopoverContent,
PopoverTrigger,
} from '@langgenius/dify-ui/popover'
import { useTranslation } from 'react-i18next'
import Divider from '@/app/components/base/divider'
import { CodeBrowser } from '@/app/components/base/icons/src/vender/line/development'
@ -240,12 +240,19 @@ const ActionTooltip = ({
return <>{children}</>
return (
<Tooltip>
<TooltipTrigger render={<div className="flex">{children}</div>} />
<TooltipContent>
<Popover>
<PopoverTrigger
openOnHover
nativeButton={false}
render={<div className="flex">{children}</div>}
/>
<PopoverContent
placement="top"
popupClassName="max-w-[300px] rounded-md border-0 bg-components-panel-bg px-3 py-2 text-left system-xs-regular wrap-break-word text-text-tertiary shadow-lg"
>
{tooltip}
</TooltipContent>
</Tooltip>
</PopoverContent>
</Popover>
)
}

View File

@ -4,12 +4,12 @@ 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 { toast } from '@langgenius/dify-ui/toast'
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from '@langgenius/dify-ui/tooltip'
Popover,
PopoverContent,
PopoverTrigger,
} from '@langgenius/dify-ui/popover'
import { toast } from '@langgenius/dify-ui/toast'
import {
RiDeleteBinLine,
RiErrorWarningFill,
@ -183,18 +183,24 @@ const AdvancedPromptInput: FC<Props> = ({
<div className="text-sm font-semibold text-indigo-800 uppercase">
{t('pageTitle.line1', { ns: 'appDebug' })}
</div>
<Tooltip>
<TooltipTrigger
<Popover>
<PopoverTrigger
openOnHover
nativeButton={false}
aria-label={t('promptTip', { ns: 'appDebug' })}
render={(
<span className="ml-1 i-ri-question-line h-4 w-4 shrink-0 text-text-quaternary" />
)}
/>
<TooltipContent>
<PopoverContent
placement="top"
popupClassName="max-w-[300px] rounded-md border-0 bg-components-panel-bg px-3 py-2 text-left system-xs-regular wrap-break-word text-text-tertiary shadow-lg"
>
<div className="w-[180px]">
{t('promptTip', { ns: 'appDebug' })}
</div>
</TooltipContent>
</Tooltip>
</PopoverContent>
</Popover>
</div>
)}
<div className={cn(s.optionWrap, 'items-center space-x-1')}>

View File

@ -4,12 +4,12 @@ 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 { toast } from '@langgenius/dify-ui/toast'
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from '@langgenius/dify-ui/tooltip'
Popover,
PopoverContent,
PopoverTrigger,
} from '@langgenius/dify-ui/popover'
import { toast } from '@langgenius/dify-ui/toast'
import { useBoolean } from 'ahooks'
import { noop } from 'es-toolkit/function'
import { produce } from 'immer'
@ -183,18 +183,24 @@ const Prompt: FC<ISimplePromptInput> = ({
<div className="flex items-center space-x-1">
<div className="system-sm-semibold-uppercase text-text-secondary">{mode !== AppModeEnum.COMPLETION ? t('chatSubTitle', { ns: 'appDebug' }) : t('completionSubTitle', { ns: 'appDebug' })}</div>
{!readonly && (
<Tooltip>
<TooltipTrigger
<Popover>
<PopoverTrigger
openOnHover
nativeButton={false}
aria-label={t('promptTip', { ns: 'appDebug' })}
render={(
<span className="ml-1 i-ri-question-line h-4 w-4 shrink-0 text-text-quaternary" />
)}
/>
<TooltipContent>
<PopoverContent
placement="top"
popupClassName="max-w-[300px] rounded-md border-0 bg-components-panel-bg px-3 py-2 text-left system-xs-regular wrap-break-word text-text-tertiary shadow-lg"
>
<div className="w-[180px]">
{t('promptTip', { ns: 'appDebug' })}
</div>
</TooltipContent>
</Tooltip>
</PopoverContent>
</Popover>
)}
</div>
<div className="flex items-center">

View File

@ -16,10 +16,10 @@ import {
} from '@langgenius/dify-ui/alert-dialog'
import { Button } from '@langgenius/dify-ui/button'
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from '@langgenius/dify-ui/tooltip'
Popover,
PopoverContent,
PopoverTrigger,
} from '@langgenius/dify-ui/popover'
import { RiArrowRightSLine, RiBookOpenLine, RiBuildingLine, RiEqualizer2Line, RiExternalLinkLine, RiGlobalLine, RiLockLine, RiPaintBrushLine, RiVerifiedBadgeLine, RiWindowLine } from '@remixicon/react'
import CopyFeedback from '@/app/components/base/copy-feedback'
import Divider from '@/app/components/base/divider'
@ -87,12 +87,19 @@ const MaybeTooltip = ({
return <>{children}</>
return (
<Tooltip>
<TooltipTrigger render={<div>{children}</div>} />
<TooltipContent className={tooltipClassName}>
<Popover>
<PopoverTrigger
openOnHover
nativeButton={false}
render={<div>{children}</div>}
/>
<PopoverContent
placement="top"
popupClassName={`max-w-[300px] rounded-md border-0 bg-components-panel-bg px-3 py-2 text-left system-xs-regular wrap-break-word text-text-tertiary shadow-lg ${tooltipClassName ?? ''}`}
>
{content}
</TooltipContent>
</Tooltip>
</PopoverContent>
</Popover>
)
}

View File

@ -1,7 +1,7 @@
import type { InvitationResult } from '@/models/common'
import { Button } from '@langgenius/dify-ui/button'
import { Dialog, DialogCloseButton, DialogContent, DialogTitle } from '@langgenius/dify-ui/dialog'
import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import { Popover, PopoverContent, PopoverTrigger } from '@langgenius/dify-ui/popover'
import { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import { IS_CE_EDITION } from '@/config'
@ -73,8 +73,11 @@ const InvitedModal = ({
{
failedInvitationResults.map(item => (
<div key={item.email} className="flex justify-center rounded-md border border-red-300 bg-orange-50 px-1">
<Tooltip>
<TooltipTrigger
<Popover>
<PopoverTrigger
openOnHover
nativeButton={false}
aria-label={item.message}
render={(
<div className="flex items-center justify-center gap-1 text-sm">
{item.email}
@ -82,10 +85,13 @@ const InvitedModal = ({
</div>
)}
/>
<TooltipContent>
<PopoverContent
placement="top"
popupClassName="max-w-[300px] rounded-md border-0 bg-components-panel-bg px-3 py-2 text-left system-xs-regular wrap-break-word text-text-tertiary shadow-lg"
>
{item.message}
</TooltipContent>
</Tooltip>
</PopoverContent>
</Popover>
</div>
),
)

View File

@ -4,10 +4,10 @@ import type {
NodeOutPutVar,
} from '@/app/components/workflow/types'
import { cn } from '@langgenius/dify-ui/cn'
import { Popover, PopoverContent, PopoverTrigger } from '@langgenius/dify-ui/popover'
import { Select, SelectContent, SelectItem, SelectItemIndicator, SelectItemText, SelectTrigger, SelectValue } from '@langgenius/dify-ui/select'
import { Slider } from '@langgenius/dify-ui/slider'
import { Switch } from '@langgenius/dify-ui/switch'
import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import { useEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import PromptEditor from '@/app/components/base/prompt-editor'
@ -349,18 +349,24 @@ function ParameterItem({
</div>
{
parameterRule.help && (
<Tooltip>
<TooltipTrigger
<Popover>
<PopoverTrigger
openOnHover
nativeButton={false}
aria-label={parameterRule.help[language] || parameterRule.help.en_US}
render={(
<span className="mr-1 flex h-4 w-4 shrink-0 items-center justify-center">
<span aria-hidden className="i-ri-question-line h-3.5 w-3.5 text-text-quaternary" />
</span>
)}
/>
<TooltipContent className="mr-1">
<PopoverContent
placement="top"
popupClassName="mr-1 max-w-[300px] rounded-md border-0 bg-components-panel-bg px-3 py-2 text-left system-xs-regular wrap-break-word text-text-tertiary shadow-lg"
>
<div className="w-[150px] whitespace-pre-wrap">{parameterRule.help[language] || parameterRule.help.en_US}</div>
</TooltipContent>
</Tooltip>
</PopoverContent>
</Popover>
)
}
</div>

View File

@ -1,6 +1,6 @@
import type { UsagePriority } from '../use-credential-panel-state'
import { cn } from '@langgenius/dify-ui/cn'
import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import { Popover, PopoverContent, PopoverTrigger } from '@langgenius/dify-ui/popover'
import { useTranslation } from 'react-i18next'
import { PreferredProviderTypeEnum } from '../../declarations'
@ -31,8 +31,10 @@ export default function UsagePrioritySection({ value, disabled, onSelect }: Usag
<span className="truncate system-sm-medium text-text-secondary">
{t('modelProvider.card.usagePriority', { ns: 'common' })}
</span>
<Tooltip>
<TooltipTrigger
<Popover>
<PopoverTrigger
openOnHover
nativeButton={false}
aria-label={t('modelProvider.card.usagePriorityTip', { ns: 'common' })}
render={(
<span className="flex h-4 w-4 shrink-0 items-center justify-center">
@ -40,10 +42,13 @@ export default function UsagePrioritySection({ value, disabled, onSelect }: Usag
</span>
)}
/>
<TooltipContent>
<PopoverContent
placement="top"
popupClassName="max-w-[300px] rounded-md border-0 bg-components-panel-bg px-3 py-2 text-left system-xs-regular wrap-break-word text-text-tertiary shadow-lg"
>
{t('modelProvider.card.usagePriorityTip', { ns: 'common' })}
</TooltipContent>
</Tooltip>
</PopoverContent>
</Popover>
</div>
<div className="flex shrink-0 items-center gap-1">
{options.map(option => (

View File

@ -3,6 +3,7 @@ import type { ModelProvider } from '../declarations'
import type { Plugin } from '@/app/components/plugins/types'
import type { ModelProviderQuotaGetPaid } from '@/types/model-provider'
import { cn } from '@langgenius/dify-ui/cn'
import { Popover, PopoverContent, PopoverTrigger } from '@langgenius/dify-ui/popover'
import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import { useBoolean } from 'ahooks'
import * as React from 'react'
@ -99,8 +100,10 @@ const QuotaPanel: FC<QuotaPanelProps> = ({
<div className="relative">
<div className="mb-2 flex h-4 items-center system-xs-medium-uppercase text-text-tertiary">
{t('modelProvider.quota', { ns: 'common' })}
<Tooltip>
<TooltipTrigger
<Popover>
<PopoverTrigger
openOnHover
nativeButton={false}
aria-label={tipText}
render={(
<span className="ml-0.5 flex h-4 w-4 shrink-0 items-center justify-center">
@ -108,10 +111,13 @@ const QuotaPanel: FC<QuotaPanelProps> = ({
</span>
)}
/>
<TooltipContent>
<PopoverContent
placement="top"
popupClassName="max-w-[300px] rounded-md border-0 bg-components-panel-bg px-3 py-2 text-left system-xs-regular wrap-break-word text-text-tertiary shadow-lg"
>
{tipText}
</TooltipContent>
</Tooltip>
</PopoverContent>
</Popover>
</div>
<div className="flex items-center justify-between">
<div className="flex items-center gap-1 text-xs text-text-tertiary">

View File

@ -10,12 +10,12 @@ import {
DialogContent,
DialogTitle,
} from '@langgenius/dify-ui/dialog'
import { toast } from '@langgenius/dify-ui/toast'
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from '@langgenius/dify-ui/tooltip'
Popover,
PopoverContent,
PopoverTrigger,
} from '@langgenius/dify-ui/popover'
import { toast } from '@langgenius/dify-ui/toast'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useAppContext } from '@/context/app-context'
@ -138,8 +138,10 @@ const SystemModel: FC<SystemModelSelectorProps> = ({
return (
<div className="flex min-h-6 items-center text-[13px] font-medium text-text-secondary">
{t(labelKey, { ns: 'common' })}
<Tooltip>
<TooltipTrigger
<Popover>
<PopoverTrigger
openOnHover
nativeButton={false}
aria-label={tipText}
render={(
<span className="ml-0.5 flex h-4 w-4 shrink-0 items-center justify-center">
@ -147,12 +149,15 @@ const SystemModel: FC<SystemModelSelectorProps> = ({
</span>
)}
/>
<TooltipContent>
<PopoverContent
placement="top"
popupClassName="max-w-[300px] rounded-md border-0 bg-components-panel-bg px-3 py-2 text-left system-xs-regular wrap-break-word text-text-tertiary shadow-lg"
>
<div className="w-[261px] text-text-tertiary">
{tipText}
</div>
</TooltipContent>
</Tooltip>
</PopoverContent>
</Popover>
</div>
)
}

View File

@ -1,10 +1,10 @@
import { fireEvent, render, screen } from '@testing-library/react'
import { FieldTitle } from '../field-title'
vi.mock('@langgenius/dify-ui/tooltip', () => ({
Tooltip: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
TooltipTrigger: ({ render }: { render: React.ReactNode }) => <>{render}</>,
TooltipContent: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
vi.mock('@langgenius/dify-ui/popover', () => ({
Popover: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
PopoverTrigger: ({ render }: { render: React.ReactNode }) => <>{render}</>,
PopoverContent: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
}))
describe('FieldTitle', () => {

View File

@ -1,6 +1,6 @@
import type { ReactNode } from 'react'
import { cn } from '@langgenius/dify-ui/cn'
import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import { Popover, PopoverContent, PopoverTrigger } from '@langgenius/dify-ui/popover'
import {
memo,
useState,
@ -62,18 +62,25 @@ export const FieldTitle = memo(({
}
{
tooltip && (
<Tooltip>
<TooltipTrigger
<Popover>
<PopoverTrigger
openOnHover
nativeButton={false}
aria-label={tooltip}
onClick={e => e.stopPropagation()}
render={(
<span className="ml-1 flex h-4 w-4 shrink-0 items-center justify-center">
<span aria-hidden className="i-ri-question-line h-3.5 w-3.5 text-text-quaternary hover:text-text-tertiary" />
</span>
)}
/>
<TooltipContent>
<PopoverContent
placement="top"
popupClassName="max-w-[300px] rounded-md border-0 bg-components-panel-bg px-3 py-2 text-left system-xs-regular wrap-break-word text-text-tertiary shadow-lg"
>
{tooltip}
</TooltipContent>
</Tooltip>
</PopoverContent>
</Popover>
)
}
</div>

View File

@ -2,7 +2,7 @@ import type { TFunction } 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 { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import { Popover, PopoverContent, PopoverTrigger } from '@langgenius/dify-ui/popover'
import { BlockEnum, NodeRunningStatus } from '@/app/components/workflow/types'
type HeaderMetaProps = {
@ -23,19 +23,27 @@ export const NodeHeaderMeta = ({
return (
<>
{data.type === BlockEnum.Iteration && (data as IterationNodeType).is_parallel && (
<Tooltip>
<TooltipTrigger>
<div className="ml-1 flex items-center justify-center rounded-[5px] border border-text-warning px-[5px] py-[3px] system-2xs-medium-uppercase text-text-warning">
{t('nodes.iteration.parallelModeUpper', { ns: 'workflow' })}
</div>
</TooltipTrigger>
<TooltipContent className="w-[180px]">
<Popover>
<PopoverTrigger
openOnHover
nativeButton={false}
aria-label={t('nodes.iteration.parallelModeEnableTitle', { ns: 'workflow' })}
render={(
<div className="ml-1 flex items-center justify-center rounded-[5px] border border-text-warning px-[5px] py-[3px] system-2xs-medium-uppercase text-text-warning">
{t('nodes.iteration.parallelModeUpper', { ns: 'workflow' })}
</div>
)}
/>
<PopoverContent
placement="top"
popupClassName="w-[180px] rounded-md border-0 bg-components-panel-bg px-3 py-2 text-left system-xs-regular wrap-break-word text-text-tertiary shadow-lg"
>
<div className="font-extrabold">
{t('nodes.iteration.parallelModeEnableTitle', { ns: 'workflow' })}
</div>
{t('nodes.iteration.parallelModeEnableDesc', { ns: 'workflow' })}
</TooltipContent>
</Tooltip>
</PopoverContent>
</Popover>
)}
{!!(data._iterationLength && data._iterationIndex && data._runningStatus === NodeRunningStatus.Running) && (
<div className="mr-1.5 text-xs font-medium text-text-accent">

View File

@ -1,7 +1,7 @@
import type { FC } from 'react'
import type { LLMNodeType } from '../types'
import type { Memory, Node, NodeOutPutVar } from '@/app/components/workflow/types'
import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import { Popover, PopoverContent, PopoverTrigger } from '@langgenius/dify-ui/popover'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import MemoryConfig from '@/app/components/workflow/nodes/_base/components/memory-config'
@ -50,18 +50,24 @@ const PanelMemorySection: FC<Props> = ({
<div className="flex h-8 items-center justify-between rounded-lg bg-components-input-bg-normal pr-2 pl-3">
<div className="flex items-center space-x-1">
<div className="text-xs font-semibold text-text-secondary uppercase">{t('nodes.common.memories.title', { ns: 'workflow' })}</div>
<Tooltip>
<TooltipTrigger
<Popover>
<PopoverTrigger
openOnHover
nativeButton={false}
aria-label={t('nodes.common.memories.tip', { ns: 'workflow' })}
render={(
<span className="ml-1 flex h-4 w-4 shrink-0 items-center justify-center">
<span aria-hidden className="i-ri-question-line h-3.5 w-3.5 text-text-quaternary hover:text-text-tertiary" />
</span>
)}
/>
<TooltipContent>
<PopoverContent
placement="top"
popupClassName="max-w-[300px] rounded-md border-0 bg-components-panel-bg px-3 py-2 text-left system-xs-regular wrap-break-word text-text-tertiary shadow-lg"
>
{t('nodes.common.memories.tip', { ns: 'workflow' })}
</TooltipContent>
</Tooltip>
</PopoverContent>
</Popover>
</div>
<div className="flex h-[18px] items-center rounded-[5px] border border-divider-deep bg-components-badge-bg-dimm px-1 text-xs font-semibold text-text-tertiary uppercase">
{t('nodes.common.memories.builtIn', { ns: 'workflow' })}
@ -72,18 +78,24 @@ 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>
<Tooltip>
<TooltipTrigger
<Popover>
<PopoverTrigger
openOnHover
nativeButton={false}
aria-label={t('nodes.llm.roleDescription.user', { ns: 'workflow' })}
render={(
<span className="ml-1 flex h-4 w-4 shrink-0 items-center justify-center">
<span aria-hidden className="i-ri-question-line h-3.5 w-3.5 text-text-quaternary hover:text-text-tertiary" />
</span>
)}
/>
<TooltipContent>
<PopoverContent
placement="top"
popupClassName="max-w-[300px] rounded-md border-0 bg-components-panel-bg px-3 py-2 text-left system-xs-regular wrap-break-word text-text-tertiary shadow-lg"
>
<div className="max-w-[180px]">{t('nodes.llm.roleDescription.user', { ns: 'workflow' })}</div>
</TooltipContent>
</Tooltip>
</PopoverContent>
</Popover>
</div>
)}
value={inputs.memory.query_prompt_template || '{{#sys.query#}}'}

View File

@ -1,8 +1,7 @@
import type { FC } from 'react'
import type { LLMNodeType, StructuredOutput } from '../types'
import { Popover, PopoverContent, PopoverTrigger } from '@langgenius/dify-ui/popover'
import { Switch } from '@langgenius/dify-ui/switch'
import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import { RiAlertFill, RiQuestionLine } from '@remixicon/react'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import OutputVars, { VarItem } from '@/app/components/workflow/nodes/_base/components/output-vars'
@ -41,35 +40,41 @@ const PanelOutputSection: FC<Props> = ({
operations={(
<div className="mr-4 flex shrink-0 items-center">
{(!isModelSupportStructuredOutput && !!inputs.structured_output_enabled) && (
<Tooltip>
<TooltipTrigger
<Popover>
<PopoverTrigger
openOnHover
nativeButton={false}
aria-label={t('structOutput.modelNotSupported', { ns: 'app' })}
render={(
<div>
<RiAlertFill className="mr-1 size-4 text-text-warning-secondary" />
<div className="mr-1">
<span aria-hidden className="i-ri-alert-fill block size-4 text-text-warning-secondary" />
</div>
)}
/>
<TooltipContent>
<PopoverContent popupClassName="max-w-[300px] rounded-md border-0 bg-components-panel-bg px-3 py-2 system-xs-regular text-text-tertiary shadow-lg">
<div className="w-[232px] rounded-xl border-[0.5px] border-components-panel-border bg-components-tooltip-bg px-4 py-3.5 shadow-lg backdrop-blur-[5px]">
<div className="title-xs-semi-bold text-text-primary">{t('structOutput.modelNotSupported', { ns: 'app' })}</div>
<div className="mt-1 body-xs-regular text-text-secondary">{t('structOutput.modelNotSupportedTip', { ns: 'app' })}</div>
</div>
</TooltipContent>
</Tooltip>
</PopoverContent>
</Popover>
)}
<div className="mr-0.5 system-xs-medium-uppercase text-text-tertiary">{t('structOutput.structured', { ns: 'app' })}</div>
<Tooltip>
<TooltipTrigger
<Popover>
<PopoverTrigger
openOnHover
nativeButton={false}
aria-label={t('structOutput.structuredTip', { ns: 'app' })}
render={(
<div>
<RiQuestionLine className="size-3.5 text-text-quaternary" />
<span aria-hidden className="i-ri-question-line block size-3.5 text-text-quaternary" />
</div>
)}
/>
<TooltipContent>
<PopoverContent popupClassName="max-w-[300px] rounded-md border-0 bg-components-panel-bg px-3 py-2 system-xs-regular text-text-tertiary shadow-lg">
<div className="max-w-[150px]">{t('structOutput.structuredTip', { ns: 'app' })}</div>
</TooltipContent>
</Tooltip>
</PopoverContent>
</Popover>
<Switch
className="ml-2"
checked={!!inputs.structured_output_enabled}