chore(web): new lint setup (#30020)

Co-authored-by: yyh <yuanyouhuilyz@gmail.com>
This commit is contained in:
Stephen Zhou
2025-12-23 16:58:55 +08:00
committed by GitHub
parent 9701a2994b
commit f2842da397
3356 changed files with 85046 additions and 81278 deletions

View File

@ -1,18 +1,18 @@
'use client'
import { useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useContext } from 'use-context-selector'
import type { ToolWithProvider } from '@/app/components/workflow/types'
import {
RiAddCircleFill,
RiArrowRightUpLine,
RiBookOpenLine,
} from '@remixicon/react'
import MCPModal from './modal'
import { useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useContext } from 'use-context-selector'
import { useAppContext } from '@/context/app-context'
import I18n from '@/context/i18n'
import { getLanguage } from '@/i18n-config/language'
import { useAppContext } from '@/context/app-context'
import { useCreateMCP } from '@/service/use-tools'
import type { ToolWithProvider } from '@/app/components/workflow/types'
import MCPModal from './modal'
type Props = {
handleCreate: (provider: ToolWithProvider) => void
@ -44,20 +44,20 @@ const NewMCPCard = ({ handleCreate }: Props) => {
return (
<>
{isCurrentWorkspaceManager && (
<div className='col-span-1 flex min-h-[108px] cursor-pointer flex-col rounded-xl bg-background-default-dimmed transition-all duration-200 ease-in-out'>
<div className='group grow rounded-t-xl' onClick={() => setShowModal(true)}>
<div className='flex shrink-0 items-center p-4 pb-3'>
<div className='flex h-10 w-10 items-center justify-center rounded-lg border border-dashed border-divider-deep group-hover:border-solid group-hover:border-state-accent-hover-alt group-hover:bg-state-accent-hover'>
<RiAddCircleFill className='h-4 w-4 text-text-quaternary group-hover:text-text-accent'/>
<div className="col-span-1 flex min-h-[108px] cursor-pointer flex-col rounded-xl bg-background-default-dimmed transition-all duration-200 ease-in-out">
<div className="group grow rounded-t-xl" onClick={() => setShowModal(true)}>
<div className="flex shrink-0 items-center p-4 pb-3">
<div className="flex h-10 w-10 items-center justify-center rounded-lg border border-dashed border-divider-deep group-hover:border-solid group-hover:border-state-accent-hover-alt group-hover:bg-state-accent-hover">
<RiAddCircleFill className="h-4 w-4 text-text-quaternary group-hover:text-text-accent" />
</div>
<div className='system-md-semibold ml-3 text-text-secondary group-hover:text-text-accent'>{t('tools.mcp.create.cardTitle')}</div>
<div className="system-md-semibold ml-3 text-text-secondary group-hover:text-text-accent">{t('tools.mcp.create.cardTitle')}</div>
</div>
</div>
<div className='rounded-b-xl border-t-[0.5px] border-divider-subtle px-4 py-3 text-text-tertiary hover:text-text-accent'>
<a href={linkUrl} target='_blank' rel='noopener noreferrer' className='flex items-center space-x-1'>
<RiBookOpenLine className='h-3 w-3 shrink-0' />
<div className='system-xs-regular grow truncate' title={t('tools.mcp.create.cardLink') || ''}>{t('tools.mcp.create.cardLink')}</div>
<RiArrowRightUpLine className='h-3 w-3 shrink-0' />
<div className="rounded-b-xl border-t-[0.5px] border-divider-subtle px-4 py-3 text-text-tertiary hover:text-text-accent">
<a href={linkUrl} target="_blank" rel="noopener noreferrer" className="flex items-center space-x-1">
<RiBookOpenLine className="h-3 w-3 shrink-0" />
<div className="system-xs-regular grow truncate" title={t('tools.mcp.create.cardLink') || ''}>{t('tools.mcp.create.cardLink')}</div>
<RiArrowRightUpLine className="h-3 w-3 shrink-0" />
</a>
</div>
</div>

View File

@ -1,26 +1,23 @@
'use client'
import React, { useCallback, useEffect } from 'react'
import type { FC } from 'react'
import { useBoolean } from 'ahooks'
import copy from 'copy-to-clipboard'
import { useTranslation } from 'react-i18next'
import { useAppContext } from '@/context/app-context'
import type { ToolWithProvider } from '../../../workflow/types'
import {
RiCloseLine,
RiLoader2Line,
RiLoopLeftLine,
} from '@remixicon/react'
import type { ToolWithProvider } from '../../../workflow/types'
import Icon from '@/app/components/plugins/card/base/card-icon'
import { useBoolean } from 'ahooks'
import copy from 'copy-to-clipboard'
import React, { useCallback, useEffect } from 'react'
import { useTranslation } from 'react-i18next'
import ActionButton from '@/app/components/base/action-button'
import Button from '@/app/components/base/button'
import Confirm from '@/app/components/base/confirm'
import Indicator from '@/app/components/header/indicator'
import Tooltip from '@/app/components/base/tooltip'
import MCPModal from '../modal'
import OperationDropdown from './operation-dropdown'
import ListLoading from './list-loading'
import ToolItem from './tool-item'
import Indicator from '@/app/components/header/indicator'
import Icon from '@/app/components/plugins/card/base/card-icon'
import { useAppContext } from '@/context/app-context'
import { openOAuthPopup } from '@/hooks/use-oauth'
import {
useAuthorizeMCP,
useDeleteMCP,
@ -29,8 +26,11 @@ import {
useUpdateMCP,
useUpdateMCPTools,
} from '@/service/use-tools'
import { openOAuthPopup } from '@/hooks/use-oauth'
import { cn } from '@/utils/classnames'
import MCPModal from '../modal'
import ListLoading from './list-loading'
import OperationDropdown from './operation-dropdown'
import ToolItem from './tool-item'
type Props = {
detail: ToolWithProvider
@ -149,50 +149,50 @@ const MCPDetailContent: FC<Props> = ({
return (
<>
<div className={cn('shrink-0 border-b border-divider-subtle bg-components-panel-bg p-4 pb-3')}>
<div className='flex'>
<div className='shrink-0 overflow-hidden rounded-xl border border-components-panel-border-subtle'>
<div className="flex">
<div className="shrink-0 overflow-hidden rounded-xl border border-components-panel-border-subtle">
<Icon src={detail.icon} />
</div>
<div className='ml-3 w-0 grow'>
<div className='flex h-5 items-center'>
<div className='system-md-semibold truncate text-text-primary' title={detail.name}>{detail.name}</div>
<div className="ml-3 w-0 grow">
<div className="flex h-5 items-center">
<div className="system-md-semibold truncate text-text-primary" title={detail.name}>{detail.name}</div>
</div>
<div className='mt-0.5 flex items-center gap-1'>
<div className="mt-0.5 flex items-center gap-1">
<Tooltip popupContent={t('tools.mcp.identifier')}>
<div className='system-xs-regular shrink-0 cursor-pointer text-text-secondary' onClick={() => copy(detail.server_identifier || '')}>{detail.server_identifier}</div>
<div className="system-xs-regular shrink-0 cursor-pointer text-text-secondary" onClick={() => copy(detail.server_identifier || '')}>{detail.server_identifier}</div>
</Tooltip>
<div className='system-xs-regular shrink-0 text-text-quaternary'>·</div>
<div className="system-xs-regular shrink-0 text-text-quaternary">·</div>
<Tooltip popupContent={t('tools.mcp.modal.serverUrl')}>
<div className='system-xs-regular truncate text-text-secondary'>{detail.server_url}</div>
<div className="system-xs-regular truncate text-text-secondary">{detail.server_url}</div>
</Tooltip>
</div>
</div>
<div className='flex gap-1'>
<div className="flex gap-1">
<OperationDropdown
onEdit={showUpdateModal}
onRemove={showDeleteConfirm}
/>
<ActionButton onClick={onHide}>
<RiCloseLine className='h-4 w-4' />
<RiCloseLine className="h-4 w-4" />
</ActionButton>
</div>
</div>
<div className='mt-5'>
<div className="mt-5">
{!isAuthorizing && detail.is_team_authorization && (
<Button
variant='secondary'
className='w-full'
variant="secondary"
className="w-full"
onClick={handleAuthorize}
disabled={!isCurrentWorkspaceManager}
>
<Indicator className='mr-2' color={'green'} />
<Indicator className="mr-2" color="green" />
{t('tools.auth.authorized')}
</Button>
)}
{!detail.is_team_authorization && !isAuthorizing && (
<Button
variant='primary'
className='w-full'
variant="primary"
className="w-full"
onClick={handleAuthorize}
disabled={!isCurrentWorkspaceManager}
>
@ -201,8 +201,8 @@ const MCPDetailContent: FC<Props> = ({
)}
{isAuthorizing && (
<Button
variant='primary'
className='w-full'
variant="primary"
className="w-full"
disabled
>
<RiLoader2Line className={cn('mr-1 h-4 w-4 animate-spin')} />
@ -211,45 +211,47 @@ const MCPDetailContent: FC<Props> = ({
)}
</div>
</div>
<div className='flex grow flex-col'>
<div className="flex grow flex-col">
{((detail.is_team_authorization && isGettingTools) || isUpdating) && (
<>
<div className='flex shrink-0 justify-between gap-2 px-4 pb-1 pt-2'>
<div className='flex h-6 items-center'>
{!isUpdating && <div className='system-sm-semibold-uppercase text-text-secondary'>{t('tools.mcp.gettingTools')}</div>}
{isUpdating && <div className='system-sm-semibold-uppercase text-text-secondary'>{t('tools.mcp.updateTools')}</div>}
<div className="flex shrink-0 justify-between gap-2 px-4 pb-1 pt-2">
<div className="flex h-6 items-center">
{!isUpdating && <div className="system-sm-semibold-uppercase text-text-secondary">{t('tools.mcp.gettingTools')}</div>}
{isUpdating && <div className="system-sm-semibold-uppercase text-text-secondary">{t('tools.mcp.updateTools')}</div>}
</div>
<div></div>
</div>
<div className='flex h-full w-full grow flex-col overflow-hidden px-4 pb-4'>
<div className="flex h-full w-full grow flex-col overflow-hidden px-4 pb-4">
<ListLoading />
</div>
</>
)}
{!isUpdating && detail.is_team_authorization && !isGettingTools && !toolList.length && (
<div className='flex h-full w-full flex-col items-center justify-center'>
<div className='system-sm-regular mb-3 text-text-tertiary'>{t('tools.mcp.toolsEmpty')}</div>
<div className="flex h-full w-full flex-col items-center justify-center">
<div className="system-sm-regular mb-3 text-text-tertiary">{t('tools.mcp.toolsEmpty')}</div>
<Button
variant='primary'
variant="primary"
onClick={handleUpdateTools}
>{t('tools.mcp.getTools')}</Button>
>
{t('tools.mcp.getTools')}
</Button>
</div>
)}
{!isUpdating && !isGettingTools && toolList.length > 0 && (
<>
<div className='flex shrink-0 justify-between gap-2 px-4 pb-1 pt-2'>
<div className='flex h-6 items-center'>
{toolList.length > 1 && <div className='system-sm-semibold-uppercase text-text-secondary'>{t('tools.mcp.toolsNum', { count: toolList.length })}</div>}
{toolList.length === 1 && <div className='system-sm-semibold-uppercase text-text-secondary'>{t('tools.mcp.onlyTool')}</div>}
<div className="flex shrink-0 justify-between gap-2 px-4 pb-1 pt-2">
<div className="flex h-6 items-center">
{toolList.length > 1 && <div className="system-sm-semibold-uppercase text-text-secondary">{t('tools.mcp.toolsNum', { count: toolList.length })}</div>}
{toolList.length === 1 && <div className="system-sm-semibold-uppercase text-text-secondary">{t('tools.mcp.onlyTool')}</div>}
</div>
<div>
<Button size='small' onClick={showUpdateConfirm}>
<RiLoopLeftLine className='mr-1 h-3.5 w-3.5' />
<Button size="small" onClick={showUpdateConfirm}>
<RiLoopLeftLine className="mr-1 h-3.5 w-3.5" />
{t('tools.mcp.update')}
</Button>
</div>
</div>
<div className='flex h-0 w-full grow flex-col gap-2 overflow-y-auto px-4 pb-4'>
<div className="flex h-0 w-full grow flex-col gap-2 overflow-y-auto px-4 pb-4">
{toolList.map(tool => (
<ToolItem
key={`${detail.id}${tool.name}`}
@ -261,10 +263,10 @@ const MCPDetailContent: FC<Props> = ({
)}
{!isUpdating && !detail.is_team_authorization && (
<div className='flex h-full w-full flex-col items-center justify-center'>
{!isAuthorizing && <div className='system-md-medium mb-1 text-text-secondary'>{t('tools.mcp.authorizingRequired')}</div>}
{isAuthorizing && <div className='system-md-medium mb-1 text-text-secondary'>{t('tools.mcp.authorizing')}</div>}
<div className='system-sm-regular text-text-tertiary'>{t('tools.mcp.authorizeTip')}</div>
<div className="flex h-full w-full flex-col items-center justify-center">
{!isAuthorizing && <div className="system-md-medium mb-1 text-text-secondary">{t('tools.mcp.authorizingRequired')}</div>}
{isAuthorizing && <div className="system-md-medium mb-1 text-text-secondary">{t('tools.mcp.authorizing')}</div>}
<div className="system-sm-regular text-text-tertiary">{t('tools.mcp.authorizeTip')}</div>
</div>
)}
</div>
@ -280,11 +282,11 @@ const MCPDetailContent: FC<Props> = ({
<Confirm
isShow
title={t('tools.mcp.delete')}
content={
content={(
<div>
{t('tools.mcp.deleteConfirmTitle', { mcp: detail.name })}
</div>
}
)}
onCancel={hideDeleteConfirm}
onConfirm={handleDelete}
isLoading={deleting}

View File

@ -5,30 +5,30 @@ import { cn } from '@/utils/classnames'
const ListLoading = () => {
return (
<div className={cn('space-y-2')}>
<div className='space-y-3 rounded-xl bg-components-panel-on-panel-item-bg-hover p-4'>
<div className='h-2 w-[180px] rounded-sm bg-text-quaternary opacity-20'></div>
<div className='h-2 rounded-sm bg-text-quaternary opacity-10'></div>
<div className='mr-10 h-2 rounded-sm bg-text-quaternary opacity-10'></div>
<div className="space-y-3 rounded-xl bg-components-panel-on-panel-item-bg-hover p-4">
<div className="h-2 w-[180px] rounded-sm bg-text-quaternary opacity-20"></div>
<div className="h-2 rounded-sm bg-text-quaternary opacity-10"></div>
<div className="mr-10 h-2 rounded-sm bg-text-quaternary opacity-10"></div>
</div>
<div className='space-y-3 rounded-xl bg-components-panel-on-panel-item-bg-hover p-4'>
<div className='h-2 w-[148px] rounded-sm bg-text-quaternary opacity-20'></div>
<div className='h-2 rounded-sm bg-text-quaternary opacity-10'></div>
<div className='mr-10 h-2 rounded-sm bg-text-quaternary opacity-10'></div>
<div className="space-y-3 rounded-xl bg-components-panel-on-panel-item-bg-hover p-4">
<div className="h-2 w-[148px] rounded-sm bg-text-quaternary opacity-20"></div>
<div className="h-2 rounded-sm bg-text-quaternary opacity-10"></div>
<div className="mr-10 h-2 rounded-sm bg-text-quaternary opacity-10"></div>
</div>
<div className='space-y-3 rounded-xl bg-components-panel-on-panel-item-bg-hover p-4'>
<div className='h-2 w-[196px] rounded-sm bg-text-quaternary opacity-20'></div>
<div className='h-2 rounded-sm bg-text-quaternary opacity-10'></div>
<div className='mr-10 h-2 rounded-sm bg-text-quaternary opacity-10'></div>
<div className="space-y-3 rounded-xl bg-components-panel-on-panel-item-bg-hover p-4">
<div className="h-2 w-[196px] rounded-sm bg-text-quaternary opacity-20"></div>
<div className="h-2 rounded-sm bg-text-quaternary opacity-10"></div>
<div className="mr-10 h-2 rounded-sm bg-text-quaternary opacity-10"></div>
</div>
<div className='space-y-3 rounded-xl bg-components-panel-on-panel-item-bg-hover p-4'>
<div className='h-2 w-[148px] rounded-sm bg-text-quaternary opacity-20'></div>
<div className='h-2 rounded-sm bg-text-quaternary opacity-10'></div>
<div className='mr-10 h-2 rounded-sm bg-text-quaternary opacity-10'></div>
<div className="space-y-3 rounded-xl bg-components-panel-on-panel-item-bg-hover p-4">
<div className="h-2 w-[148px] rounded-sm bg-text-quaternary opacity-20"></div>
<div className="h-2 rounded-sm bg-text-quaternary opacity-10"></div>
<div className="mr-10 h-2 rounded-sm bg-text-quaternary opacity-10"></div>
</div>
<div className='space-y-3 rounded-xl bg-components-panel-on-panel-item-bg-hover p-4'>
<div className='h-2 w-[180px] rounded-sm bg-text-quaternary opacity-20'></div>
<div className='h-2 rounded-sm bg-text-quaternary opacity-10'></div>
<div className='mr-10 h-2 rounded-sm bg-text-quaternary opacity-10'></div>
<div className="space-y-3 rounded-xl bg-components-panel-on-panel-item-bg-hover p-4">
<div className="h-2 w-[180px] rounded-sm bg-text-quaternary opacity-20"></div>
<div className="h-2 rounded-sm bg-text-quaternary opacity-10"></div>
<div className="mr-10 h-2 rounded-sm bg-text-quaternary opacity-10"></div>
</div>
</div>
)

View File

@ -1,12 +1,12 @@
'use client'
import type { FC } from 'react'
import React, { useCallback, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import {
RiDeleteBinLine,
RiEditLine,
RiMoreFill,
} from '@remixicon/react'
import React, { useCallback, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import ActionButton from '@/app/components/base/action-button'
import {
PortalToFollowElem,
@ -45,7 +45,7 @@ const OperationDropdown: FC<Props> = ({
<PortalToFollowElem
open={open}
onOpenChange={setOpen}
placement='bottom-end'
placement="bottom-end"
offset={{
mainAxis: !inCard ? -12 : 0,
crossAxis: !inCard ? 36 : 0,
@ -58,27 +58,27 @@ const OperationDropdown: FC<Props> = ({
</ActionButton>
</div>
</PortalToFollowElemTrigger>
<PortalToFollowElemContent className='z-50'>
<div className='w-[160px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-1 shadow-lg backdrop-blur-sm'>
<PortalToFollowElemContent className="z-50">
<div className="w-[160px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-1 shadow-lg backdrop-blur-sm">
<div
className='flex cursor-pointer items-center rounded-lg px-3 py-1.5 hover:bg-state-base-hover'
className="flex cursor-pointer items-center rounded-lg px-3 py-1.5 hover:bg-state-base-hover"
onClick={() => {
onEdit()
handleTrigger()
}}
>
<RiEditLine className='h-4 w-4 text-text-tertiary' />
<div className='system-md-regular ml-2 text-text-secondary'>{t('tools.mcp.operation.edit')}</div>
<RiEditLine className="h-4 w-4 text-text-tertiary" />
<div className="system-md-regular ml-2 text-text-secondary">{t('tools.mcp.operation.edit')}</div>
</div>
<div
className='group flex cursor-pointer items-center rounded-lg px-3 py-1.5 hover:bg-state-destructive-hover'
className="group flex cursor-pointer items-center rounded-lg px-3 py-1.5 hover:bg-state-destructive-hover"
onClick={() => {
onRemove()
handleTrigger()
}}
>
<RiDeleteBinLine className='h-4 w-4 text-text-tertiary group-hover:text-text-destructive-secondary' />
<div className='system-md-regular ml-2 text-text-secondary group-hover:text-text-destructive'>{t('tools.mcp.operation.remove')}</div>
<RiDeleteBinLine className="h-4 w-4 text-text-tertiary group-hover:text-text-destructive-secondary" />
<div className="system-md-regular ml-2 text-text-secondary group-hover:text-text-destructive">{t('tools.mcp.operation.remove')}</div>
</div>
</div>
</PortalToFollowElemContent>

View File

@ -1,10 +1,10 @@
'use client'
import React from 'react'
import type { FC } from 'react'
import Drawer from '@/app/components/base/drawer'
import MCPDetailContent from './content'
import type { ToolWithProvider } from '../../../workflow/types'
import React from 'react'
import Drawer from '@/app/components/base/drawer'
import { cn } from '@/utils/classnames'
import MCPDetailContent from './content'
type Props = {
detail?: ToolWithProvider

View File

@ -1,12 +1,12 @@
'use client'
import React from 'react'
import { useContext } from 'use-context-selector'
import type { Tool } from '@/app/components/tools/types'
import React from 'react'
import { useTranslation } from 'react-i18next'
import { useContext } from 'use-context-selector'
import Tooltip from '@/app/components/base/tooltip'
import I18n from '@/context/i18n'
import { getLanguage } from '@/i18n-config/language'
import Tooltip from '@/app/components/base/tooltip'
import { cn } from '@/utils/classnames'
import { useTranslation } from 'react-i18next'
type Props = {
tool: Tool
@ -26,16 +26,23 @@ const MCPToolItem = ({
return null
return (
<div className='mt-2'>
<div className='title-xs-semi-bold mb-1 text-text-primary'>{t('tools.mcp.toolItem.parameters')}:</div>
<ul className='space-y-1'>
<div className="mt-2">
<div className="title-xs-semi-bold mb-1 text-text-primary">
{t('tools.mcp.toolItem.parameters')}
:
</div>
<ul className="space-y-1">
{parameters.map((parameter) => {
const descriptionContent = parameter.human_description[language] || t('tools.mcp.toolItem.noDescription')
return (
<li key={parameter.name} className='pl-2'>
<span className='system-xs-regular font-bold text-text-secondary'>{parameter.name}</span>
<span className='system-xs-regular mr-1 text-text-tertiary'>({parameter.type}):</span>
<span className='system-xs-regular text-text-tertiary'>{descriptionContent}</span>
<li key={parameter.name} className="pl-2">
<span className="system-xs-regular font-bold text-text-secondary">{parameter.name}</span>
<span className="system-xs-regular mr-1 text-text-tertiary">
(
{parameter.type}
):
</span>
<span className="system-xs-regular text-text-tertiary">{descriptionContent}</span>
</li>
)
})}
@ -47,12 +54,12 @@ const MCPToolItem = ({
return (
<Tooltip
key={tool.name}
position='left'
popupClassName='!p-0 !px-4 !py-3.5 !w-[360px] !border-[0.5px] !border-components-panel-border !rounded-xl !shadow-lg'
position="left"
popupClassName="!p-0 !px-4 !py-3.5 !w-[360px] !border-[0.5px] !border-components-panel-border !rounded-xl !shadow-lg"
popupContent={(
<div>
<div className='title-xs-semi-bold mb-1 text-text-primary'>{tool.label[language]}</div>
<div className='body-xs-regular text-text-secondary'>{tool.description[language]}</div>
<div className="title-xs-semi-bold mb-1 text-text-primary">{tool.label[language]}</div>
<div className="body-xs-regular text-text-secondary">{tool.description[language]}</div>
{renderParameters()}
</div>
)}
@ -60,8 +67,8 @@ const MCPToolItem = ({
<div
className={cn('bg-components-panel-item-bg cursor-pointer rounded-xl border-[0.5px] border-components-panel-border-subtle px-4 py-3 shadow-xs hover:bg-components-panel-on-panel-item-bg-hover')}
>
<div className='system-md-semibold pb-0.5 text-text-secondary'>{tool.label[language]}</div>
<div className='system-xs-regular line-clamp-2 text-text-tertiary' title={tool.description[language]}>{tool.description[language]}</div>
<div className="system-md-semibold pb-0.5 text-text-secondary">{tool.label[language]}</div>
<div className="system-xs-regular line-clamp-2 text-text-tertiary" title={tool.description[language]}>{tool.description[language]}</div>
</div>
</Tooltip>
)

View File

@ -1,11 +1,11 @@
'use client'
import { RiAddLine, RiDeleteBinLine } from '@remixicon/react'
import React from 'react'
import { useTranslation } from 'react-i18next'
import { v4 as uuid } from 'uuid'
import { RiAddLine, RiDeleteBinLine } from '@remixicon/react'
import Input from '@/app/components/base/input'
import Button from '@/app/components/base/button'
import ActionButton from '@/app/components/base/action-button'
import Button from '@/app/components/base/button'
import Input from '@/app/components/base/input'
import { cn } from '@/utils/classnames'
export type HeaderItem = {
@ -50,18 +50,18 @@ const HeadersInput = ({
if (headersItems.length === 0) {
return (
<div className='space-y-2'>
<div className='body-xs-regular text-text-tertiary'>
<div className="space-y-2">
<div className="body-xs-regular text-text-tertiary">
{t('tools.mcp.modal.noHeaders')}
</div>
{!readonly && (
<Button
variant='secondary'
size='small'
variant="secondary"
size="small"
onClick={handleAddItem}
className='w-full'
className="w-full"
>
<RiAddLine className='mr-1 h-4 w-4' />
<RiAddLine className="mr-1 h-4 w-4" />
{t('tools.mcp.modal.addHeader')}
</Button>
)}
@ -70,45 +70,48 @@ const HeadersInput = ({
}
return (
<div className='space-y-2'>
<div className="space-y-2">
{isMasked && (
<div className='body-xs-regular text-text-tertiary'>
<div className="body-xs-regular text-text-tertiary">
{t('tools.mcp.modal.maskedHeadersTip')}
</div>
)}
<div className='overflow-hidden rounded-lg border border-divider-regular'>
<div className='system-xs-medium-uppercase bg-background-secondary flex h-7 items-center leading-7 text-text-tertiary'>
<div className='h-full w-1/2 border-r border-divider-regular pl-3'>{t('tools.mcp.modal.headerKey')}</div>
<div className='h-full w-1/2 pl-3 pr-1'>{t('tools.mcp.modal.headerValue')}</div>
<div className="overflow-hidden rounded-lg border border-divider-regular">
<div className="system-xs-medium-uppercase bg-background-secondary flex h-7 items-center leading-7 text-text-tertiary">
<div className="h-full w-1/2 border-r border-divider-regular pl-3">{t('tools.mcp.modal.headerKey')}</div>
<div className="h-full w-1/2 pl-3 pr-1">{t('tools.mcp.modal.headerValue')}</div>
</div>
{headersItems.map((item, index) => (
<div key={item.id} className={cn(
'flex items-center border-divider-regular',
index < headersItems.length - 1 && 'border-b',
)}>
<div className='w-1/2 border-r border-divider-regular'>
<div
key={item.id}
className={cn(
'flex items-center border-divider-regular',
index < headersItems.length - 1 && 'border-b',
)}
>
<div className="w-1/2 border-r border-divider-regular">
<Input
value={item.key}
onChange={e => handleItemChange(index, 'key', e.target.value)}
placeholder={t('tools.mcp.modal.headerKeyPlaceholder')}
className='rounded-none border-0'
className="rounded-none border-0"
readOnly={readonly}
/>
</div>
<div className='flex w-1/2 items-center'>
<div className="flex w-1/2 items-center">
<Input
value={item.value}
onChange={e => handleItemChange(index, 'value', e.target.value)}
placeholder={t('tools.mcp.modal.headerValuePlaceholder')}
className='flex-1 rounded-none border-0'
className="flex-1 rounded-none border-0"
readOnly={readonly}
/>
{!readonly && !!headersItems.length && (
<ActionButton
onClick={() => handleRemoveItem(index)}
className='mr-2'
className="mr-2"
>
<RiDeleteBinLine className='h-4 w-4 text-text-destructive' />
<RiDeleteBinLine className="h-4 w-4 text-text-destructive" />
</ActionButton>
)}
</div>
@ -117,12 +120,12 @@ const HeadersInput = ({
</div>
{!readonly && (
<Button
variant='secondary'
size='small'
variant="secondary"
size="small"
onClick={handleAddItem}
className='w-full'
className="w-full"
>
<RiAddLine className='mr-1 h-4 w-4' />
<RiAddLine className="mr-1 h-4 w-4" />
{t('tools.mcp.modal.addHeader')}
</Button>
)}

View File

@ -1,13 +1,13 @@
'use client'
import type { ToolWithProvider } from '@/app/components/workflow/types'
import { useMemo, useState } from 'react'
import NewMCPCard from './create-card'
import MCPCard from './provider-card'
import MCPDetailPanel from './detail/provider-detail'
import {
useAllToolProviders,
} from '@/service/use-tools'
import type { ToolWithProvider } from '@/app/components/workflow/types'
import { cn } from '@/utils/classnames'
import NewMCPCard from './create-card'
import MCPDetailPanel from './detail/provider-detail'
import MCPCard from './provider-card'
type Props = {
searchText: string
@ -26,7 +26,8 @@ function renderDefaultCard() {
index >= 16 && index < 20 && 'opacity-25',
index >= 20 && index < 24 && 'opacity-20',
)}
></div>
>
</div>
))
return defaultCards
}

View File

@ -1,15 +1,15 @@
'use client'
import React from 'react'
import { useTranslation } from 'react-i18next'
import { RiCloseLine } from '@remixicon/react'
import Modal from '@/app/components/base/modal'
import Button from '@/app/components/base/button'
import Textarea from '@/app/components/base/textarea'
import Divider from '@/app/components/base/divider'
import MCPServerParamItem from '@/app/components/tools/mcp/mcp-server-param-item'
import type {
MCPServerDetail,
} from '@/app/components/tools/types'
import { RiCloseLine } from '@remixicon/react'
import React from 'react'
import { useTranslation } from 'react-i18next'
import Button from '@/app/components/base/button'
import Divider from '@/app/components/base/divider'
import Modal from '@/app/components/base/modal'
import Textarea from '@/app/components/base/textarea'
import MCPServerParamItem from '@/app/components/tools/mcp/mcp-server-param-item'
import {
useCreateMCPServer,
useInvalidateMCPServerDetail,
@ -93,33 +93,34 @@ const MCPServerModal = ({
onClose={onHide}
className={cn('relative !max-w-[520px] !p-0')}
>
<div className='absolute right-5 top-5 z-10 cursor-pointer p-1.5' onClick={onHide}>
<RiCloseLine className='h-5 w-5 text-text-tertiary' />
<div className="absolute right-5 top-5 z-10 cursor-pointer p-1.5" onClick={onHide}>
<RiCloseLine className="h-5 w-5 text-text-tertiary" />
</div>
<div className='title-2xl-semi-bold relative p-6 pb-3 text-xl text-text-primary'>
<div className="title-2xl-semi-bold relative p-6 pb-3 text-xl text-text-primary">
{!data ? t('tools.mcp.server.modal.addTitle') : t('tools.mcp.server.modal.editTitle')}
</div>
<div className='space-y-5 px-6 py-3'>
<div className='space-y-0.5'>
<div className='flex h-6 items-center gap-1'>
<div className='system-sm-medium text-text-secondary'>{t('tools.mcp.server.modal.description')}</div>
<div className='system-xs-regular text-text-destructive-secondary'>*</div>
<div className="space-y-5 px-6 py-3">
<div className="space-y-0.5">
<div className="flex h-6 items-center gap-1">
<div className="system-sm-medium text-text-secondary">{t('tools.mcp.server.modal.description')}</div>
<div className="system-xs-regular text-text-destructive-secondary">*</div>
</div>
<Textarea
className='h-[96px] resize-none'
className="h-[96px] resize-none"
value={description}
placeholder={t('tools.mcp.server.modal.descriptionPlaceholder')}
onChange={e => setDescription(e.target.value)}
></Textarea>
>
</Textarea>
</div>
{latestParams.length > 0 && (
<div>
<div className='mb-1 flex items-center gap-2'>
<div className='system-xs-medium-uppercase shrink-0 text-text-primary'>{t('tools.mcp.server.modal.parameters')}</div>
<Divider type='horizontal' className='!m-0 !h-px grow bg-divider-subtle' />
<div className="mb-1 flex items-center gap-2">
<div className="system-xs-medium-uppercase shrink-0 text-text-primary">{t('tools.mcp.server.modal.parameters')}</div>
<Divider type="horizontal" className="!m-0 !h-px grow bg-divider-subtle" />
</div>
<div className='body-xs-regular mb-2 text-text-tertiary'>{t('tools.mcp.server.modal.parametersTip')}</div>
<div className='space-y-3'>
<div className="body-xs-regular mb-2 text-text-tertiary">{t('tools.mcp.server.modal.parametersTip')}</div>
<div className="space-y-3">
{latestParams.map(paramItem => (
<MCPServerParamItem
key={paramItem.variable}
@ -132,8 +133,8 @@ const MCPServerModal = ({
</div>
)}
</div>
<div className='flex flex-row-reverse p-6 pt-5'>
<Button disabled={!description || creating || updating} className='ml-2' variant='primary' onClick={submit}>{data ? t('tools.mcp.modal.save') : t('tools.mcp.server.modal.confirm')}</Button>
<div className="flex flex-row-reverse p-6 pt-5">
<Button disabled={!description || creating || updating} className="ml-2" variant="primary" onClick={submit}>{data ? t('tools.mcp.modal.save') : t('tools.mcp.server.modal.confirm')}</Button>
<Button onClick={onHide}>{t('tools.mcp.modal.cancel')}</Button>
</div>
</Modal>

View File

@ -17,19 +17,20 @@ const MCPServerParamItem = ({
const { t } = useTranslation()
return (
<div className='space-y-0.5'>
<div className='flex h-6 items-center gap-2'>
<div className='system-xs-medium text-text-secondary'>{data.label}</div>
<div className='system-xs-medium text-text-quaternary'>·</div>
<div className='system-xs-medium text-text-secondary'>{data.variable}</div>
<div className='system-xs-medium text-text-tertiary'>{data.type}</div>
<div className="space-y-0.5">
<div className="flex h-6 items-center gap-2">
<div className="system-xs-medium text-text-secondary">{data.label}</div>
<div className="system-xs-medium text-text-quaternary">·</div>
<div className="system-xs-medium text-text-secondary">{data.variable}</div>
<div className="system-xs-medium text-text-tertiary">{data.type}</div>
</div>
<Textarea
className='h-8 resize-none'
className="h-8 resize-none"
value={value}
placeholder={t('tools.mcp.server.modal.parametersPlaceholder')}
onChange={e => onChange(e.target.value)}
></Textarea>
>
</Textarea>
</div>
)
}

View File

@ -1,32 +1,33 @@
'use client'
import type { AppDetailResponse } from '@/models/app'
import type { AppSSO } from '@/types/app'
import { RiEditLine, RiLoopLeftLine } from '@remixicon/react'
import React, { useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { RiEditLine, RiLoopLeftLine } from '@remixicon/react'
import Button from '@/app/components/base/button'
import Confirm from '@/app/components/base/confirm'
import CopyFeedback from '@/app/components/base/copy-feedback'
import Divider from '@/app/components/base/divider'
import {
Mcp,
} from '@/app/components/base/icons/src/vender/other'
import Button from '@/app/components/base/button'
import Tooltip from '@/app/components/base/tooltip'
import Switch from '@/app/components/base/switch'
import Divider from '@/app/components/base/divider'
import CopyFeedback from '@/app/components/base/copy-feedback'
import Confirm from '@/app/components/base/confirm'
import type { AppDetailResponse } from '@/models/app'
import { useAppContext } from '@/context/app-context'
import { AppModeEnum, type AppSSO } from '@/types/app'
import Tooltip from '@/app/components/base/tooltip'
import Indicator from '@/app/components/header/indicator'
import MCPServerModal from '@/app/components/tools/mcp/mcp-server-modal'
import { useAppWorkflow } from '@/service/use-workflow'
import { BlockEnum } from '@/app/components/workflow/types'
import { useAppContext } from '@/context/app-context'
import { useDocLink } from '@/context/i18n'
import { fetchAppDetail } from '@/service/apps'
import {
useInvalidateMCPServerDetail,
useMCPServerDetail,
useRefreshMCPServerCode,
useUpdateMCPServer,
} from '@/service/use-tools'
import { BlockEnum } from '@/app/components/workflow/types'
import { useAppWorkflow } from '@/service/use-workflow'
import { AppModeEnum } from '@/types/app'
import { cn } from '@/utils/classnames'
import { fetchAppDetail } from '@/service/apps'
import { useDocLink } from '@/context/i18n'
export type IAppCardProps = {
appInfo: AppDetailResponse & Partial<AppSSO>
@ -54,7 +55,7 @@ function MCPServiceCard({
const { data: currentWorkflow } = useAppWorkflow(isAdvancedApp ? appId : '')
const [basicAppConfig, setBasicAppConfig] = useState<any>({})
const basicAppInputForm = useMemo(() => {
if(!isBasicApp || !basicAppConfig?.user_input_form)
if (!isBasicApp || !basicAppConfig?.user_input_form)
return []
return basicAppConfig.user_input_form.map((item: any) => {
const type = Object.keys(item)[0]
@ -65,7 +66,7 @@ function MCPServiceCard({
})
}, [basicAppConfig.user_input_form, isBasicApp])
useEffect(() => {
if(isBasicApp && appId) {
if (isBasicApp && appId) {
(async () => {
const res = await fetchAppDetail({ url: '/apps', id: appId })
setBasicAppConfig(res?.model_config || {})
@ -89,7 +90,7 @@ function MCPServiceCard({
const [activated, setActivated] = useState(serverActivated)
const latestParams = useMemo(() => {
if(isAdvancedApp) {
if (isAdvancedApp) {
if (!currentWorkflow?.graph)
return []
const startNode = currentWorkflow?.graph.nodes.find(node => node.data.type === BlockEnum.Start) as any
@ -150,21 +151,23 @@ function MCPServiceCard({
<div className={cn('w-full max-w-full rounded-xl border-l-[0.5px] border-t border-effects-highlight', isMinimalState && 'h-12')}>
<div className={cn('relative rounded-xl bg-background-default', triggerModeDisabled && 'opacity-60')}>
{triggerModeDisabled && (
triggerModeMessage ? (
<Tooltip
popupContent={triggerModeMessage}
popupClassName="max-w-64 rounded-xl bg-components-panel-bg px-3 py-2 text-xs text-text-secondary shadow-lg"
position="right"
>
<div className='absolute inset-0 z-10 cursor-not-allowed rounded-xl' aria-hidden="true"></div>
</Tooltip>
) : <div className='absolute inset-0 z-10 cursor-not-allowed rounded-xl' aria-hidden="true"></div>
triggerModeMessage
? (
<Tooltip
popupContent={triggerModeMessage}
popupClassName="max-w-64 rounded-xl bg-components-panel-bg px-3 py-2 text-xs text-text-secondary shadow-lg"
position="right"
>
<div className="absolute inset-0 z-10 cursor-not-allowed rounded-xl" aria-hidden="true"></div>
</Tooltip>
)
: <div className="absolute inset-0 z-10 cursor-not-allowed rounded-xl" aria-hidden="true"></div>
)}
<div className={cn('flex w-full flex-col items-start justify-center gap-3 self-stretch p-3', isMinimalState ? 'border-0' : 'border-b-[0.5px] border-divider-subtle')}>
<div className='flex w-full items-center gap-3 self-stretch'>
<div className='flex grow items-center'>
<div className='mr-2 shrink-0 rounded-lg border-[0.5px] border-divider-subtle bg-util-colors-blue-brand-blue-brand-500 p-1 shadow-md'>
<Mcp className='h-4 w-4 text-text-primary-on-surface' />
<div className="flex w-full items-center gap-3 self-stretch">
<div className="flex grow items-center">
<div className="mr-2 shrink-0 rounded-lg border-[0.5px] border-divider-subtle bg-util-colors-blue-brand-blue-brand-500 p-1 shadow-md">
<Mcp className="h-4 w-4 text-text-primary-on-surface" />
</div>
<div className="group w-full">
<div className="system-md-semibold min-w-0 overflow-hidden text-ellipsis break-normal text-text-secondary group-hover:text-text-primary">
@ -172,7 +175,7 @@ function MCPServiceCard({
</div>
</div>
</div>
<div className='flex items-center gap-1'>
<div className="flex items-center gap-1">
<Indicator color={serverActivated ? 'green' : 'yellow'} />
<div className={`${serverActivated ? 'text-text-success' : 'text-text-warning'} system-xs-semibold-uppercase`}>
{serverActivated
@ -182,23 +185,29 @@ function MCPServiceCard({
</div>
<Tooltip
popupContent={
toggleDisabled ? (
appUnpublished ? (
t('tools.mcp.server.publishTip')
) : missingStartNode ? (
<>
<div className="mb-1 text-xs font-normal text-text-secondary">
{t('appOverview.overview.appInfo.enableTooltip.description')}
</div>
<div
className="cursor-pointer text-xs font-normal text-text-accent hover:underline"
onClick={() => window.open(docLink('/guides/workflow/node/user-input'), '_blank')}
>
{t('appOverview.overview.appInfo.enableTooltip.learnMore')}
</div>
</>
) : triggerModeMessage || ''
) : ''
toggleDisabled
? (
appUnpublished
? (
t('tools.mcp.server.publishTip')
)
: missingStartNode
? (
<>
<div className="mb-1 text-xs font-normal text-text-secondary">
{t('appOverview.overview.appInfo.enableTooltip.description')}
</div>
<div
className="cursor-pointer text-xs font-normal text-text-accent hover:underline"
onClick={() => window.open(docLink('/guides/workflow/node/user-input'), '_blank')}
>
{t('appOverview.overview.appInfo.enableTooltip.learnMore')}
</div>
</>
)
: triggerModeMessage || ''
)
: ''
}
position="right"
popupClassName="w-58 max-w-60 rounded-xl bg-components-panel-bg px-3.5 py-3 shadow-lg"
@ -210,7 +219,7 @@ function MCPServiceCard({
</Tooltip>
</div>
{!isMinimalState && (
<div className='flex flex-col items-start justify-center self-stretch'>
<div className="flex flex-col items-start justify-center self-stretch">
<div className="system-xs-medium pb-1 text-text-tertiary">
{t('tools.mcp.server.url')}
</div>
@ -224,7 +233,7 @@ function MCPServiceCard({
<>
<CopyFeedback
content={serverURL}
className={'!size-6'}
className="!size-6"
/>
<Divider type="vertical" className="!mx-0.5 !h-3.5 shrink-0" />
{isCurrentWorkspaceManager && (
@ -235,7 +244,7 @@ function MCPServiceCard({
className="cursor-pointer rounded-md p-1 hover:bg-state-base-hover"
onClick={() => setShowConfirmDelete(true)}
>
<RiLoopLeftLine className={cn('h-4 w-4 text-text-tertiary hover:text-text-secondary', genLoading && 'animate-spin')}/>
<RiLoopLeftLine className={cn('h-4 w-4 text-text-tertiary hover:text-text-secondary', genLoading && 'animate-spin')} />
</div>
</Tooltip>
)}
@ -246,11 +255,11 @@ function MCPServiceCard({
)}
</div>
{!isMinimalState && (
<div className='flex items-center gap-1 self-stretch p-3'>
<div className="flex items-center gap-1 self-stretch p-3">
<Button
disabled={toggleDisabled}
size='small'
variant='ghost'
size="small"
variant="ghost"
onClick={() => setShowMCPServerModal(true)}
>
@ -276,7 +285,7 @@ function MCPServiceCard({
{/* button copy link/ button regenerate */}
{showConfirmDelete && (
<Confirm
type='warning'
type="warning"
title={t('appOverview.overview.appInfo.regenerate')}
content={t('tools.mcp.server.reGen')}
isShow={showConfirmDelete}

View File

@ -1,31 +1,31 @@
'use client'
import type { HeaderItem } from './headers-input'
import type { AppIconSelection } from '@/app/components/base/app-icon-picker'
import type { ToolWithProvider } from '@/app/components/workflow/types'
import type { AppIconType } from '@/types/app'
import { RiCloseLine, RiEditLine } from '@remixicon/react'
import { useHover } from 'ahooks'
import { noop } from 'lodash-es'
import React, { useCallback, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { v4 as uuid } from 'uuid'
import { getDomain } from 'tldts'
import { RiCloseLine, RiEditLine } from '@remixicon/react'
import { Mcp } from '@/app/components/base/icons/src/vender/other'
import AppIconPicker from '@/app/components/base/app-icon-picker'
import type { AppIconSelection } from '@/app/components/base/app-icon-picker'
import { v4 as uuid } from 'uuid'
import AppIcon from '@/app/components/base/app-icon'
import Modal from '@/app/components/base/modal'
import AppIconPicker from '@/app/components/base/app-icon-picker'
import Button from '@/app/components/base/button'
import { Mcp } from '@/app/components/base/icons/src/vender/other'
import AlertTriangle from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback/AlertTriangle'
import Input from '@/app/components/base/input'
import HeadersInput from './headers-input'
import type { HeaderItem } from './headers-input'
import type { AppIconType } from '@/types/app'
import type { ToolWithProvider } from '@/app/components/workflow/types'
import { noop } from 'lodash-es'
import Modal from '@/app/components/base/modal'
import Switch from '@/app/components/base/switch'
import TabSlider from '@/app/components/base/tab-slider'
import Toast from '@/app/components/base/toast'
import { MCPAuthMethod } from '@/app/components/tools/types'
import { API_PREFIX } from '@/config'
import { uploadRemoteFileInfo } from '@/service/common'
import { cn } from '@/utils/classnames'
import { useHover } from 'ahooks'
import { shouldUseMcpIconForAppIcon } from '@/utils/mcp'
import TabSlider from '@/app/components/base/tab-slider'
import { MCPAuthMethod } from '@/app/components/tools/types'
import Switch from '@/app/components/base/switch'
import AlertTriangle from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback/AlertTriangle'
import { API_PREFIX } from '@/config'
import HeadersInput from './headers-input'
export type DuplicateAppModalProps = {
data?: ToolWithProvider
@ -212,7 +212,7 @@ const MCPModal = ({
sse_read_timeout: sseReadTimeout || 300,
},
})
if(isCreate)
if (isCreate)
onHide()
}
@ -227,14 +227,14 @@ const MCPModal = ({
onClose={noop}
className={cn('relative !max-w-[520px]', 'p-6')}
>
<div className='absolute right-5 top-5 z-10 cursor-pointer p-1.5' onClick={onHide}>
<RiCloseLine className='h-5 w-5 text-text-tertiary' />
<div className="absolute right-5 top-5 z-10 cursor-pointer p-1.5" onClick={onHide}>
<RiCloseLine className="h-5 w-5 text-text-tertiary" />
</div>
<div className='title-2xl-semi-bold relative pb-3 text-xl text-text-primary'>{!isCreate ? t('tools.mcp.modal.editTitle') : t('tools.mcp.modal.title')}</div>
<div className='space-y-5 py-3'>
<div className="title-2xl-semi-bold relative pb-3 text-xl text-text-primary">{!isCreate ? t('tools.mcp.modal.editTitle') : t('tools.mcp.modal.title')}</div>
<div className="space-y-5 py-3">
<div>
<div className='mb-1 flex h-6 items-center'>
<span className='system-sm-medium text-text-secondary'>{t('tools.mcp.modal.serverUrl')}</span>
<div className="mb-1 flex h-6 items-center">
<span className="system-sm-medium text-text-secondary">{t('tools.mcp.modal.serverUrl')}</span>
</div>
<Input
value={url}
@ -243,15 +243,15 @@ const MCPModal = ({
placeholder={t('tools.mcp.modal.serverUrlPlaceholder')}
/>
{originalServerUrl && originalServerUrl !== url && (
<div className='mt-1 flex h-5 items-center'>
<span className='body-xs-regular text-text-warning'>{t('tools.mcp.modal.serverUrlWarning')}</span>
<div className="mt-1 flex h-5 items-center">
<span className="body-xs-regular text-text-warning">{t('tools.mcp.modal.serverUrlWarning')}</span>
</div>
)}
</div>
<div className='flex space-x-3'>
<div className='grow pb-1'>
<div className='mb-1 flex h-6 items-center'>
<span className='system-sm-medium text-text-secondary'>{t('tools.mcp.modal.name')}</span>
<div className="flex space-x-3">
<div className="grow pb-1">
<div className="mb-1 flex h-6 items-center">
<span className="system-sm-medium text-text-secondary">{t('tools.mcp.modal.name')}</span>
</div>
<Input
value={name}
@ -259,43 +259,46 @@ const MCPModal = ({
placeholder={t('tools.mcp.modal.namePlaceholder')}
/>
</div>
<div className='pt-2' ref={appIconRef}>
<div className="pt-2" ref={appIconRef}>
<AppIcon
iconType={appIcon.type}
icon={appIcon.type === 'emoji' ? appIcon.icon : appIcon.fileId}
background={appIcon.type === 'emoji' ? appIcon.background : undefined}
imageUrl={appIcon.type === 'image' ? appIcon.url : undefined}
innerIcon={shouldUseMcpIconForAppIcon(appIcon.type, appIcon.type === 'emoji' ? appIcon.icon : '') ? <Mcp className='h-8 w-8 text-text-primary-on-surface' /> : undefined}
size='xxl'
className='relative cursor-pointer rounded-2xl'
innerIcon={shouldUseMcpIconForAppIcon(appIcon.type, appIcon.type === 'emoji' ? appIcon.icon : '') ? <Mcp className="h-8 w-8 text-text-primary-on-surface" /> : undefined}
size="xxl"
className="relative cursor-pointer rounded-2xl"
coverElement={
isHovering
? (<div className='absolute inset-0 flex items-center justify-center overflow-hidden rounded-2xl bg-background-overlay-alt'>
<RiEditLine className='size-6 text-text-primary-on-surface' />
</div>) : null
? (
<div className="absolute inset-0 flex items-center justify-center overflow-hidden rounded-2xl bg-background-overlay-alt">
<RiEditLine className="size-6 text-text-primary-on-surface" />
</div>
)
: null
}
onClick={() => { setShowAppIconPicker(true) }}
/>
</div>
</div>
<div>
<div className='flex h-6 items-center'>
<span className='system-sm-medium text-text-secondary'>{t('tools.mcp.modal.serverIdentifier')}</span>
<div className="flex h-6 items-center">
<span className="system-sm-medium text-text-secondary">{t('tools.mcp.modal.serverIdentifier')}</span>
</div>
<div className='body-xs-regular mb-1 text-text-tertiary'>{t('tools.mcp.modal.serverIdentifierTip')}</div>
<div className="body-xs-regular mb-1 text-text-tertiary">{t('tools.mcp.modal.serverIdentifierTip')}</div>
<Input
value={serverIdentifier}
onChange={e => setServerIdentifier(e.target.value)}
placeholder={t('tools.mcp.modal.serverIdentifierPlaceholder')}
/>
{originalServerID && originalServerID !== serverIdentifier && (
<div className='mt-1 flex h-5 items-center'>
<span className='body-xs-regular text-text-warning'>{t('tools.mcp.modal.serverIdentifierWarning')}</span>
<div className="mt-1 flex h-5 items-center">
<span className="body-xs-regular text-text-warning">{t('tools.mcp.modal.serverIdentifierWarning')}</span>
</div>
)}
</div>
<TabSlider
className='w-full'
className="w-full"
itemClassName={(isActive) => {
return `flex-1 ${isActive && 'text-text-accent-light-mode-only'}`
}}
@ -307,20 +310,20 @@ const MCPModal = ({
authMethod === MCPAuthMethod.authentication && (
<>
<div>
<div className='mb-1 flex h-6 items-center'>
<div className="mb-1 flex h-6 items-center">
<Switch
className='mr-2'
className="mr-2"
defaultValue={isDynamicRegistration}
onChange={setIsDynamicRegistration}
/>
<span className='system-sm-medium text-text-secondary'>{t('tools.mcp.modal.useDynamicClientRegistration')}</span>
<span className="system-sm-medium text-text-secondary">{t('tools.mcp.modal.useDynamicClientRegistration')}</span>
</div>
{!isDynamicRegistration && (
<div className='mt-2 flex gap-2 rounded-lg bg-state-warning-hover p-3'>
<AlertTriangle className='mt-0.5 h-4 w-4 shrink-0 text-text-warning' />
<div className='system-xs-regular text-text-secondary'>
<div className='mb-1'>{t('tools.mcp.modal.redirectUrlWarning')}</div>
<code className='system-xs-medium block break-all rounded bg-state-warning-active px-2 py-1 text-text-secondary'>
<div className="mt-2 flex gap-2 rounded-lg bg-state-warning-hover p-3">
<AlertTriangle className="mt-0.5 h-4 w-4 shrink-0 text-text-warning" />
<div className="system-xs-regular text-text-secondary">
<div className="mb-1">{t('tools.mcp.modal.redirectUrlWarning')}</div>
<code className="system-xs-medium block break-all rounded bg-state-warning-active px-2 py-1 text-text-secondary">
{`${API_PREFIX}/mcp/oauth/callback`}
</code>
</div>
@ -329,7 +332,7 @@ const MCPModal = ({
</div>
<div>
<div className={cn('mb-1 flex h-6 items-center', isDynamicRegistration && 'opacity-50')}>
<span className='system-sm-medium text-text-secondary'>{t('tools.mcp.modal.clientID')}</span>
<span className="system-sm-medium text-text-secondary">{t('tools.mcp.modal.clientID')}</span>
</div>
<Input
value={clientID}
@ -341,7 +344,7 @@ const MCPModal = ({
</div>
<div>
<div className={cn('mb-1 flex h-6 items-center', isDynamicRegistration && 'opacity-50')}>
<span className='system-sm-medium text-text-secondary'>{t('tools.mcp.modal.clientSecret')}</span>
<span className="system-sm-medium text-text-secondary">{t('tools.mcp.modal.clientSecret')}</span>
</div>
<Input
value={credentials}
@ -357,10 +360,10 @@ const MCPModal = ({
{
authMethod === MCPAuthMethod.headers && (
<div>
<div className='mb-1 flex h-6 items-center'>
<span className='system-sm-medium text-text-secondary'>{t('tools.mcp.modal.headers')}</span>
<div className="mb-1 flex h-6 items-center">
<span className="system-sm-medium text-text-secondary">{t('tools.mcp.modal.headers')}</span>
</div>
<div className='body-xs-regular mb-2 text-text-tertiary'>{t('tools.mcp.modal.headersTip')}</div>
<div className="body-xs-regular mb-2 text-text-tertiary">{t('tools.mcp.modal.headersTip')}</div>
<HeadersInput
headersItems={headers}
onChange={setHeaders}
@ -374,11 +377,11 @@ const MCPModal = ({
authMethod === MCPAuthMethod.configurations && (
<>
<div>
<div className='mb-1 flex h-6 items-center'>
<span className='system-sm-medium text-text-secondary'>{t('tools.mcp.modal.timeout')}</span>
<div className="mb-1 flex h-6 items-center">
<span className="system-sm-medium text-text-secondary">{t('tools.mcp.modal.timeout')}</span>
</div>
<Input
type='number'
type="number"
value={timeout}
onChange={e => setMcpTimeout(Number(e.target.value))}
onBlur={e => handleBlur(e.target.value.trim())}
@ -386,11 +389,11 @@ const MCPModal = ({
/>
</div>
<div>
<div className='mb-1 flex h-6 items-center'>
<span className='system-sm-medium text-text-secondary'>{t('tools.mcp.modal.sseReadTimeout')}</span>
<div className="mb-1 flex h-6 items-center">
<span className="system-sm-medium text-text-secondary">{t('tools.mcp.modal.sseReadTimeout')}</span>
</div>
<Input
type='number'
type="number"
value={sseReadTimeout}
onChange={e => setSseReadTimeout(Number(e.target.value))}
onBlur={e => handleBlur(e.target.value.trim())}
@ -401,21 +404,23 @@ const MCPModal = ({
)
}
</div>
<div className='flex flex-row-reverse pt-5'>
<Button disabled={!name || !url || !serverIdentifier || isFetchingIcon} className='ml-2' variant='primary' onClick={submit}>{data ? t('tools.mcp.modal.save') : t('tools.mcp.modal.confirm')}</Button>
<div className="flex flex-row-reverse pt-5">
<Button disabled={!name || !url || !serverIdentifier || isFetchingIcon} className="ml-2" variant="primary" onClick={submit}>{data ? t('tools.mcp.modal.save') : t('tools.mcp.modal.confirm')}</Button>
<Button onClick={onHide}>{t('tools.mcp.modal.cancel')}</Button>
</div>
</Modal>
{showAppIconPicker && <AppIconPicker
onSelect={(payload) => {
setAppIcon(payload)
setShowAppIconPicker(false)
}}
onClose={() => {
setAppIcon(getIcon(data))
setShowAppIconPicker(false)
}}
/>}
{showAppIconPicker && (
<AppIconPicker
onSelect={(payload) => {
setAppIcon(payload)
setShowAppIconPicker(false)
}}
onClose={() => {
setAppIcon(getIcon(data))
setShowAppIconPicker(false)
}}
/>
)}
</>
)

View File

@ -1,18 +1,18 @@
'use client'
import { useCallback, useState } from 'react'
import { useBoolean } from 'ahooks'
import { useTranslation } from 'react-i18next'
import { useAppContext } from '@/context/app-context'
import type { ToolWithProvider } from '../../workflow/types'
import { RiHammerFill } from '@remixicon/react'
import { useBoolean } from 'ahooks'
import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Confirm from '@/app/components/base/confirm'
import Indicator from '@/app/components/header/indicator'
import Icon from '@/app/components/plugins/card/base/card-icon'
import { useAppContext } from '@/context/app-context'
import { useFormatTimeFromNow } from '@/hooks/use-format-time-from-now'
import type { ToolWithProvider } from '../../workflow/types'
import Confirm from '@/app/components/base/confirm'
import MCPModal from './modal'
import OperationDropdown from './detail/operation-dropdown'
import { useDeleteMCP, useUpdateMCP } from '@/service/use-tools'
import { cn } from '@/utils/classnames'
import OperationDropdown from './detail/operation-dropdown'
import MCPModal from './modal'
type Props = {
currentProvider?: ToolWithProvider
@ -82,34 +82,34 @@ const MCPCard = ({
currentProvider?.id === data.id && 'border-components-option-card-option-selected-border bg-components-card-bg-alt',
)}
>
<div className='flex grow items-center gap-3 rounded-t-xl p-4'>
<div className='shrink-0 overflow-hidden rounded-xl border border-components-panel-border-subtle'>
<div className="flex grow items-center gap-3 rounded-t-xl p-4">
<div className="shrink-0 overflow-hidden rounded-xl border border-components-panel-border-subtle">
<Icon src={data.icon} />
</div>
<div className='grow'>
<div className='system-md-semibold mb-1 truncate text-text-secondary' title={data.name}>{data.name}</div>
<div className='system-xs-regular text-text-tertiary'>{data.server_identifier}</div>
<div className="grow">
<div className="system-md-semibold mb-1 truncate text-text-secondary" title={data.name}>{data.name}</div>
<div className="system-xs-regular text-text-tertiary">{data.server_identifier}</div>
</div>
</div>
<div className='flex items-center gap-1 rounded-b-xl pb-2.5 pl-4 pr-2.5 pt-1.5'>
<div className='flex w-0 grow items-center gap-2'>
<div className='flex items-center gap-1'>
<RiHammerFill className='h-3 w-3 shrink-0 text-text-quaternary' />
<div className="flex items-center gap-1 rounded-b-xl pb-2.5 pl-4 pr-2.5 pt-1.5">
<div className="flex w-0 grow items-center gap-2">
<div className="flex items-center gap-1">
<RiHammerFill className="h-3 w-3 shrink-0 text-text-quaternary" />
{data.tools.length > 0 && (
<div className='system-xs-regular shrink-0 text-text-tertiary'>{t('tools.mcp.toolsCount', { count: data.tools.length })}</div>
<div className="system-xs-regular shrink-0 text-text-tertiary">{t('tools.mcp.toolsCount', { count: data.tools.length })}</div>
)}
{!data.tools.length && (
<div className='system-xs-regular shrink-0 text-text-tertiary'>{t('tools.mcp.noTools')}</div>
<div className="system-xs-regular shrink-0 text-text-tertiary">{t('tools.mcp.noTools')}</div>
)}
</div>
<div className={cn('system-xs-regular text-divider-deep', (!data.is_team_authorization || !data.tools.length) && 'sm:hidden')}>/</div>
<div className={cn('system-xs-regular truncate text-text-tertiary', (!data.is_team_authorization || !data.tools.length) && ' sm:hidden')} title={`${t('tools.mcp.updateTime')} ${formatTimeFromNow(data.updated_at! * 1000)}`}>{`${t('tools.mcp.updateTime')} ${formatTimeFromNow(data.updated_at! * 1000)}`}</div>
</div>
{data.is_team_authorization && data.tools.length > 0 && <Indicator color='green' className='shrink-0' />}
{data.is_team_authorization && data.tools.length > 0 && <Indicator color="green" className="shrink-0" />}
{(!data.is_team_authorization || !data.tools.length) && (
<div className='system-xs-medium flex shrink-0 items-center gap-1 rounded-md border border-util-colors-red-red-500 bg-components-badge-bg-red-soft px-1.5 py-0.5 text-util-colors-red-red-500'>
<div className="system-xs-medium flex shrink-0 items-center gap-1 rounded-md border border-util-colors-red-red-500 bg-components-badge-bg-red-soft px-1.5 py-0.5 text-util-colors-red-red-500">
{t('tools.mcp.noConfigured')}
<Indicator color='red' />
<Indicator color="red" />
</div>
)}
</div>
@ -135,11 +135,11 @@ const MCPCard = ({
<Confirm
isShow
title={t('tools.mcp.delete')}
content={
content={(
<div>
{t('tools.mcp.deleteConfirmTitle', { mcp: data.name })}
</div>
}
)}
onCancel={hideDeleteConfirm}
onConfirm={handleDelete}
isLoading={deleting}