Feat: Adjust the style of the mcp dialog #10703 (#10719)

### What problem does this PR solve?

Feat: Adjust the style of the mcp dialog #10703

### Type of change


- [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
balibabu
2025-10-22 12:20:19 +08:00
committed by GitHub
parent 307cdc62ea
commit 02a452993e
14 changed files with 134 additions and 86 deletions

View File

@ -51,8 +51,8 @@ export function Collapse({
onOpenChange={handleOpenChange}
disabled={disabled}
>
<CollapsibleTrigger className="w-full">
<section className="flex justify-between items-center pb-2">
<CollapsibleTrigger className={'w-full'}>
<section className="flex justify-between items-center">
<div className="flex items-center gap-1">
<IconFontFill
name={`more`}
@ -60,12 +60,18 @@ export function Collapse({
'rotate-90': !currentOpen,
})}
></IconFontFill>
{title}
<div
className={cn('text-text-secondary', {
'text-text-primary': open,
})}
>
{title}
</div>
</div>
<div>{rightContent}</div>
</section>
</CollapsibleTrigger>
<CollapsibleContent>{children}</CollapsibleContent>
<CollapsibleContent className="pt-2">{children}</CollapsibleContent>
</Collapsible>
);
}

View File

@ -56,13 +56,13 @@ export function ConfirmDeleteDialog({
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel onClick={onCancel}>
{t('common.cancel')}
{t('common.no')}
</AlertDialogCancel>
<AlertDialogAction
className="bg-state-error text-text-primary"
onClick={onOk}
>
{t('common.ok')}
{t('common.yes')}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>

View File

@ -73,7 +73,7 @@ const DialogFooter = ({
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
'flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2',
'flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-4',
className,
)}
{...props}

View File

@ -7,7 +7,7 @@ import * as React from 'react';
import { cn } from '@/lib/utils';
const labelVariants = cva(
'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70',
'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 text-text-secondary',
);
const Label = React.forwardRef<

View File

@ -94,9 +94,9 @@ export const useShowDeleteConfirm = () => {
title: title ?? t('common.deleteModalTitle'),
icon: <ExclamationCircleFilled />,
content,
okText: t('common.ok'),
okText: t('common.yes'),
okType: 'danger',
cancelText: t('common.cancel'),
cancelText: t('common.no'),
async onOk() {
try {
const ret = await onOk?.();

View File

@ -6,8 +6,9 @@ export default {
selectAll: 'Select All',
delete: 'Delete',
deleteModalTitle: 'Are you sure to delete this item?',
ok: 'Yes',
cancel: 'No',
ok: 'Ok',
cancel: 'Cancel',
yes: 'Yes',
no: 'No',
total: 'Total',
rename: 'Rename',
@ -1744,6 +1745,7 @@ Important structured information may include: names, dates, locations, events, k
toolsAvailable: 'tools available',
mcpServers: 'MCP Servers',
customizeTheListOfMcpServers: 'Customize the list of MCP servers',
cachedTools: 'cached tools',
},
search: {
searchApps: 'Search Apps',

View File

@ -6,8 +6,10 @@ export default {
selectAll: '全选',
delete: '删除',
deleteModalTitle: '确定删除吗?',
ok: '',
cancel: '',
ok: '确认',
cancel: '取消',
yes: '是',
no: '否',
total: '总共',
rename: '重命名',
name: '名称',
@ -1631,6 +1633,7 @@ Tokenizer 会根据所选方式将内容存储为对应的数据结构。`,
toolsAvailable: '可用的工具',
mcpServers: 'MCP 服务器',
customizeTheListOfMcpServers: '自定义 MCP 服务器列表',
cachedTools: '缓存工具',
},
search: {
searchApps: '搜索',

View File

@ -124,8 +124,8 @@ export const ParsingStatusCell = ({ record }: IProps) => {
onConfirm={handleOperationIconClick(true)}
onCancel={handleOperationIconClick(false)}
disabled={record.chunk_num === 0}
okText={t('common.ok')}
cancelText={t('common.cancel')}
okText={t('common.yes')}
cancelText={t('common.no')}
>
<div
className={classNames(styles.operationIcon)}

View File

@ -1,5 +1,24 @@
import { Card, CardContent, CardHeader } from '@/components/ui/card';
import { PropsWithChildren } from 'react';
export function Title({ children }: PropsWithChildren) {
return <span className="font-bold text-xl">{children}</span>;
}
type ProfileSettingWrapperCardProps = {
header: React.ReactNode;
} & PropsWithChildren;
export function ProfileSettingWrapperCard({
header,
children,
}: ProfileSettingWrapperCardProps) {
return (
<Card className="w-full mb-5 border-border-button bg-transparent">
<CardHeader className="border-b border-border-button p-5">
{header}
</CardHeader>
<CardContent className="p-5">{children}</CardContent>
</Card>
);
}

View File

@ -1,7 +1,9 @@
import { Collapse } from '@/components/collapse';
import { Button, ButtonLoading } from '@/components/ui/button';
import { Card, CardContent } from '@/components/ui/card';
import {
Dialog,
DialogClose,
DialogContent,
DialogFooter,
DialogHeader,
@ -121,36 +123,44 @@ export function EditMcpDialog({
form={form}
setFieldChanged={setFieldChanged}
></EditMcpForm>
<Collapse
title={
<div>
{nextTools?.length || 0} {t('mcp.toolsAvailable')}
</div>
}
open={collapseOpen}
onOpenChange={setCollapseOpen}
rightContent={
<Button
variant={'ghost'}
form={FormId}
type="submit"
onClick={handleTest}
<Card>
<CardContent className="p-3">
<Collapse
title={
<div>
{nextTools?.length || 0} {t('mcp.toolsAvailable')}
</div>
}
open={collapseOpen}
onOpenChange={setCollapseOpen}
rightContent={
<Button
variant={'transparent'}
form={FormId}
type="submit"
onClick={handleTest}
className="border-none p-0 hover:bg-transparent"
>
<RefreshCw
className={cn('text-text-secondary', {
'animate-spin': testLoading,
})}
/>
</Button>
}
>
<RefreshCw
className={cn('text-accent-primary', {
'animate-spin': testLoading,
})}
/>
</Button>
}
>
<div className="space-y-2.5 overflow-auto max-h-80">
{nextTools?.map((x) => (
<McpToolCard key={x.name} data={x}></McpToolCard>
))}
</div>
</Collapse>
<div className="overflow-auto max-h-80 divide-y bg-bg-card rounded-md px-2.5">
{nextTools?.map((x) => (
<McpToolCard key={x.name} data={x}></McpToolCard>
))}
</div>
</Collapse>
</CardContent>
</Card>
<DialogFooter>
<DialogClose asChild>
<Button variant="outline">{t('common.cancel')}</Button>
</DialogClose>
<ButtonLoading
type="submit"
form={FormId}

View File

@ -89,7 +89,7 @@ export function EditMcpForm({
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>{t('common.name')}</FormLabel>
<FormLabel required>{t('common.name')}</FormLabel>
<FormControl>
<Input
placeholder={t('common.mcp.namePlaceholder')}
@ -106,7 +106,7 @@ export function EditMcpForm({
name="url"
render={({ field }) => (
<FormItem>
<FormLabel>{t('mcp.url')}</FormLabel>
<FormLabel required>{t('mcp.url')}</FormLabel>
<FormControl>
<Input
placeholder={t('common.mcp.urlPlaceholder')}
@ -127,7 +127,7 @@ export function EditMcpForm({
name="server_type"
render={({ field }) => (
<FormItem>
<FormLabel>{t('mcp.serverType')}</FormLabel>
<FormLabel required>{t('mcp.serverType')}</FormLabel>
<FormControl>
<RAGFlowSelect
{...field}

View File

@ -1,13 +1,15 @@
import { BulkOperateBar } from '@/components/bulk-operate-bar';
import { CardContainer } from '@/components/card-container';
import Spotlight from '@/components/spotlight';
import { Button } from '@/components/ui/button';
import { SearchInput } from '@/components/ui/input';
import { RAGFlowPagination } from '@/components/ui/ragflow-pagination';
import { useListMcpServer } from '@/hooks/use-mcp-request';
import { pick } from 'lodash';
import { Import, Plus } from 'lucide-react';
import { Plus, SquareArrowOutDownLeft } from 'lucide-react';
import { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { ProfileSettingWrapperCard } from '../components';
import { EditMcpDialog } from './edit-mcp-dialog';
import { ImportMcpDialog } from './import-mcp-dialog';
import { McpCard } from './mcp-card';
@ -33,27 +35,33 @@ export default function McpServer() {
);
return (
<section className="p-4 w-full">
<div className="text-text-primary text-2xl">{t('mcp.mcpServers')}</div>
<section className="flex items-center justify-between pb-5">
<div className="text-text-secondary">
{t('mcp.customizeTheListOfMcpServers')}
</div>
<div className="flex gap-5">
<SearchInput
className="w-40"
value={searchString}
onChange={handleInputChange}
></SearchInput>
<Button variant={'secondary'} onClick={showImportModal}>
<Import /> {t('mcp.import')}
</Button>
<Button onClick={showEditModal('')}>
<Plus /> {t('mcp.addMCP')}
</Button>
</div>
</section>
<ProfileSettingWrapperCard
header={
<>
<div className="text-text-primary text-2xl font-semibold">
{t('mcp.mcpServers')}
</div>
<section className="flex items-center justify-between">
<div className="text-text-secondary">
{t('mcp.customizeTheListOfMcpServers')}
</div>
<div className="flex gap-5">
<SearchInput
className="w-40"
value={searchString}
onChange={handleInputChange}
></SearchInput>
<Button onClick={showEditModal('')}>
<Plus /> {t('mcp.addMCP')}
</Button>
<Button variant={'secondary'} onClick={showImportModal}>
<SquareArrowOutDownLeft /> {t('mcp.import')}
</Button>
</div>
</section>
</>
}
>
{selectedList.length > 0 && (
<BulkOperateBar
list={list}
@ -93,6 +101,7 @@ export default function McpServer() {
onOk={onImportOk}
></ImportMcpDialog>
)}
</section>
<Spotlight className="z-0" opcity={0.7} coverage={70} />
</ProfileSettingWrapperCard>
);
}

View File

@ -5,6 +5,7 @@ import { IMcpServer } from '@/interfaces/database/mcp';
import { formatDate } from '@/utils/date';
import { isPlainObject } from 'lodash';
import { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { McpDropdown } from './mcp-dropdown';
import { UseBulkOperateMCPReturnType } from './use-bulk-operate-mcp';
import { UseEditMcpReturnType } from './use-edit-mcp';
@ -20,6 +21,7 @@ export function McpCard({
handleSelectChange,
showEditModal,
}: DatasetCardProps) {
const { t } = useTranslation();
const toolLength = useMemo(() => {
const tools = data.variables?.tools;
if (isPlainObject(tools)) {
@ -36,7 +38,9 @@ export function McpCard({
<Card key={data.id}>
<CardContent className="p-2.5 pt-2 group">
<section className="flex justify-between pb-2">
<h3 className="text-lg font-semibold truncate flex-1">{data.name}</h3>
<h3 className="text-base font-normal truncate flex-1 text-text-primary">
{data.name}
</h3>
<div className="space-x-4">
<McpDropdown mcpId={data.id} showEditModal={showEditModal}>
<MoreButton></MoreButton>
@ -50,14 +54,12 @@ export function McpCard({
/>
</div>
</section>
<div className="flex justify-between items-end">
<div className="flex justify-between items-end text-xs text-text-secondary">
<div className="w-full">
<div className="text-base font-semibold mb-3 line-clamp-1 text-text-secondary">
{toolLength} cached tools
<div className="line-clamp-1 pb-1">
{toolLength} {t('mcp.cachedTools')}
</div>
<p className="text-sm text-text-secondary">
{formatDate(data.update_date)}
</p>
<p>{formatDate(data.update_date)}</p>
</div>
</div>
</CardContent>

View File

@ -1,4 +1,3 @@
import { Card, CardContent } from '@/components/ui/card';
import { IMCPTool } from '@/interfaces/database/mcp';
export type McpToolCardProps = {
@ -7,13 +6,11 @@ export type McpToolCardProps = {
export function McpToolCard({ data }: McpToolCardProps) {
return (
<Card>
<CardContent className="p-2.5 pt-2 group">
<h3 className="text-sm font-semibold line-clamp-1 pb-2">{data.name}</h3>
<div className="text-xs font-normal mb-3 text-text-secondary">
{data.description}
</div>
</CardContent>
</Card>
<section className="group py-2.5">
<h3 className="text-sm font-semibold line-clamp-1 pb-2">{data.name}</h3>
<div className="text-xs font-normal text-text-secondary">
{data.description}
</div>
</section>
);
}